sync
[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      * Sets the url of the image - used to update it
1519      * @param {String} url the url of the image
1520      */
1521     
1522     setSrc : function(url)
1523     {
1524         this.src =  url;
1525         this.el.select('img', true).first().dom.src =  url;
1526     }
1527     
1528     
1529    
1530 });
1531
1532  /*
1533  * - LGPL
1534  *
1535  * image
1536  * 
1537  */
1538
1539
1540 /**
1541  * @class Roo.bootstrap.Link
1542  * @extends Roo.bootstrap.Component
1543  * Bootstrap Link Class
1544  * @cfg {String} alt image alternative text
1545  * @cfg {String} href a tag href
1546  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1547  * @cfg {String} html the content of the link.
1548  * @cfg {String} anchor name for the anchor link
1549  * @cfg {String} fa - favicon
1550
1551  * @cfg {Boolean} preventDefault (true | false) default false
1552
1553  * 
1554  * @constructor
1555  * Create a new Input
1556  * @param {Object} config The config object
1557  */
1558
1559 Roo.bootstrap.Link = function(config){
1560     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1561     
1562     this.addEvents({
1563         // img events
1564         /**
1565          * @event click
1566          * The img click event for the img.
1567          * @param {Roo.EventObject} e
1568          */
1569         "click" : true
1570     });
1571 };
1572
1573 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1574     
1575     href: false,
1576     target: false,
1577     preventDefault: false,
1578     anchor : false,
1579     alt : false,
1580     fa: false,
1581
1582
1583     getAutoCreate : function()
1584     {
1585         var html = this.html || '';
1586         
1587         if (this.fa !== false) {
1588             html = '<i class="fa fa-' + this.fa + '"></i>';
1589         }
1590         var cfg = {
1591             tag: 'a'
1592         };
1593         // anchor's do not require html/href...
1594         if (this.anchor === false) {
1595             cfg.html = html;
1596             cfg.href = this.href || '#';
1597         } else {
1598             cfg.name = this.anchor;
1599             if (this.html !== false || this.fa !== false) {
1600                 cfg.html = html;
1601             }
1602             if (this.href !== false) {
1603                 cfg.href = this.href;
1604             }
1605         }
1606         
1607         if(this.alt !== false){
1608             cfg.alt = this.alt;
1609         }
1610         
1611         
1612         if(this.target !== false) {
1613             cfg.target = this.target;
1614         }
1615         
1616         return cfg;
1617     },
1618     
1619     initEvents: function() {
1620         
1621         if(!this.href || this.preventDefault){
1622             this.el.on('click', this.onClick, this);
1623         }
1624     },
1625     
1626     onClick : function(e)
1627     {
1628         if(this.preventDefault){
1629             e.preventDefault();
1630         }
1631         //Roo.log('img onclick');
1632         this.fireEvent('click', this, e);
1633     }
1634    
1635 });
1636
1637  /*
1638  * - LGPL
1639  *
1640  * header
1641  * 
1642  */
1643
1644 /**
1645  * @class Roo.bootstrap.Header
1646  * @extends Roo.bootstrap.Component
1647  * Bootstrap Header class
1648  * @cfg {String} html content of header
1649  * @cfg {Number} level (1|2|3|4|5|6) default 1
1650  * 
1651  * @constructor
1652  * Create a new Header
1653  * @param {Object} config The config object
1654  */
1655
1656
1657 Roo.bootstrap.Header  = function(config){
1658     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1659 };
1660
1661 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1662     
1663     //href : false,
1664     html : false,
1665     level : 1,
1666     
1667     
1668     
1669     getAutoCreate : function(){
1670         
1671         
1672         
1673         var cfg = {
1674             tag: 'h' + (1 *this.level),
1675             html: this.html || ''
1676         } ;
1677         
1678         return cfg;
1679     }
1680    
1681 });
1682
1683  
1684
1685  /*
1686  * Based on:
1687  * Ext JS Library 1.1.1
1688  * Copyright(c) 2006-2007, Ext JS, LLC.
1689  *
1690  * Originally Released Under LGPL - original licence link has changed is not relivant.
1691  *
1692  * Fork - LGPL
1693  * <script type="text/javascript">
1694  */
1695  
1696 /**
1697  * @class Roo.bootstrap.MenuMgr
1698  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1699  * @singleton
1700  */
1701 Roo.bootstrap.MenuMgr = function(){
1702    var menus, active, groups = {}, attached = false, lastShow = new Date();
1703
1704    // private - called when first menu is created
1705    function init(){
1706        menus = {};
1707        active = new Roo.util.MixedCollection();
1708        Roo.get(document).addKeyListener(27, function(){
1709            if(active.length > 0){
1710                hideAll();
1711            }
1712        });
1713    }
1714
1715    // private
1716    function hideAll(){
1717        if(active && active.length > 0){
1718            var c = active.clone();
1719            c.each(function(m){
1720                m.hide();
1721            });
1722        }
1723    }
1724
1725    // private
1726    function onHide(m){
1727        active.remove(m);
1728        if(active.length < 1){
1729            Roo.get(document).un("mouseup", onMouseDown);
1730             
1731            attached = false;
1732        }
1733    }
1734
1735    // private
1736    function onShow(m){
1737        var last = active.last();
1738        lastShow = new Date();
1739        active.add(m);
1740        if(!attached){
1741           Roo.get(document).on("mouseup", onMouseDown);
1742            
1743            attached = true;
1744        }
1745        if(m.parentMenu){
1746           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1747           m.parentMenu.activeChild = m;
1748        }else if(last && last.isVisible()){
1749           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1750        }
1751    }
1752
1753    // private
1754    function onBeforeHide(m){
1755        if(m.activeChild){
1756            m.activeChild.hide();
1757        }
1758        if(m.autoHideTimer){
1759            clearTimeout(m.autoHideTimer);
1760            delete m.autoHideTimer;
1761        }
1762    }
1763
1764    // private
1765    function onBeforeShow(m){
1766        var pm = m.parentMenu;
1767        if(!pm && !m.allowOtherMenus){
1768            hideAll();
1769        }else if(pm && pm.activeChild && active != m){
1770            pm.activeChild.hide();
1771        }
1772    }
1773
1774    // private this should really trigger on mouseup..
1775    function onMouseDown(e){
1776         Roo.log("on Mouse Up");
1777         
1778         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1779             Roo.log("MenuManager hideAll");
1780             hideAll();
1781             e.stopEvent();
1782         }
1783         
1784         
1785    }
1786
1787    // private
1788    function onBeforeCheck(mi, state){
1789        if(state){
1790            var g = groups[mi.group];
1791            for(var i = 0, l = g.length; i < l; i++){
1792                if(g[i] != mi){
1793                    g[i].setChecked(false);
1794                }
1795            }
1796        }
1797    }
1798
1799    return {
1800
1801        /**
1802         * Hides all menus that are currently visible
1803         */
1804        hideAll : function(){
1805             hideAll();  
1806        },
1807
1808        // private
1809        register : function(menu){
1810            if(!menus){
1811                init();
1812            }
1813            menus[menu.id] = menu;
1814            menu.on("beforehide", onBeforeHide);
1815            menu.on("hide", onHide);
1816            menu.on("beforeshow", onBeforeShow);
1817            menu.on("show", onShow);
1818            var g = menu.group;
1819            if(g && menu.events["checkchange"]){
1820                if(!groups[g]){
1821                    groups[g] = [];
1822                }
1823                groups[g].push(menu);
1824                menu.on("checkchange", onCheck);
1825            }
1826        },
1827
1828         /**
1829          * Returns a {@link Roo.menu.Menu} object
1830          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1831          * be used to generate and return a new Menu instance.
1832          */
1833        get : function(menu){
1834            if(typeof menu == "string"){ // menu id
1835                return menus[menu];
1836            }else if(menu.events){  // menu instance
1837                return menu;
1838            }
1839            /*else if(typeof menu.length == 'number'){ // array of menu items?
1840                return new Roo.bootstrap.Menu({items:menu});
1841            }else{ // otherwise, must be a config
1842                return new Roo.bootstrap.Menu(menu);
1843            }
1844            */
1845            return false;
1846        },
1847
1848        // private
1849        unregister : function(menu){
1850            delete menus[menu.id];
1851            menu.un("beforehide", onBeforeHide);
1852            menu.un("hide", onHide);
1853            menu.un("beforeshow", onBeforeShow);
1854            menu.un("show", onShow);
1855            var g = menu.group;
1856            if(g && menu.events["checkchange"]){
1857                groups[g].remove(menu);
1858                menu.un("checkchange", onCheck);
1859            }
1860        },
1861
1862        // private
1863        registerCheckable : function(menuItem){
1864            var g = menuItem.group;
1865            if(g){
1866                if(!groups[g]){
1867                    groups[g] = [];
1868                }
1869                groups[g].push(menuItem);
1870                menuItem.on("beforecheckchange", onBeforeCheck);
1871            }
1872        },
1873
1874        // private
1875        unregisterCheckable : function(menuItem){
1876            var g = menuItem.group;
1877            if(g){
1878                groups[g].remove(menuItem);
1879                menuItem.un("beforecheckchange", onBeforeCheck);
1880            }
1881        }
1882    };
1883 }();/*
1884  * - LGPL
1885  *
1886  * menu
1887  * 
1888  */
1889
1890 /**
1891  * @class Roo.bootstrap.Menu
1892  * @extends Roo.bootstrap.Component
1893  * Bootstrap Menu class - container for MenuItems
1894  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1895  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1896  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1897  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1898  * 
1899  * @constructor
1900  * Create a new Menu
1901  * @param {Object} config The config object
1902  */
1903
1904
1905 Roo.bootstrap.Menu = function(config){
1906     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1907     if (this.registerMenu && this.type != 'treeview')  {
1908         Roo.bootstrap.MenuMgr.register(this);
1909     }
1910     this.addEvents({
1911         /**
1912          * @event beforeshow
1913          * Fires before this menu is displayed
1914          * @param {Roo.menu.Menu} this
1915          */
1916         beforeshow : true,
1917         /**
1918          * @event beforehide
1919          * Fires before this menu is hidden
1920          * @param {Roo.menu.Menu} this
1921          */
1922         beforehide : true,
1923         /**
1924          * @event show
1925          * Fires after this menu is displayed
1926          * @param {Roo.menu.Menu} this
1927          */
1928         show : true,
1929         /**
1930          * @event hide
1931          * Fires after this menu is hidden
1932          * @param {Roo.menu.Menu} this
1933          */
1934         hide : true,
1935         /**
1936          * @event click
1937          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1938          * @param {Roo.menu.Menu} this
1939          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1940          * @param {Roo.EventObject} e
1941          */
1942         click : true,
1943         /**
1944          * @event mouseover
1945          * Fires when the mouse is hovering over this menu
1946          * @param {Roo.menu.Menu} this
1947          * @param {Roo.EventObject} e
1948          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1949          */
1950         mouseover : true,
1951         /**
1952          * @event mouseout
1953          * Fires when the mouse exits this menu
1954          * @param {Roo.menu.Menu} this
1955          * @param {Roo.EventObject} e
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          */
1958         mouseout : true,
1959         /**
1960          * @event itemclick
1961          * Fires when a menu item contained in this menu is clicked
1962          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1963          * @param {Roo.EventObject} e
1964          */
1965         itemclick: true
1966     });
1967     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1968 };
1969
1970 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1971     
1972    /// html : false,
1973     //align : '',
1974     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1975     type: false,
1976     /**
1977      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1978      */
1979     registerMenu : true,
1980     
1981     menuItems :false, // stores the menu items..
1982     
1983     hidden:true,
1984         
1985     parentMenu : false,
1986     
1987     stopEvent : true,
1988     
1989     isLink : false,
1990     
1991     getChildContainer : function() {
1992         return this.el;  
1993     },
1994     
1995     getAutoCreate : function(){
1996          
1997         //if (['right'].indexOf(this.align)!==-1) {
1998         //    cfg.cn[1].cls += ' pull-right'
1999         //}
2000         
2001         
2002         var cfg = {
2003             tag : 'ul',
2004             cls : 'dropdown-menu' ,
2005             style : 'z-index:1000'
2006             
2007         };
2008         
2009         if (this.type === 'submenu') {
2010             cfg.cls = 'submenu active';
2011         }
2012         if (this.type === 'treeview') {
2013             cfg.cls = 'treeview-menu';
2014         }
2015         
2016         return cfg;
2017     },
2018     initEvents : function() {
2019         
2020        // Roo.log("ADD event");
2021        // Roo.log(this.triggerEl.dom);
2022         
2023         this.triggerEl.on('click', this.onTriggerClick, this);
2024         
2025         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2026         
2027         this.triggerEl.addClass('dropdown-toggle');
2028         
2029         if (Roo.isTouch) {
2030             this.el.on('touchstart'  , this.onTouch, this);
2031         }
2032         this.el.on('click' , this.onClick, this);
2033
2034         this.el.on("mouseover", this.onMouseOver, this);
2035         this.el.on("mouseout", this.onMouseOut, this);
2036         
2037     },
2038     
2039     findTargetItem : function(e)
2040     {
2041         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2042         if(!t){
2043             return false;
2044         }
2045         //Roo.log(t);         Roo.log(t.id);
2046         if(t && t.id){
2047             //Roo.log(this.menuitems);
2048             return this.menuitems.get(t.id);
2049             
2050             //return this.items.get(t.menuItemId);
2051         }
2052         
2053         return false;
2054     },
2055     
2056     onTouch : function(e) 
2057     {
2058         Roo.log("menu.onTouch");
2059         //e.stopEvent(); this make the user popdown broken
2060         this.onClick(e);
2061     },
2062     
2063     onClick : function(e)
2064     {
2065         Roo.log("menu.onClick");
2066         
2067         var t = this.findTargetItem(e);
2068         if(!t || t.isContainer){
2069             return;
2070         }
2071         Roo.log(e);
2072         /*
2073         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2074             if(t == this.activeItem && t.shouldDeactivate(e)){
2075                 this.activeItem.deactivate();
2076                 delete this.activeItem;
2077                 return;
2078             }
2079             if(t.canActivate){
2080                 this.setActiveItem(t, true);
2081             }
2082             return;
2083             
2084             
2085         }
2086         */
2087        
2088         Roo.log('pass click event');
2089         
2090         t.onClick(e);
2091         
2092         this.fireEvent("click", this, t, e);
2093         
2094         var _this = this;
2095         
2096         (function() { _this.hide(); }).defer(500);
2097     },
2098     
2099     onMouseOver : function(e){
2100         var t  = this.findTargetItem(e);
2101         //Roo.log(t);
2102         //if(t){
2103         //    if(t.canActivate && !t.disabled){
2104         //        this.setActiveItem(t, true);
2105         //    }
2106         //}
2107         
2108         this.fireEvent("mouseover", this, e, t);
2109     },
2110     isVisible : function(){
2111         return !this.hidden;
2112     },
2113      onMouseOut : function(e){
2114         var t  = this.findTargetItem(e);
2115         
2116         //if(t ){
2117         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2118         //        this.activeItem.deactivate();
2119         //        delete this.activeItem;
2120         //    }
2121         //}
2122         this.fireEvent("mouseout", this, e, t);
2123     },
2124     
2125     
2126     /**
2127      * Displays this menu relative to another element
2128      * @param {String/HTMLElement/Roo.Element} element The element to align to
2129      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2130      * the element (defaults to this.defaultAlign)
2131      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2132      */
2133     show : function(el, pos, parentMenu){
2134         this.parentMenu = parentMenu;
2135         if(!this.el){
2136             this.render();
2137         }
2138         this.fireEvent("beforeshow", this);
2139         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2140     },
2141      /**
2142      * Displays this menu at a specific xy position
2143      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2144      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2145      */
2146     showAt : function(xy, parentMenu, /* private: */_e){
2147         this.parentMenu = parentMenu;
2148         if(!this.el){
2149             this.render();
2150         }
2151         if(_e !== false){
2152             this.fireEvent("beforeshow", this);
2153             //xy = this.el.adjustForConstraints(xy);
2154         }
2155         
2156         //this.el.show();
2157         this.hideMenuItems();
2158         this.hidden = false;
2159         this.triggerEl.addClass('open');
2160         
2161         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2162             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2163         }
2164         
2165         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2166             this.el.setXY(xy);
2167         }
2168         
2169         this.focus();
2170         this.fireEvent("show", this);
2171     },
2172     
2173     focus : function(){
2174         return;
2175         if(!this.hidden){
2176             this.doFocus.defer(50, this);
2177         }
2178     },
2179
2180     doFocus : function(){
2181         if(!this.hidden){
2182             this.focusEl.focus();
2183         }
2184     },
2185
2186     /**
2187      * Hides this menu and optionally all parent menus
2188      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2189      */
2190     hide : function(deep)
2191     {
2192         
2193         this.hideMenuItems();
2194         if(this.el && this.isVisible()){
2195             this.fireEvent("beforehide", this);
2196             if(this.activeItem){
2197                 this.activeItem.deactivate();
2198                 this.activeItem = null;
2199             }
2200             this.triggerEl.removeClass('open');;
2201             this.hidden = true;
2202             this.fireEvent("hide", this);
2203         }
2204         if(deep === true && this.parentMenu){
2205             this.parentMenu.hide(true);
2206         }
2207     },
2208     
2209     onTriggerClick : function(e)
2210     {
2211         Roo.log('trigger click');
2212         
2213         var target = e.getTarget();
2214         
2215         Roo.log(target.nodeName.toLowerCase());
2216         
2217         if(target.nodeName.toLowerCase() === 'i'){
2218             e.preventDefault();
2219         }
2220         
2221     },
2222     
2223     onTriggerPress  : function(e)
2224     {
2225         Roo.log('trigger press');
2226         //Roo.log(e.getTarget());
2227        // Roo.log(this.triggerEl.dom);
2228        
2229         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2230         var pel = Roo.get(e.getTarget());
2231         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2232             Roo.log('is treeview or dropdown?');
2233             return;
2234         }
2235         
2236         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2237             return;
2238         }
2239         
2240         if (this.isVisible()) {
2241             Roo.log('hide');
2242             this.hide();
2243         } else {
2244             Roo.log('show');
2245             this.show(this.triggerEl, false, false);
2246         }
2247         
2248         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2249             e.stopEvent();
2250         }
2251         
2252     },
2253        
2254     
2255     hideMenuItems : function()
2256     {
2257         Roo.log("hide Menu Items");
2258         if (!this.el) { 
2259             return;
2260         }
2261         //$(backdrop).remove()
2262         this.el.select('.open',true).each(function(aa) {
2263             
2264             aa.removeClass('open');
2265           //var parent = getParent($(this))
2266           //var relatedTarget = { relatedTarget: this }
2267           
2268            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2269           //if (e.isDefaultPrevented()) return
2270            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2271         });
2272     },
2273     addxtypeChild : function (tree, cntr) {
2274         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2275           
2276         this.menuitems.add(comp);
2277         return comp;
2278
2279     },
2280     getEl : function()
2281     {
2282         Roo.log(this.el);
2283         return this.el;
2284     }
2285 });
2286
2287  
2288  /*
2289  * - LGPL
2290  *
2291  * menu item
2292  * 
2293  */
2294
2295
2296 /**
2297  * @class Roo.bootstrap.MenuItem
2298  * @extends Roo.bootstrap.Component
2299  * Bootstrap MenuItem class
2300  * @cfg {String} html the menu label
2301  * @cfg {String} href the link
2302  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2303  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2304  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2305  * @cfg {String} fa favicon to show on left of menu item.
2306  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2307  * 
2308  * 
2309  * @constructor
2310  * Create a new MenuItem
2311  * @param {Object} config The config object
2312  */
2313
2314
2315 Roo.bootstrap.MenuItem = function(config){
2316     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2317     this.addEvents({
2318         // raw events
2319         /**
2320          * @event click
2321          * The raw click event for the entire grid.
2322          * @param {Roo.bootstrap.MenuItem} this
2323          * @param {Roo.EventObject} e
2324          */
2325         "click" : true
2326     });
2327 };
2328
2329 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2330     
2331     href : false,
2332     html : false,
2333     preventDefault: true,
2334     isContainer : false,
2335     active : false,
2336     fa: false,
2337     
2338     getAutoCreate : function(){
2339         
2340         if(this.isContainer){
2341             return {
2342                 tag: 'li',
2343                 cls: 'dropdown-menu-item'
2344             };
2345         }
2346         var ctag = {
2347             tag: 'span',
2348             html: 'Link'
2349         };
2350         
2351         var anc = {
2352             tag : 'a',
2353             href : '#',
2354             cn : [  ]
2355         };
2356         
2357         if (this.fa !== false) {
2358             anc.cn.push({
2359                 tag : 'i',
2360                 cls : 'fa fa-' + this.fa
2361             });
2362         }
2363         
2364         anc.cn.push(ctag);
2365         
2366         
2367         var cfg= {
2368             tag: 'li',
2369             cls: 'dropdown-menu-item',
2370             cn: [ anc ]
2371         };
2372         if (this.parent().type == 'treeview') {
2373             cfg.cls = 'treeview-menu';
2374         }
2375         if (this.active) {
2376             cfg.cls += ' active';
2377         }
2378         
2379         
2380         
2381         anc.href = this.href || cfg.cn[0].href ;
2382         ctag.html = this.html || cfg.cn[0].html ;
2383         return cfg;
2384     },
2385     
2386     initEvents: function()
2387     {
2388         if (this.parent().type == 'treeview') {
2389             this.el.select('a').on('click', this.onClick, this);
2390         }
2391         if (this.menu) {
2392             this.menu.parentType = this.xtype;
2393             this.menu.triggerEl = this.el;
2394             this.menu = this.addxtype(Roo.apply({}, this.menu));
2395         }
2396         
2397     },
2398     onClick : function(e)
2399     {
2400         Roo.log('item on click ');
2401         //if(this.preventDefault){
2402         //    e.preventDefault();
2403         //}
2404         //this.parent().hideMenuItems();
2405         
2406         this.fireEvent('click', this, e);
2407     },
2408     getEl : function()
2409     {
2410         return this.el;
2411     } 
2412 });
2413
2414  
2415
2416  /*
2417  * - LGPL
2418  *
2419  * menu separator
2420  * 
2421  */
2422
2423
2424 /**
2425  * @class Roo.bootstrap.MenuSeparator
2426  * @extends Roo.bootstrap.Component
2427  * Bootstrap MenuSeparator class
2428  * 
2429  * @constructor
2430  * Create a new MenuItem
2431  * @param {Object} config The config object
2432  */
2433
2434
2435 Roo.bootstrap.MenuSeparator = function(config){
2436     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2437 };
2438
2439 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2440     
2441     getAutoCreate : function(){
2442         var cfg = {
2443             cls: 'divider',
2444             tag : 'li'
2445         };
2446         
2447         return cfg;
2448     }
2449    
2450 });
2451
2452  
2453
2454  
2455 /*
2456 * Licence: LGPL
2457 */
2458
2459 /**
2460  * @class Roo.bootstrap.Modal
2461  * @extends Roo.bootstrap.Component
2462  * Bootstrap Modal class
2463  * @cfg {String} title Title of dialog
2464  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2465  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2466  * @cfg {Boolean} specificTitle default false
2467  * @cfg {Array} buttons Array of buttons or standard button set..
2468  * @cfg {String} buttonPosition (left|right|center) default right
2469  * @cfg {Boolean} animate default true
2470  * @cfg {Boolean} allow_close default true
2471  * @cfg {Boolean} fitwindow default false
2472  * @cfg {String} size (sm|lg) default empty
2473  * 
2474  * 
2475  * @constructor
2476  * Create a new Modal Dialog
2477  * @param {Object} config The config object
2478  */
2479
2480 Roo.bootstrap.Modal = function(config){
2481     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2482     this.addEvents({
2483         // raw events
2484         /**
2485          * @event btnclick
2486          * The raw btnclick event for the button
2487          * @param {Roo.EventObject} e
2488          */
2489         "btnclick" : true
2490     });
2491     this.buttons = this.buttons || [];
2492      
2493     if (this.tmpl) {
2494         this.tmpl = Roo.factory(this.tmpl);
2495     }
2496     
2497 };
2498
2499 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2500     
2501     title : 'test dialog',
2502    
2503     buttons : false,
2504     
2505     // set on load...
2506      
2507     html: false,
2508     
2509     tmp: false,
2510     
2511     specificTitle: false,
2512     
2513     buttonPosition: 'right',
2514     
2515     allow_close : true,
2516     
2517     animate : true,
2518     
2519     fitwindow: false,
2520     
2521     
2522      // private
2523     dialogEl: false,
2524     bodyEl:  false,
2525     footerEl:  false,
2526     titleEl:  false,
2527     closeEl:  false,
2528     
2529     size: '',
2530     
2531     
2532     onRender : function(ct, position)
2533     {
2534         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2535      
2536         if(!this.el){
2537             var cfg = Roo.apply({},  this.getAutoCreate());
2538             cfg.id = Roo.id();
2539             //if(!cfg.name){
2540             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2541             //}
2542             //if (!cfg.name.length) {
2543             //    delete cfg.name;
2544            // }
2545             if (this.cls) {
2546                 cfg.cls += ' ' + this.cls;
2547             }
2548             if (this.style) {
2549                 cfg.style = this.style;
2550             }
2551             this.el = Roo.get(document.body).createChild(cfg, position);
2552         }
2553         //var type = this.el.dom.type;
2554         
2555         
2556         if(this.tabIndex !== undefined){
2557             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2558         }
2559         
2560         this.dialogEl = this.el.select('.modal-dialog',true).first();
2561         this.bodyEl = this.el.select('.modal-body',true).first();
2562         this.closeEl = this.el.select('.modal-header .close', true).first();
2563         this.footerEl = this.el.select('.modal-footer',true).first();
2564         this.titleEl = this.el.select('.modal-title',true).first();
2565         
2566         
2567          
2568         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2569         this.maskEl.enableDisplayMode("block");
2570         this.maskEl.hide();
2571         //this.el.addClass("x-dlg-modal");
2572     
2573         if (this.buttons.length) {
2574             Roo.each(this.buttons, function(bb) {
2575                 var b = Roo.apply({}, bb);
2576                 b.xns = b.xns || Roo.bootstrap;
2577                 b.xtype = b.xtype || 'Button';
2578                 if (typeof(b.listeners) == 'undefined') {
2579                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2580                 }
2581                 
2582                 var btn = Roo.factory(b);
2583                 
2584                 btn.render(this.el.select('.modal-footer div').first());
2585                 
2586             },this);
2587         }
2588         // render the children.
2589         var nitems = [];
2590         
2591         if(typeof(this.items) != 'undefined'){
2592             var items = this.items;
2593             delete this.items;
2594
2595             for(var i =0;i < items.length;i++) {
2596                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2597             }
2598         }
2599         
2600         this.items = nitems;
2601         
2602         // where are these used - they used to be body/close/footer
2603         
2604        
2605         this.initEvents();
2606         //this.el.addClass([this.fieldClass, this.cls]);
2607         
2608     },
2609     
2610     getAutoCreate : function(){
2611         
2612         
2613         var bdy = {
2614                 cls : 'modal-body',
2615                 html : this.html || ''
2616         };
2617         
2618         var title = {
2619             tag: 'h4',
2620             cls : 'modal-title',
2621             html : this.title
2622         };
2623         
2624         if(this.specificTitle){
2625             title = this.title;
2626             
2627         };
2628         
2629         var header = [];
2630         if (this.allow_close) {
2631             header.push({
2632                 tag: 'button',
2633                 cls : 'close',
2634                 html : '&times'
2635             });
2636         }
2637         
2638         header.push(title);
2639         
2640         var size = '';
2641         
2642         if(this.size.length){
2643             size = 'modal-' + this.size;
2644         }
2645         
2646         var modal = {
2647             cls: "modal",
2648             style : 'display: none',
2649             cn : [
2650                 {
2651                     cls: "modal-dialog " + size,
2652                     cn : [
2653                         {
2654                             cls : "modal-content",
2655                             cn : [
2656                                 {
2657                                     cls : 'modal-header',
2658                                     cn : header
2659                                 },
2660                                 bdy,
2661                                 {
2662                                     cls : 'modal-footer',
2663                                     cn : [
2664                                         {
2665                                             tag: 'div',
2666                                             cls: 'btn-' + this.buttonPosition
2667                                         }
2668                                     ]
2669                                     
2670                                 }
2671                                 
2672                                 
2673                             ]
2674                             
2675                         }
2676                     ]
2677                         
2678                 }
2679             ]
2680         };
2681         
2682         if(this.animate){
2683             modal.cls += ' fade';
2684         }
2685         
2686         return modal;
2687           
2688     },
2689     getChildContainer : function() {
2690          
2691          return this.bodyEl;
2692         
2693     },
2694     getButtonContainer : function() {
2695          return this.el.select('.modal-footer div',true).first();
2696         
2697     },
2698     initEvents : function()
2699     {
2700         if (this.allow_close) {
2701             this.closeEl.on('click', this.hide, this);
2702         }
2703         Roo.EventManager.onWindowResize(this.resize, this, true);
2704         
2705  
2706     },
2707     
2708     resize : function()
2709     {
2710         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2711         if (this.fitwindow) {
2712             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2713             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2714             this.setSize(w,h);
2715         }
2716     },
2717     
2718     setSize : function(w,h)
2719     {
2720         if (!w && !h) {
2721             return;
2722         }
2723         this.resizeTo(w,h);
2724     },
2725     
2726     show : function() {
2727         
2728         if (!this.rendered) {
2729             this.render();
2730         }
2731         
2732         this.el.setStyle('display', 'block');
2733         
2734         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2735             var _this = this;
2736             (function(){
2737                 this.el.addClass('in');
2738             }).defer(50, this);
2739         }else{
2740             this.el.addClass('in');
2741             
2742         }
2743         
2744         // not sure how we can show data in here.. 
2745         //if (this.tmpl) {
2746         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2747         //}
2748         
2749         Roo.get(document.body).addClass("x-body-masked");
2750         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2751         this.maskEl.show();
2752         this.el.setStyle('zIndex', '10001');
2753        
2754         this.fireEvent('show', this);
2755         
2756         this.resize();
2757         
2758         (function () {
2759             this.items.forEach( function(e) {
2760                 e.layout ? e.layout() : false;
2761                     
2762             });
2763         }).defer(100,this);
2764         
2765     },
2766     hide : function()
2767     {
2768         this.maskEl.hide();
2769         Roo.get(document.body).removeClass("x-body-masked");
2770         this.el.removeClass('in');
2771         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2772         
2773         if(this.animate){ // why
2774             var _this = this;
2775             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2776         }else{
2777             this.el.setStyle('display', 'none');
2778         }
2779         
2780         this.fireEvent('hide', this);
2781     },
2782     
2783     addButton : function(str, cb)
2784     {
2785          
2786         
2787         var b = Roo.apply({}, { html : str } );
2788         b.xns = b.xns || Roo.bootstrap;
2789         b.xtype = b.xtype || 'Button';
2790         if (typeof(b.listeners) == 'undefined') {
2791             b.listeners = { click : cb.createDelegate(this)  };
2792         }
2793         
2794         var btn = Roo.factory(b);
2795            
2796         btn.render(this.el.select('.modal-footer div').first());
2797         
2798         return btn;   
2799        
2800     },
2801     
2802     setDefaultButton : function(btn)
2803     {
2804         //this.el.select('.modal-footer').()
2805     },
2806     diff : false,
2807     
2808     resizeTo: function(w,h)
2809     {
2810         // skip.. ?? why??
2811         
2812         this.dialogEl.setWidth(w);
2813         if (this.diff === false) {
2814             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2815         }
2816         
2817         this.bodyEl.setHeight(h-this.diff);
2818         
2819         
2820     },
2821     setContentSize  : function(w, h)
2822     {
2823         
2824     },
2825     onButtonClick: function(btn,e)
2826     {
2827         //Roo.log([a,b,c]);
2828         this.fireEvent('btnclick', btn.name, e);
2829     },
2830      /**
2831      * Set the title of the Dialog
2832      * @param {String} str new Title
2833      */
2834     setTitle: function(str) {
2835         this.titleEl.dom.innerHTML = str;    
2836     },
2837     /**
2838      * Set the body of the Dialog
2839      * @param {String} str new Title
2840      */
2841     setBody: function(str) {
2842         this.bodyEl.dom.innerHTML = str;    
2843     },
2844     /**
2845      * Set the body of the Dialog using the template
2846      * @param {Obj} data - apply this data to the template and replace the body contents.
2847      */
2848     applyBody: function(obj)
2849     {
2850         if (!this.tmpl) {
2851             Roo.log("Error - using apply Body without a template");
2852             //code
2853         }
2854         this.tmpl.overwrite(this.bodyEl, obj);
2855     }
2856     
2857 });
2858
2859
2860 Roo.apply(Roo.bootstrap.Modal,  {
2861     /**
2862          * Button config that displays a single OK button
2863          * @type Object
2864          */
2865         OK :  [{
2866             name : 'ok',
2867             weight : 'primary',
2868             html : 'OK'
2869         }], 
2870         /**
2871          * Button config that displays Yes and No buttons
2872          * @type Object
2873          */
2874         YESNO : [
2875             {
2876                 name  : 'no',
2877                 html : 'No'
2878             },
2879             {
2880                 name  :'yes',
2881                 weight : 'primary',
2882                 html : 'Yes'
2883             }
2884         ],
2885         
2886         /**
2887          * Button config that displays OK and Cancel buttons
2888          * @type Object
2889          */
2890         OKCANCEL : [
2891             {
2892                name : 'cancel',
2893                 html : 'Cancel'
2894             },
2895             {
2896                 name : 'ok',
2897                 weight : 'primary',
2898                 html : 'OK'
2899             }
2900         ],
2901         /**
2902          * Button config that displays Yes, No and Cancel buttons
2903          * @type Object
2904          */
2905         YESNOCANCEL : [
2906             {
2907                 name : 'yes',
2908                 weight : 'primary',
2909                 html : 'Yes'
2910             },
2911             {
2912                 name : 'no',
2913                 html : 'No'
2914             },
2915             {
2916                 name : 'cancel',
2917                 html : 'Cancel'
2918             }
2919         ]
2920 });
2921  
2922  /*
2923  * - LGPL
2924  *
2925  * messagebox - can be used as a replace
2926  * 
2927  */
2928 /**
2929  * @class Roo.MessageBox
2930  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2931  * Example usage:
2932  *<pre><code>
2933 // Basic alert:
2934 Roo.Msg.alert('Status', 'Changes saved successfully.');
2935
2936 // Prompt for user data:
2937 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2938     if (btn == 'ok'){
2939         // process text value...
2940     }
2941 });
2942
2943 // Show a dialog using config options:
2944 Roo.Msg.show({
2945    title:'Save Changes?',
2946    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2947    buttons: Roo.Msg.YESNOCANCEL,
2948    fn: processResult,
2949    animEl: 'elId'
2950 });
2951 </code></pre>
2952  * @singleton
2953  */
2954 Roo.bootstrap.MessageBox = function(){
2955     var dlg, opt, mask, waitTimer;
2956     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2957     var buttons, activeTextEl, bwidth;
2958
2959     
2960     // private
2961     var handleButton = function(button){
2962         dlg.hide();
2963         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2964     };
2965
2966     // private
2967     var handleHide = function(){
2968         if(opt && opt.cls){
2969             dlg.el.removeClass(opt.cls);
2970         }
2971         //if(waitTimer){
2972         //    Roo.TaskMgr.stop(waitTimer);
2973         //    waitTimer = null;
2974         //}
2975     };
2976
2977     // private
2978     var updateButtons = function(b){
2979         var width = 0;
2980         if(!b){
2981             buttons["ok"].hide();
2982             buttons["cancel"].hide();
2983             buttons["yes"].hide();
2984             buttons["no"].hide();
2985             //dlg.footer.dom.style.display = 'none';
2986             return width;
2987         }
2988         dlg.footerEl.dom.style.display = '';
2989         for(var k in buttons){
2990             if(typeof buttons[k] != "function"){
2991                 if(b[k]){
2992                     buttons[k].show();
2993                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2994                     width += buttons[k].el.getWidth()+15;
2995                 }else{
2996                     buttons[k].hide();
2997                 }
2998             }
2999         }
3000         return width;
3001     };
3002
3003     // private
3004     var handleEsc = function(d, k, e){
3005         if(opt && opt.closable !== false){
3006             dlg.hide();
3007         }
3008         if(e){
3009             e.stopEvent();
3010         }
3011     };
3012
3013     return {
3014         /**
3015          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3016          * @return {Roo.BasicDialog} The BasicDialog element
3017          */
3018         getDialog : function(){
3019            if(!dlg){
3020                 dlg = new Roo.bootstrap.Modal( {
3021                     //draggable: true,
3022                     //resizable:false,
3023                     //constraintoviewport:false,
3024                     //fixedcenter:true,
3025                     //collapsible : false,
3026                     //shim:true,
3027                     //modal: true,
3028                   //  width:400,
3029                   //  height:100,
3030                     //buttonAlign:"center",
3031                     closeClick : function(){
3032                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3033                             handleButton("no");
3034                         }else{
3035                             handleButton("cancel");
3036                         }
3037                     }
3038                 });
3039                 dlg.render();
3040                 dlg.on("hide", handleHide);
3041                 mask = dlg.mask;
3042                 //dlg.addKeyListener(27, handleEsc);
3043                 buttons = {};
3044                 this.buttons = buttons;
3045                 var bt = this.buttonText;
3046                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3047                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3048                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3049                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3050                 //Roo.log(buttons);
3051                 bodyEl = dlg.bodyEl.createChild({
3052
3053                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3054                         '<textarea class="roo-mb-textarea"></textarea>' +
3055                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3056                 });
3057                 msgEl = bodyEl.dom.firstChild;
3058                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3059                 textboxEl.enableDisplayMode();
3060                 textboxEl.addKeyListener([10,13], function(){
3061                     if(dlg.isVisible() && opt && opt.buttons){
3062                         if(opt.buttons.ok){
3063                             handleButton("ok");
3064                         }else if(opt.buttons.yes){
3065                             handleButton("yes");
3066                         }
3067                     }
3068                 });
3069                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3070                 textareaEl.enableDisplayMode();
3071                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3072                 progressEl.enableDisplayMode();
3073                 var pf = progressEl.dom.firstChild;
3074                 if (pf) {
3075                     pp = Roo.get(pf.firstChild);
3076                     pp.setHeight(pf.offsetHeight);
3077                 }
3078                 
3079             }
3080             return dlg;
3081         },
3082
3083         /**
3084          * Updates the message box body text
3085          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3086          * the XHTML-compliant non-breaking space character '&amp;#160;')
3087          * @return {Roo.MessageBox} This message box
3088          */
3089         updateText : function(text){
3090             if(!dlg.isVisible() && !opt.width){
3091                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3092             }
3093             msgEl.innerHTML = text || '&#160;';
3094       
3095             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3096             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3097             var w = Math.max(
3098                     Math.min(opt.width || cw , this.maxWidth), 
3099                     Math.max(opt.minWidth || this.minWidth, bwidth)
3100             );
3101             if(opt.prompt){
3102                 activeTextEl.setWidth(w);
3103             }
3104             if(dlg.isVisible()){
3105                 dlg.fixedcenter = false;
3106             }
3107             // to big, make it scroll. = But as usual stupid IE does not support
3108             // !important..
3109             
3110             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3111                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3112                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3113             } else {
3114                 bodyEl.dom.style.height = '';
3115                 bodyEl.dom.style.overflowY = '';
3116             }
3117             if (cw > w) {
3118                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3119             } else {
3120                 bodyEl.dom.style.overflowX = '';
3121             }
3122             
3123             dlg.setContentSize(w, bodyEl.getHeight());
3124             if(dlg.isVisible()){
3125                 dlg.fixedcenter = true;
3126             }
3127             return this;
3128         },
3129
3130         /**
3131          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3132          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3133          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3134          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3135          * @return {Roo.MessageBox} This message box
3136          */
3137         updateProgress : function(value, text){
3138             if(text){
3139                 this.updateText(text);
3140             }
3141             if (pp) { // weird bug on my firefox - for some reason this is not defined
3142                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3143             }
3144             return this;
3145         },        
3146
3147         /**
3148          * Returns true if the message box is currently displayed
3149          * @return {Boolean} True if the message box is visible, else false
3150          */
3151         isVisible : function(){
3152             return dlg && dlg.isVisible();  
3153         },
3154
3155         /**
3156          * Hides the message box if it is displayed
3157          */
3158         hide : function(){
3159             if(this.isVisible()){
3160                 dlg.hide();
3161             }  
3162         },
3163
3164         /**
3165          * Displays a new message box, or reinitializes an existing message box, based on the config options
3166          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3167          * The following config object properties are supported:
3168          * <pre>
3169 Property    Type             Description
3170 ----------  ---------------  ------------------------------------------------------------------------------------
3171 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3172                                    closes (defaults to undefined)
3173 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3174                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3175 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3176                                    progress and wait dialogs will ignore this property and always hide the
3177                                    close button as they can only be closed programmatically.
3178 cls               String           A custom CSS class to apply to the message box element
3179 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3180                                    displayed (defaults to 75)
3181 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3182                                    function will be btn (the name of the button that was clicked, if applicable,
3183                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3184                                    Progress and wait dialogs will ignore this option since they do not respond to
3185                                    user actions and can only be closed programmatically, so any required function
3186                                    should be called by the same code after it closes the dialog.
3187 icon              String           A CSS class that provides a background image to be used as an icon for
3188                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3189 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3190 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3191 modal             Boolean          False to allow user interaction with the page while the message box is
3192                                    displayed (defaults to true)
3193 msg               String           A string that will replace the existing message box body text (defaults
3194                                    to the XHTML-compliant non-breaking space character '&#160;')
3195 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3196 progress          Boolean          True to display a progress bar (defaults to false)
3197 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3198 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3199 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3200 title             String           The title text
3201 value             String           The string value to set into the active textbox element if displayed
3202 wait              Boolean          True to display a progress bar (defaults to false)
3203 width             Number           The width of the dialog in pixels
3204 </pre>
3205          *
3206          * Example usage:
3207          * <pre><code>
3208 Roo.Msg.show({
3209    title: 'Address',
3210    msg: 'Please enter your address:',
3211    width: 300,
3212    buttons: Roo.MessageBox.OKCANCEL,
3213    multiline: true,
3214    fn: saveAddress,
3215    animEl: 'addAddressBtn'
3216 });
3217 </code></pre>
3218          * @param {Object} config Configuration options
3219          * @return {Roo.MessageBox} This message box
3220          */
3221         show : function(options)
3222         {
3223             
3224             // this causes nightmares if you show one dialog after another
3225             // especially on callbacks..
3226              
3227             if(this.isVisible()){
3228                 
3229                 this.hide();
3230                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3231                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3232                 Roo.log("New Dialog Message:" +  options.msg )
3233                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3234                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3235                 
3236             }
3237             var d = this.getDialog();
3238             opt = options;
3239             d.setTitle(opt.title || "&#160;");
3240             d.closeEl.setDisplayed(opt.closable !== false);
3241             activeTextEl = textboxEl;
3242             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3243             if(opt.prompt){
3244                 if(opt.multiline){
3245                     textboxEl.hide();
3246                     textareaEl.show();
3247                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3248                         opt.multiline : this.defaultTextHeight);
3249                     activeTextEl = textareaEl;
3250                 }else{
3251                     textboxEl.show();
3252                     textareaEl.hide();
3253                 }
3254             }else{
3255                 textboxEl.hide();
3256                 textareaEl.hide();
3257             }
3258             progressEl.setDisplayed(opt.progress === true);
3259             this.updateProgress(0);
3260             activeTextEl.dom.value = opt.value || "";
3261             if(opt.prompt){
3262                 dlg.setDefaultButton(activeTextEl);
3263             }else{
3264                 var bs = opt.buttons;
3265                 var db = null;
3266                 if(bs && bs.ok){
3267                     db = buttons["ok"];
3268                 }else if(bs && bs.yes){
3269                     db = buttons["yes"];
3270                 }
3271                 dlg.setDefaultButton(db);
3272             }
3273             bwidth = updateButtons(opt.buttons);
3274             this.updateText(opt.msg);
3275             if(opt.cls){
3276                 d.el.addClass(opt.cls);
3277             }
3278             d.proxyDrag = opt.proxyDrag === true;
3279             d.modal = opt.modal !== false;
3280             d.mask = opt.modal !== false ? mask : false;
3281             if(!d.isVisible()){
3282                 // force it to the end of the z-index stack so it gets a cursor in FF
3283                 document.body.appendChild(dlg.el.dom);
3284                 d.animateTarget = null;
3285                 d.show(options.animEl);
3286             }
3287             return this;
3288         },
3289
3290         /**
3291          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3292          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3293          * and closing the message box when the process is complete.
3294          * @param {String} title The title bar text
3295          * @param {String} msg The message box body text
3296          * @return {Roo.MessageBox} This message box
3297          */
3298         progress : function(title, msg){
3299             this.show({
3300                 title : title,
3301                 msg : msg,
3302                 buttons: false,
3303                 progress:true,
3304                 closable:false,
3305                 minWidth: this.minProgressWidth,
3306                 modal : true
3307             });
3308             return this;
3309         },
3310
3311         /**
3312          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3313          * If a callback function is passed it will be called after the user clicks the button, and the
3314          * id of the button that was clicked will be passed as the only parameter to the callback
3315          * (could also be the top-right close button).
3316          * @param {String} title The title bar text
3317          * @param {String} msg The message box body text
3318          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3319          * @param {Object} scope (optional) The scope of the callback function
3320          * @return {Roo.MessageBox} This message box
3321          */
3322         alert : function(title, msg, fn, scope){
3323             this.show({
3324                 title : title,
3325                 msg : msg,
3326                 buttons: this.OK,
3327                 fn: fn,
3328                 scope : scope,
3329                 modal : true
3330             });
3331             return this;
3332         },
3333
3334         /**
3335          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3336          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3337          * You are responsible for closing the message box when the process is complete.
3338          * @param {String} msg The message box body text
3339          * @param {String} title (optional) The title bar text
3340          * @return {Roo.MessageBox} This message box
3341          */
3342         wait : function(msg, title){
3343             this.show({
3344                 title : title,
3345                 msg : msg,
3346                 buttons: false,
3347                 closable:false,
3348                 progress:true,
3349                 modal:true,
3350                 width:300,
3351                 wait:true
3352             });
3353             waitTimer = Roo.TaskMgr.start({
3354                 run: function(i){
3355                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3356                 },
3357                 interval: 1000
3358             });
3359             return this;
3360         },
3361
3362         /**
3363          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3364          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3365          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3366          * @param {String} title The title bar text
3367          * @param {String} msg The message box body text
3368          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3369          * @param {Object} scope (optional) The scope of the callback function
3370          * @return {Roo.MessageBox} This message box
3371          */
3372         confirm : function(title, msg, fn, scope){
3373             this.show({
3374                 title : title,
3375                 msg : msg,
3376                 buttons: this.YESNO,
3377                 fn: fn,
3378                 scope : scope,
3379                 modal : true
3380             });
3381             return this;
3382         },
3383
3384         /**
3385          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3386          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3387          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3388          * (could also be the top-right close button) and the text that was entered will be passed as the two
3389          * parameters to the callback.
3390          * @param {String} title The title bar text
3391          * @param {String} msg The message box body text
3392          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3393          * @param {Object} scope (optional) The scope of the callback function
3394          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3395          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3396          * @return {Roo.MessageBox} This message box
3397          */
3398         prompt : function(title, msg, fn, scope, multiline){
3399             this.show({
3400                 title : title,
3401                 msg : msg,
3402                 buttons: this.OKCANCEL,
3403                 fn: fn,
3404                 minWidth:250,
3405                 scope : scope,
3406                 prompt:true,
3407                 multiline: multiline,
3408                 modal : true
3409             });
3410             return this;
3411         },
3412
3413         /**
3414          * Button config that displays a single OK button
3415          * @type Object
3416          */
3417         OK : {ok:true},
3418         /**
3419          * Button config that displays Yes and No buttons
3420          * @type Object
3421          */
3422         YESNO : {yes:true, no:true},
3423         /**
3424          * Button config that displays OK and Cancel buttons
3425          * @type Object
3426          */
3427         OKCANCEL : {ok:true, cancel:true},
3428         /**
3429          * Button config that displays Yes, No and Cancel buttons
3430          * @type Object
3431          */
3432         YESNOCANCEL : {yes:true, no:true, cancel:true},
3433
3434         /**
3435          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3436          * @type Number
3437          */
3438         defaultTextHeight : 75,
3439         /**
3440          * The maximum width in pixels of the message box (defaults to 600)
3441          * @type Number
3442          */
3443         maxWidth : 600,
3444         /**
3445          * The minimum width in pixels of the message box (defaults to 100)
3446          * @type Number
3447          */
3448         minWidth : 100,
3449         /**
3450          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3451          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3452          * @type Number
3453          */
3454         minProgressWidth : 250,
3455         /**
3456          * An object containing the default button text strings that can be overriden for localized language support.
3457          * Supported properties are: ok, cancel, yes and no.
3458          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3459          * @type Object
3460          */
3461         buttonText : {
3462             ok : "OK",
3463             cancel : "Cancel",
3464             yes : "Yes",
3465             no : "No"
3466         }
3467     };
3468 }();
3469
3470 /**
3471  * Shorthand for {@link Roo.MessageBox}
3472  */
3473 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3474 Roo.Msg = Roo.Msg || Roo.MessageBox;
3475 /*
3476  * - LGPL
3477  *
3478  * navbar
3479  * 
3480  */
3481
3482 /**
3483  * @class Roo.bootstrap.Navbar
3484  * @extends Roo.bootstrap.Component
3485  * Bootstrap Navbar class
3486
3487  * @constructor
3488  * Create a new Navbar
3489  * @param {Object} config The config object
3490  */
3491
3492
3493 Roo.bootstrap.Navbar = function(config){
3494     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3495     this.addEvents({
3496         // raw events
3497         /**
3498          * @event beforetoggle
3499          * Fire before toggle the menu
3500          * @param {Roo.EventObject} e
3501          */
3502         "beforetoggle" : true
3503     });
3504 };
3505
3506 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3507     
3508     
3509    
3510     // private
3511     navItems : false,
3512     loadMask : false,
3513     
3514     
3515     getAutoCreate : function(){
3516         
3517         
3518         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3519         
3520     },
3521     
3522     initEvents :function ()
3523     {
3524         //Roo.log(this.el.select('.navbar-toggle',true));
3525         this.el.select('.navbar-toggle',true).on('click', function() {
3526             if(this.fireEvent('beforetoggle', this) !== false){
3527                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3528             }
3529             
3530         }, this);
3531         
3532         var mark = {
3533             tag: "div",
3534             cls:"x-dlg-mask"
3535         };
3536         
3537         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3538         
3539         var size = this.el.getSize();
3540         this.maskEl.setSize(size.width, size.height);
3541         this.maskEl.enableDisplayMode("block");
3542         this.maskEl.hide();
3543         
3544         if(this.loadMask){
3545             this.maskEl.show();
3546         }
3547     },
3548     
3549     
3550     getChildContainer : function()
3551     {
3552         if (this.el.select('.collapse').getCount()) {
3553             return this.el.select('.collapse',true).first();
3554         }
3555         
3556         return this.el;
3557     },
3558     
3559     mask : function()
3560     {
3561         this.maskEl.show();
3562     },
3563     
3564     unmask : function()
3565     {
3566         this.maskEl.hide();
3567     } 
3568     
3569     
3570     
3571     
3572 });
3573
3574
3575
3576  
3577
3578  /*
3579  * - LGPL
3580  *
3581  * navbar
3582  * 
3583  */
3584
3585 /**
3586  * @class Roo.bootstrap.NavSimplebar
3587  * @extends Roo.bootstrap.Navbar
3588  * Bootstrap Sidebar class
3589  *
3590  * @cfg {Boolean} inverse is inverted color
3591  * 
3592  * @cfg {String} type (nav | pills | tabs)
3593  * @cfg {Boolean} arrangement stacked | justified
3594  * @cfg {String} align (left | right) alignment
3595  * 
3596  * @cfg {Boolean} main (true|false) main nav bar? default false
3597  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3598  * 
3599  * @cfg {String} tag (header|footer|nav|div) default is nav 
3600
3601  * 
3602  * 
3603  * 
3604  * @constructor
3605  * Create a new Sidebar
3606  * @param {Object} config The config object
3607  */
3608
3609
3610 Roo.bootstrap.NavSimplebar = function(config){
3611     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3612 };
3613
3614 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3615     
3616     inverse: false,
3617     
3618     type: false,
3619     arrangement: '',
3620     align : false,
3621     
3622     
3623     
3624     main : false,
3625     
3626     
3627     tag : false,
3628     
3629     
3630     getAutoCreate : function(){
3631         
3632         
3633         var cfg = {
3634             tag : this.tag || 'div',
3635             cls : 'navbar'
3636         };
3637           
3638         
3639         cfg.cn = [
3640             {
3641                 cls: 'nav',
3642                 tag : 'ul'
3643             }
3644         ];
3645         
3646          
3647         this.type = this.type || 'nav';
3648         if (['tabs','pills'].indexOf(this.type)!==-1) {
3649             cfg.cn[0].cls += ' nav-' + this.type
3650         
3651         
3652         } else {
3653             if (this.type!=='nav') {
3654                 Roo.log('nav type must be nav/tabs/pills')
3655             }
3656             cfg.cn[0].cls += ' navbar-nav'
3657         }
3658         
3659         
3660         
3661         
3662         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3663             cfg.cn[0].cls += ' nav-' + this.arrangement;
3664         }
3665         
3666         
3667         if (this.align === 'right') {
3668             cfg.cn[0].cls += ' navbar-right';
3669         }
3670         
3671         if (this.inverse) {
3672             cfg.cls += ' navbar-inverse';
3673             
3674         }
3675         
3676         
3677         return cfg;
3678     
3679         
3680     }
3681     
3682     
3683     
3684 });
3685
3686
3687
3688  
3689
3690  
3691        /*
3692  * - LGPL
3693  *
3694  * navbar
3695  * 
3696  */
3697
3698 /**
3699  * @class Roo.bootstrap.NavHeaderbar
3700  * @extends Roo.bootstrap.NavSimplebar
3701  * Bootstrap Sidebar class
3702  *
3703  * @cfg {String} brand what is brand
3704  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3705  * @cfg {String} brand_href href of the brand
3706  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3707  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3708  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3709  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3710  * 
3711  * @constructor
3712  * Create a new Sidebar
3713  * @param {Object} config The config object
3714  */
3715
3716
3717 Roo.bootstrap.NavHeaderbar = function(config){
3718     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3719       
3720 };
3721
3722 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3723     
3724     position: '',
3725     brand: '',
3726     brand_href: false,
3727     srButton : true,
3728     autohide : false,
3729     desktopCenter : false,
3730    
3731     
3732     getAutoCreate : function(){
3733         
3734         var   cfg = {
3735             tag: this.nav || 'nav',
3736             cls: 'navbar',
3737             role: 'navigation',
3738             cn: []
3739         };
3740         
3741         var cn = cfg.cn;
3742         if (this.desktopCenter) {
3743             cn.push({cls : 'container', cn : []});
3744             cn = cn[0].cn;
3745         }
3746         
3747         if(this.srButton){
3748             cn.push({
3749                 tag: 'div',
3750                 cls: 'navbar-header',
3751                 cn: [
3752                     {
3753                         tag: 'button',
3754                         type: 'button',
3755                         cls: 'navbar-toggle',
3756                         'data-toggle': 'collapse',
3757                         cn: [
3758                             {
3759                                 tag: 'span',
3760                                 cls: 'sr-only',
3761                                 html: 'Toggle navigation'
3762                             },
3763                             {
3764                                 tag: 'span',
3765                                 cls: 'icon-bar'
3766                             },
3767                             {
3768                                 tag: 'span',
3769                                 cls: 'icon-bar'
3770                             },
3771                             {
3772                                 tag: 'span',
3773                                 cls: 'icon-bar'
3774                             }
3775                         ]
3776                     }
3777                 ]
3778             });
3779         }
3780         
3781         cn.push({
3782             tag: 'div',
3783             cls: 'collapse navbar-collapse',
3784             cn : []
3785         });
3786         
3787         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3788         
3789         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3790             cfg.cls += ' navbar-' + this.position;
3791             
3792             // tag can override this..
3793             
3794             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3795         }
3796         
3797         if (this.brand !== '') {
3798             cn[0].cn.push({
3799                 tag: 'a',
3800                 href: this.brand_href ? this.brand_href : '#',
3801                 cls: 'navbar-brand',
3802                 cn: [
3803                 this.brand
3804                 ]
3805             });
3806         }
3807         
3808         if(this.main){
3809             cfg.cls += ' main-nav';
3810         }
3811         
3812         
3813         return cfg;
3814
3815         
3816     },
3817     getHeaderChildContainer : function()
3818     {
3819         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3820             return this.el.select('.navbar-header',true).first();
3821         }
3822         
3823         return this.getChildContainer();
3824     },
3825     
3826     
3827     initEvents : function()
3828     {
3829         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3830         
3831         if (this.autohide) {
3832             
3833             var prevScroll = 0;
3834             var ft = this.el;
3835             
3836             Roo.get(document).on('scroll',function(e) {
3837                 var ns = Roo.get(document).getScroll().top;
3838                 var os = prevScroll;
3839                 prevScroll = ns;
3840                 
3841                 if(ns > os){
3842                     ft.removeClass('slideDown');
3843                     ft.addClass('slideUp');
3844                     return;
3845                 }
3846                 ft.removeClass('slideUp');
3847                 ft.addClass('slideDown');
3848                  
3849               
3850           },this);
3851         }
3852     }    
3853     
3854 });
3855
3856
3857
3858  
3859
3860  /*
3861  * - LGPL
3862  *
3863  * navbar
3864  * 
3865  */
3866
3867 /**
3868  * @class Roo.bootstrap.NavSidebar
3869  * @extends Roo.bootstrap.Navbar
3870  * Bootstrap Sidebar class
3871  * 
3872  * @constructor
3873  * Create a new Sidebar
3874  * @param {Object} config The config object
3875  */
3876
3877
3878 Roo.bootstrap.NavSidebar = function(config){
3879     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3880 };
3881
3882 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3883     
3884     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3885     
3886     getAutoCreate : function(){
3887         
3888         
3889         return  {
3890             tag: 'div',
3891             cls: 'sidebar sidebar-nav'
3892         };
3893     
3894         
3895     }
3896     
3897     
3898     
3899 });
3900
3901
3902
3903  
3904
3905  /*
3906  * - LGPL
3907  *
3908  * nav group
3909  * 
3910  */
3911
3912 /**
3913  * @class Roo.bootstrap.NavGroup
3914  * @extends Roo.bootstrap.Component
3915  * Bootstrap NavGroup class
3916  * @cfg {String} align (left|right)
3917  * @cfg {Boolean} inverse
3918  * @cfg {String} type (nav|pills|tab) default nav
3919  * @cfg {String} navId - reference Id for navbar.
3920
3921  * 
3922  * @constructor
3923  * Create a new nav group
3924  * @param {Object} config The config object
3925  */
3926
3927 Roo.bootstrap.NavGroup = function(config){
3928     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3929     this.navItems = [];
3930    
3931     Roo.bootstrap.NavGroup.register(this);
3932      this.addEvents({
3933         /**
3934              * @event changed
3935              * Fires when the active item changes
3936              * @param {Roo.bootstrap.NavGroup} this
3937              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3938              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3939          */
3940         'changed': true
3941      });
3942     
3943 };
3944
3945 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3946     
3947     align: '',
3948     inverse: false,
3949     form: false,
3950     type: 'nav',
3951     navId : '',
3952     // private
3953     
3954     navItems : false, 
3955     
3956     getAutoCreate : function()
3957     {
3958         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3959         
3960         cfg = {
3961             tag : 'ul',
3962             cls: 'nav' 
3963         };
3964         
3965         if (['tabs','pills'].indexOf(this.type)!==-1) {
3966             cfg.cls += ' nav-' + this.type
3967         } else {
3968             if (this.type!=='nav') {
3969                 Roo.log('nav type must be nav/tabs/pills')
3970             }
3971             cfg.cls += ' navbar-nav'
3972         }
3973         
3974         if (this.parent().sidebar) {
3975             cfg = {
3976                 tag: 'ul',
3977                 cls: 'dashboard-menu sidebar-menu'
3978             };
3979             
3980             return cfg;
3981         }
3982         
3983         if (this.form === true) {
3984             cfg = {
3985                 tag: 'form',
3986                 cls: 'navbar-form'
3987             };
3988             
3989             if (this.align === 'right') {
3990                 cfg.cls += ' navbar-right';
3991             } else {
3992                 cfg.cls += ' navbar-left';
3993             }
3994         }
3995         
3996         if (this.align === 'right') {
3997             cfg.cls += ' navbar-right';
3998         }
3999         
4000         if (this.inverse) {
4001             cfg.cls += ' navbar-inverse';
4002             
4003         }
4004         
4005         
4006         return cfg;
4007     },
4008     /**
4009     * sets the active Navigation item
4010     * @param {Roo.bootstrap.NavItem} the new current navitem
4011     */
4012     setActiveItem : function(item)
4013     {
4014         var prev = false;
4015         Roo.each(this.navItems, function(v){
4016             if (v == item) {
4017                 return ;
4018             }
4019             if (v.isActive()) {
4020                 v.setActive(false, true);
4021                 prev = v;
4022                 
4023             }
4024             
4025         });
4026
4027         item.setActive(true, true);
4028         this.fireEvent('changed', this, item, prev);
4029         
4030         
4031     },
4032     /**
4033     * gets the active Navigation item
4034     * @return {Roo.bootstrap.NavItem} the current navitem
4035     */
4036     getActive : function()
4037     {
4038         
4039         var prev = false;
4040         Roo.each(this.navItems, function(v){
4041             
4042             if (v.isActive()) {
4043                 prev = v;
4044                 
4045             }
4046             
4047         });
4048         return prev;
4049     },
4050     
4051     indexOfNav : function()
4052     {
4053         
4054         var prev = false;
4055         Roo.each(this.navItems, function(v,i){
4056             
4057             if (v.isActive()) {
4058                 prev = i;
4059                 
4060             }
4061             
4062         });
4063         return prev;
4064     },
4065     /**
4066     * adds a Navigation item
4067     * @param {Roo.bootstrap.NavItem} the navitem to add
4068     */
4069     addItem : function(cfg)
4070     {
4071         var cn = new Roo.bootstrap.NavItem(cfg);
4072         this.register(cn);
4073         cn.parentId = this.id;
4074         cn.onRender(this.el, null);
4075         return cn;
4076     },
4077     /**
4078     * register a Navigation item
4079     * @param {Roo.bootstrap.NavItem} the navitem to add
4080     */
4081     register : function(item)
4082     {
4083         this.navItems.push( item);
4084         item.navId = this.navId;
4085     
4086     },
4087     
4088     /**
4089     * clear all the Navigation item
4090     */
4091    
4092     clearAll : function()
4093     {
4094         this.navItems = [];
4095         this.el.dom.innerHTML = '';
4096     },
4097     
4098     getNavItem: function(tabId)
4099     {
4100         var ret = false;
4101         Roo.each(this.navItems, function(e) {
4102             if (e.tabId == tabId) {
4103                ret =  e;
4104                return false;
4105             }
4106             return true;
4107             
4108         });
4109         return ret;
4110     },
4111     
4112     setActiveNext : function()
4113     {
4114         var i = this.indexOfNav(this.getActive());
4115         if (i > this.navItems.length) {
4116             return;
4117         }
4118         this.setActiveItem(this.navItems[i+1]);
4119     },
4120     setActivePrev : function()
4121     {
4122         var i = this.indexOfNav(this.getActive());
4123         if (i  < 1) {
4124             return;
4125         }
4126         this.setActiveItem(this.navItems[i-1]);
4127     },
4128     clearWasActive : function(except) {
4129         Roo.each(this.navItems, function(e) {
4130             if (e.tabId != except.tabId && e.was_active) {
4131                e.was_active = false;
4132                return false;
4133             }
4134             return true;
4135             
4136         });
4137     },
4138     getWasActive : function ()
4139     {
4140         var r = false;
4141         Roo.each(this.navItems, function(e) {
4142             if (e.was_active) {
4143                r = e;
4144                return false;
4145             }
4146             return true;
4147             
4148         });
4149         return r;
4150     }
4151     
4152     
4153 });
4154
4155  
4156 Roo.apply(Roo.bootstrap.NavGroup, {
4157     
4158     groups: {},
4159      /**
4160     * register a Navigation Group
4161     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4162     */
4163     register : function(navgrp)
4164     {
4165         this.groups[navgrp.navId] = navgrp;
4166         
4167     },
4168     /**
4169     * fetch a Navigation Group based on the navigation ID
4170     * @param {string} the navgroup to add
4171     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4172     */
4173     get: function(navId) {
4174         if (typeof(this.groups[navId]) == 'undefined') {
4175             return false;
4176             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4177         }
4178         return this.groups[navId] ;
4179     }
4180     
4181     
4182     
4183 });
4184
4185  /*
4186  * - LGPL
4187  *
4188  * row
4189  * 
4190  */
4191
4192 /**
4193  * @class Roo.bootstrap.NavItem
4194  * @extends Roo.bootstrap.Component
4195  * Bootstrap Navbar.NavItem class
4196  * @cfg {String} href  link to
4197  * @cfg {String} html content of button
4198  * @cfg {String} badge text inside badge
4199  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4200  * @cfg {String} glyphicon name of glyphicon
4201  * @cfg {String} icon name of font awesome icon
4202  * @cfg {Boolean} active Is item active
4203  * @cfg {Boolean} disabled Is item disabled
4204  
4205  * @cfg {Boolean} preventDefault (true | false) default false
4206  * @cfg {String} tabId the tab that this item activates.
4207  * @cfg {String} tagtype (a|span) render as a href or span?
4208  * @cfg {Boolean} animateRef (true|false) link to element default false  
4209   
4210  * @constructor
4211  * Create a new Navbar Item
4212  * @param {Object} config The config object
4213  */
4214 Roo.bootstrap.NavItem = function(config){
4215     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4216     this.addEvents({
4217         // raw events
4218         /**
4219          * @event click
4220          * The raw click event for the entire grid.
4221          * @param {Roo.EventObject} e
4222          */
4223         "click" : true,
4224          /**
4225             * @event changed
4226             * Fires when the active item active state changes
4227             * @param {Roo.bootstrap.NavItem} this
4228             * @param {boolean} state the new state
4229              
4230          */
4231         'changed': true,
4232         /**
4233             * @event scrollto
4234             * Fires when scroll to element
4235             * @param {Roo.bootstrap.NavItem} this
4236             * @param {Object} options
4237             * @param {Roo.EventObject} e
4238              
4239          */
4240         'scrollto': true
4241     });
4242    
4243 };
4244
4245 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4246     
4247     href: false,
4248     html: '',
4249     badge: '',
4250     icon: false,
4251     glyphicon: false,
4252     active: false,
4253     preventDefault : false,
4254     tabId : false,
4255     tagtype : 'a',
4256     disabled : false,
4257     animateRef : false,
4258     was_active : false,
4259     
4260     getAutoCreate : function(){
4261          
4262         var cfg = {
4263             tag: 'li',
4264             cls: 'nav-item'
4265             
4266         };
4267         
4268         if (this.active) {
4269             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4270         }
4271         if (this.disabled) {
4272             cfg.cls += ' disabled';
4273         }
4274         
4275         if (this.href || this.html || this.glyphicon || this.icon) {
4276             cfg.cn = [
4277                 {
4278                     tag: this.tagtype,
4279                     href : this.href || "#",
4280                     html: this.html || ''
4281                 }
4282             ];
4283             
4284             if (this.icon) {
4285                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4286             }
4287
4288             if(this.glyphicon) {
4289                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4290             }
4291             
4292             if (this.menu) {
4293                 
4294                 cfg.cn[0].html += " <span class='caret'></span>";
4295              
4296             }
4297             
4298             if (this.badge !== '') {
4299                  
4300                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4301             }
4302         }
4303         
4304         
4305         
4306         return cfg;
4307     },
4308     initEvents: function() 
4309     {
4310         if (typeof (this.menu) != 'undefined') {
4311             this.menu.parentType = this.xtype;
4312             this.menu.triggerEl = this.el;
4313             this.menu = this.addxtype(Roo.apply({}, this.menu));
4314         }
4315         
4316         this.el.select('a',true).on('click', this.onClick, this);
4317         
4318         if(this.tagtype == 'span'){
4319             this.el.select('span',true).on('click', this.onClick, this);
4320         }
4321        
4322         // at this point parent should be available..
4323         this.parent().register(this);
4324     },
4325     
4326     onClick : function(e)
4327     {
4328         if (e.getTarget('.dropdown-menu-item')) {
4329             // did you click on a menu itemm.... - then don't trigger onclick..
4330             return;
4331         }
4332         
4333         if(
4334                 this.preventDefault || 
4335                 this.href == '#' 
4336         ){
4337             Roo.log("NavItem - prevent Default?");
4338             e.preventDefault();
4339         }
4340         
4341         if (this.disabled) {
4342             return;
4343         }
4344         
4345         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4346         if (tg && tg.transition) {
4347             Roo.log("waiting for the transitionend");
4348             return;
4349         }
4350         
4351         
4352         
4353         //Roo.log("fire event clicked");
4354         if(this.fireEvent('click', this, e) === false){
4355             return;
4356         };
4357         
4358         if(this.tagtype == 'span'){
4359             return;
4360         }
4361         
4362         //Roo.log(this.href);
4363         var ael = this.el.select('a',true).first();
4364         //Roo.log(ael);
4365         
4366         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4367             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4368             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4369                 return; // ignore... - it's a 'hash' to another page.
4370             }
4371             Roo.log("NavItem - prevent Default?");
4372             e.preventDefault();
4373             this.scrollToElement(e);
4374         }
4375         
4376         
4377         var p =  this.parent();
4378    
4379         if (['tabs','pills'].indexOf(p.type)!==-1) {
4380             if (typeof(p.setActiveItem) !== 'undefined') {
4381                 p.setActiveItem(this);
4382             }
4383         }
4384         
4385         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4386         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4387             // remove the collapsed menu expand...
4388             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4389         }
4390     },
4391     
4392     isActive: function () {
4393         return this.active
4394     },
4395     setActive : function(state, fire, is_was_active)
4396     {
4397         if (this.active && !state && this.navId) {
4398             this.was_active = true;
4399             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4400             if (nv) {
4401                 nv.clearWasActive(this);
4402             }
4403             
4404         }
4405         this.active = state;
4406         
4407         if (!state ) {
4408             this.el.removeClass('active');
4409         } else if (!this.el.hasClass('active')) {
4410             this.el.addClass('active');
4411         }
4412         if (fire) {
4413             this.fireEvent('changed', this, state);
4414         }
4415         
4416         // show a panel if it's registered and related..
4417         
4418         if (!this.navId || !this.tabId || !state || is_was_active) {
4419             return;
4420         }
4421         
4422         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4423         if (!tg) {
4424             return;
4425         }
4426         var pan = tg.getPanelByName(this.tabId);
4427         if (!pan) {
4428             return;
4429         }
4430         // if we can not flip to new panel - go back to old nav highlight..
4431         if (false == tg.showPanel(pan)) {
4432             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4433             if (nv) {
4434                 var onav = nv.getWasActive();
4435                 if (onav) {
4436                     onav.setActive(true, false, true);
4437                 }
4438             }
4439             
4440         }
4441         
4442         
4443         
4444     },
4445      // this should not be here...
4446     setDisabled : function(state)
4447     {
4448         this.disabled = state;
4449         if (!state ) {
4450             this.el.removeClass('disabled');
4451         } else if (!this.el.hasClass('disabled')) {
4452             this.el.addClass('disabled');
4453         }
4454         
4455     },
4456     
4457     /**
4458      * Fetch the element to display the tooltip on.
4459      * @return {Roo.Element} defaults to this.el
4460      */
4461     tooltipEl : function()
4462     {
4463         return this.el.select('' + this.tagtype + '', true).first();
4464     },
4465     
4466     scrollToElement : function(e)
4467     {
4468         var c = document.body;
4469         
4470         /*
4471          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4472          */
4473         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4474             c = document.documentElement;
4475         }
4476         
4477         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4478         
4479         if(!target){
4480             return;
4481         }
4482
4483         var o = target.calcOffsetsTo(c);
4484         
4485         var options = {
4486             target : target,
4487             value : o[1]
4488         };
4489         
4490         this.fireEvent('scrollto', this, options, e);
4491         
4492         Roo.get(c).scrollTo('top', options.value, true);
4493         
4494         return;
4495     }
4496 });
4497  
4498
4499  /*
4500  * - LGPL
4501  *
4502  * sidebar item
4503  *
4504  *  li
4505  *    <span> icon </span>
4506  *    <span> text </span>
4507  *    <span>badge </span>
4508  */
4509
4510 /**
4511  * @class Roo.bootstrap.NavSidebarItem
4512  * @extends Roo.bootstrap.NavItem
4513  * Bootstrap Navbar.NavSidebarItem class
4514  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4515  * {bool} open is the menu open
4516  * @constructor
4517  * Create a new Navbar Button
4518  * @param {Object} config The config object
4519  */
4520 Roo.bootstrap.NavSidebarItem = function(config){
4521     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4522     this.addEvents({
4523         // raw events
4524         /**
4525          * @event click
4526          * The raw click event for the entire grid.
4527          * @param {Roo.EventObject} e
4528          */
4529         "click" : true,
4530          /**
4531             * @event changed
4532             * Fires when the active item active state changes
4533             * @param {Roo.bootstrap.NavSidebarItem} this
4534             * @param {boolean} state the new state
4535              
4536          */
4537         'changed': true
4538     });
4539    
4540 };
4541
4542 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4543     
4544     badgeWeight : 'default',
4545     
4546     open: false,
4547     
4548     getAutoCreate : function(){
4549         
4550         
4551         var a = {
4552                 tag: 'a',
4553                 href : this.href || '#',
4554                 cls: '',
4555                 html : '',
4556                 cn : []
4557         };
4558         var cfg = {
4559             tag: 'li',
4560             cls: '',
4561             cn: [ a ]
4562         };
4563         var span = {
4564             tag: 'span',
4565             html : this.html || ''
4566         };
4567         
4568         
4569         if (this.active) {
4570             cfg.cls += ' active';
4571         }
4572         
4573         if (this.disabled) {
4574             cfg.cls += ' disabled';
4575         }
4576         if (this.open) {
4577             cfg.cls += ' open x-open';
4578         }
4579         // left icon..
4580         if (this.glyphicon || this.icon) {
4581             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4582             a.cn.push({ tag : 'i', cls : c }) ;
4583         }
4584         // html..
4585         a.cn.push(span);
4586         // then badge..
4587         if (this.badge !== '') {
4588             
4589             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4590         }
4591         // fi
4592         if (this.menu) {
4593             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4594             a.cls += 'dropdown-toggle treeview' ;
4595         }
4596         
4597         return cfg;
4598          
4599            
4600     },
4601     
4602     initEvents : function()
4603     { 
4604         if (typeof (this.menu) != 'undefined') {
4605             this.menu.parentType = this.xtype;
4606             this.menu.triggerEl = this.el;
4607             this.menu = this.addxtype(Roo.apply({}, this.menu));
4608         }
4609         
4610         this.el.on('click', this.onClick, this);
4611        
4612     
4613         if(this.badge !== ''){
4614  
4615             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4616         }
4617         
4618     },
4619     
4620     onClick : function(e)
4621     {
4622         if(this.disabled){
4623             e.preventDefault();
4624             return;
4625         }
4626         
4627         if(this.preventDefault){
4628             e.preventDefault();
4629         }
4630         
4631         this.fireEvent('click', this);
4632     },
4633     
4634     disable : function()
4635     {
4636         this.setDisabled(true);
4637     },
4638     
4639     enable : function()
4640     {
4641         this.setDisabled(false);
4642     },
4643     
4644     setDisabled : function(state)
4645     {
4646         if(this.disabled == state){
4647             return;
4648         }
4649         
4650         this.disabled = state;
4651         
4652         if (state) {
4653             this.el.addClass('disabled');
4654             return;
4655         }
4656         
4657         this.el.removeClass('disabled');
4658         
4659         return;
4660     },
4661     
4662     setActive : function(state)
4663     {
4664         if(this.active == state){
4665             return;
4666         }
4667         
4668         this.active = state;
4669         
4670         if (state) {
4671             this.el.addClass('active');
4672             return;
4673         }
4674         
4675         this.el.removeClass('active');
4676         
4677         return;
4678     },
4679     
4680     isActive: function () 
4681     {
4682         return this.active;
4683     },
4684     
4685     setBadge : function(str)
4686     {
4687         if(!this.badgeEl){
4688             return;
4689         }
4690         
4691         this.badgeEl.dom.innerHTML = str;
4692     }
4693     
4694    
4695      
4696  
4697 });
4698  
4699
4700  /*
4701  * - LGPL
4702  *
4703  * row
4704  * 
4705  */
4706
4707 /**
4708  * @class Roo.bootstrap.Row
4709  * @extends Roo.bootstrap.Component
4710  * Bootstrap Row class (contains columns...)
4711  * 
4712  * @constructor
4713  * Create a new Row
4714  * @param {Object} config The config object
4715  */
4716
4717 Roo.bootstrap.Row = function(config){
4718     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4719 };
4720
4721 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4722     
4723     getAutoCreate : function(){
4724        return {
4725             cls: 'row clearfix'
4726        };
4727     }
4728     
4729     
4730 });
4731
4732  
4733
4734  /*
4735  * - LGPL
4736  *
4737  * element
4738  * 
4739  */
4740
4741 /**
4742  * @class Roo.bootstrap.Element
4743  * @extends Roo.bootstrap.Component
4744  * Bootstrap Element class
4745  * @cfg {String} html contents of the element
4746  * @cfg {String} tag tag of the element
4747  * @cfg {String} cls class of the element
4748  * @cfg {Boolean} preventDefault (true|false) default false
4749  * @cfg {Boolean} clickable (true|false) default false
4750  * 
4751  * @constructor
4752  * Create a new Element
4753  * @param {Object} config The config object
4754  */
4755
4756 Roo.bootstrap.Element = function(config){
4757     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4758     
4759     this.addEvents({
4760         // raw events
4761         /**
4762          * @event click
4763          * When a element is chick
4764          * @param {Roo.bootstrap.Element} this
4765          * @param {Roo.EventObject} e
4766          */
4767         "click" : true
4768     });
4769 };
4770
4771 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4772     
4773     tag: 'div',
4774     cls: '',
4775     html: '',
4776     preventDefault: false, 
4777     clickable: false,
4778     
4779     getAutoCreate : function(){
4780         
4781         var cfg = {
4782             tag: this.tag,
4783             cls: this.cls,
4784             html: this.html
4785         };
4786         
4787         return cfg;
4788     },
4789     
4790     initEvents: function() 
4791     {
4792         Roo.bootstrap.Element.superclass.initEvents.call(this);
4793         
4794         if(this.clickable){
4795             this.el.on('click', this.onClick, this);
4796         }
4797         
4798     },
4799     
4800     onClick : function(e)
4801     {
4802         if(this.preventDefault){
4803             e.preventDefault();
4804         }
4805         
4806         this.fireEvent('click', this, e);
4807     },
4808     
4809     getValue : function()
4810     {
4811         return this.el.dom.innerHTML;
4812     },
4813     
4814     setValue : function(value)
4815     {
4816         this.el.dom.innerHTML = value;
4817     }
4818    
4819 });
4820
4821  
4822
4823  /*
4824  * - LGPL
4825  *
4826  * pagination
4827  * 
4828  */
4829
4830 /**
4831  * @class Roo.bootstrap.Pagination
4832  * @extends Roo.bootstrap.Component
4833  * Bootstrap Pagination class
4834  * @cfg {String} size xs | sm | md | lg
4835  * @cfg {Boolean} inverse false | true
4836  * 
4837  * @constructor
4838  * Create a new Pagination
4839  * @param {Object} config The config object
4840  */
4841
4842 Roo.bootstrap.Pagination = function(config){
4843     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4844 };
4845
4846 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4847     
4848     cls: false,
4849     size: false,
4850     inverse: false,
4851     
4852     getAutoCreate : function(){
4853         var cfg = {
4854             tag: 'ul',
4855                 cls: 'pagination'
4856         };
4857         if (this.inverse) {
4858             cfg.cls += ' inverse';
4859         }
4860         if (this.html) {
4861             cfg.html=this.html;
4862         }
4863         if (this.cls) {
4864             cfg.cls += " " + this.cls;
4865         }
4866         return cfg;
4867     }
4868    
4869 });
4870
4871  
4872
4873  /*
4874  * - LGPL
4875  *
4876  * Pagination item
4877  * 
4878  */
4879
4880
4881 /**
4882  * @class Roo.bootstrap.PaginationItem
4883  * @extends Roo.bootstrap.Component
4884  * Bootstrap PaginationItem class
4885  * @cfg {String} html text
4886  * @cfg {String} href the link
4887  * @cfg {Boolean} preventDefault (true | false) default true
4888  * @cfg {Boolean} active (true | false) default false
4889  * @cfg {Boolean} disabled default false
4890  * 
4891  * 
4892  * @constructor
4893  * Create a new PaginationItem
4894  * @param {Object} config The config object
4895  */
4896
4897
4898 Roo.bootstrap.PaginationItem = function(config){
4899     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4900     this.addEvents({
4901         // raw events
4902         /**
4903          * @event click
4904          * The raw click event for the entire grid.
4905          * @param {Roo.EventObject} e
4906          */
4907         "click" : true
4908     });
4909 };
4910
4911 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4912     
4913     href : false,
4914     html : false,
4915     preventDefault: true,
4916     active : false,
4917     cls : false,
4918     disabled: false,
4919     
4920     getAutoCreate : function(){
4921         var cfg= {
4922             tag: 'li',
4923             cn: [
4924                 {
4925                     tag : 'a',
4926                     href : this.href ? this.href : '#',
4927                     html : this.html ? this.html : ''
4928                 }
4929             ]
4930         };
4931         
4932         if(this.cls){
4933             cfg.cls = this.cls;
4934         }
4935         
4936         if(this.disabled){
4937             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4938         }
4939         
4940         if(this.active){
4941             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4942         }
4943         
4944         return cfg;
4945     },
4946     
4947     initEvents: function() {
4948         
4949         this.el.on('click', this.onClick, this);
4950         
4951     },
4952     onClick : function(e)
4953     {
4954         Roo.log('PaginationItem on click ');
4955         if(this.preventDefault){
4956             e.preventDefault();
4957         }
4958         
4959         if(this.disabled){
4960             return;
4961         }
4962         
4963         this.fireEvent('click', this, e);
4964     }
4965    
4966 });
4967
4968  
4969
4970  /*
4971  * - LGPL
4972  *
4973  * slider
4974  * 
4975  */
4976
4977
4978 /**
4979  * @class Roo.bootstrap.Slider
4980  * @extends Roo.bootstrap.Component
4981  * Bootstrap Slider class
4982  *    
4983  * @constructor
4984  * Create a new Slider
4985  * @param {Object} config The config object
4986  */
4987
4988 Roo.bootstrap.Slider = function(config){
4989     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4990 };
4991
4992 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4993     
4994     getAutoCreate : function(){
4995         
4996         var cfg = {
4997             tag: 'div',
4998             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4999             cn: [
5000                 {
5001                     tag: 'a',
5002                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5003                 }
5004             ]
5005         };
5006         
5007         return cfg;
5008     }
5009    
5010 });
5011
5012  /*
5013  * Based on:
5014  * Ext JS Library 1.1.1
5015  * Copyright(c) 2006-2007, Ext JS, LLC.
5016  *
5017  * Originally Released Under LGPL - original licence link has changed is not relivant.
5018  *
5019  * Fork - LGPL
5020  * <script type="text/javascript">
5021  */
5022  
5023
5024 /**
5025  * @class Roo.grid.ColumnModel
5026  * @extends Roo.util.Observable
5027  * This is the default implementation of a ColumnModel used by the Grid. It defines
5028  * the columns in the grid.
5029  * <br>Usage:<br>
5030  <pre><code>
5031  var colModel = new Roo.grid.ColumnModel([
5032         {header: "Ticker", width: 60, sortable: true, locked: true},
5033         {header: "Company Name", width: 150, sortable: true},
5034         {header: "Market Cap.", width: 100, sortable: true},
5035         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5036         {header: "Employees", width: 100, sortable: true, resizable: false}
5037  ]);
5038  </code></pre>
5039  * <p>
5040  
5041  * The config options listed for this class are options which may appear in each
5042  * individual column definition.
5043  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5044  * @constructor
5045  * @param {Object} config An Array of column config objects. See this class's
5046  * config objects for details.
5047 */
5048 Roo.grid.ColumnModel = function(config){
5049         /**
5050      * The config passed into the constructor
5051      */
5052     this.config = config;
5053     this.lookup = {};
5054
5055     // if no id, create one
5056     // if the column does not have a dataIndex mapping,
5057     // map it to the order it is in the config
5058     for(var i = 0, len = config.length; i < len; i++){
5059         var c = config[i];
5060         if(typeof c.dataIndex == "undefined"){
5061             c.dataIndex = i;
5062         }
5063         if(typeof c.renderer == "string"){
5064             c.renderer = Roo.util.Format[c.renderer];
5065         }
5066         if(typeof c.id == "undefined"){
5067             c.id = Roo.id();
5068         }
5069         if(c.editor && c.editor.xtype){
5070             c.editor  = Roo.factory(c.editor, Roo.grid);
5071         }
5072         if(c.editor && c.editor.isFormField){
5073             c.editor = new Roo.grid.GridEditor(c.editor);
5074         }
5075         this.lookup[c.id] = c;
5076     }
5077
5078     /**
5079      * The width of columns which have no width specified (defaults to 100)
5080      * @type Number
5081      */
5082     this.defaultWidth = 100;
5083
5084     /**
5085      * Default sortable of columns which have no sortable specified (defaults to false)
5086      * @type Boolean
5087      */
5088     this.defaultSortable = false;
5089
5090     this.addEvents({
5091         /**
5092              * @event widthchange
5093              * Fires when the width of a column changes.
5094              * @param {ColumnModel} this
5095              * @param {Number} columnIndex The column index
5096              * @param {Number} newWidth The new width
5097              */
5098             "widthchange": true,
5099         /**
5100              * @event headerchange
5101              * Fires when the text of a header changes.
5102              * @param {ColumnModel} this
5103              * @param {Number} columnIndex The column index
5104              * @param {Number} newText The new header text
5105              */
5106             "headerchange": true,
5107         /**
5108              * @event hiddenchange
5109              * Fires when a column is hidden or "unhidden".
5110              * @param {ColumnModel} this
5111              * @param {Number} columnIndex The column index
5112              * @param {Boolean} hidden true if hidden, false otherwise
5113              */
5114             "hiddenchange": true,
5115             /**
5116          * @event columnmoved
5117          * Fires when a column is moved.
5118          * @param {ColumnModel} this
5119          * @param {Number} oldIndex
5120          * @param {Number} newIndex
5121          */
5122         "columnmoved" : true,
5123         /**
5124          * @event columlockchange
5125          * Fires when a column's locked state is changed
5126          * @param {ColumnModel} this
5127          * @param {Number} colIndex
5128          * @param {Boolean} locked true if locked
5129          */
5130         "columnlockchange" : true
5131     });
5132     Roo.grid.ColumnModel.superclass.constructor.call(this);
5133 };
5134 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5135     /**
5136      * @cfg {String} header The header text to display in the Grid view.
5137      */
5138     /**
5139      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5140      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5141      * specified, the column's index is used as an index into the Record's data Array.
5142      */
5143     /**
5144      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5145      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5146      */
5147     /**
5148      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5149      * Defaults to the value of the {@link #defaultSortable} property.
5150      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5151      */
5152     /**
5153      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5154      */
5155     /**
5156      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5157      */
5158     /**
5159      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5160      */
5161     /**
5162      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5163      */
5164     /**
5165      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5166      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5167      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5168      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5169      */
5170        /**
5171      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5172      */
5173     /**
5174      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5175      */
5176     /**
5177      * @cfg {String} cursor (Optional)
5178      */
5179     /**
5180      * @cfg {String} tooltip (Optional)
5181      */
5182     /**
5183      * @cfg {Number} xs (Optional)
5184      */
5185     /**
5186      * @cfg {Number} sm (Optional)
5187      */
5188     /**
5189      * @cfg {Number} md (Optional)
5190      */
5191     /**
5192      * @cfg {Number} lg (Optional)
5193      */
5194     /**
5195      * Returns the id of the column at the specified index.
5196      * @param {Number} index The column index
5197      * @return {String} the id
5198      */
5199     getColumnId : function(index){
5200         return this.config[index].id;
5201     },
5202
5203     /**
5204      * Returns the column for a specified id.
5205      * @param {String} id The column id
5206      * @return {Object} the column
5207      */
5208     getColumnById : function(id){
5209         return this.lookup[id];
5210     },
5211
5212     
5213     /**
5214      * Returns the column for a specified dataIndex.
5215      * @param {String} dataIndex The column dataIndex
5216      * @return {Object|Boolean} the column or false if not found
5217      */
5218     getColumnByDataIndex: function(dataIndex){
5219         var index = this.findColumnIndex(dataIndex);
5220         return index > -1 ? this.config[index] : false;
5221     },
5222     
5223     /**
5224      * Returns the index for a specified column id.
5225      * @param {String} id The column id
5226      * @return {Number} the index, or -1 if not found
5227      */
5228     getIndexById : function(id){
5229         for(var i = 0, len = this.config.length; i < len; i++){
5230             if(this.config[i].id == id){
5231                 return i;
5232             }
5233         }
5234         return -1;
5235     },
5236     
5237     /**
5238      * Returns the index for a specified column dataIndex.
5239      * @param {String} dataIndex The column dataIndex
5240      * @return {Number} the index, or -1 if not found
5241      */
5242     
5243     findColumnIndex : function(dataIndex){
5244         for(var i = 0, len = this.config.length; i < len; i++){
5245             if(this.config[i].dataIndex == dataIndex){
5246                 return i;
5247             }
5248         }
5249         return -1;
5250     },
5251     
5252     
5253     moveColumn : function(oldIndex, newIndex){
5254         var c = this.config[oldIndex];
5255         this.config.splice(oldIndex, 1);
5256         this.config.splice(newIndex, 0, c);
5257         this.dataMap = null;
5258         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5259     },
5260
5261     isLocked : function(colIndex){
5262         return this.config[colIndex].locked === true;
5263     },
5264
5265     setLocked : function(colIndex, value, suppressEvent){
5266         if(this.isLocked(colIndex) == value){
5267             return;
5268         }
5269         this.config[colIndex].locked = value;
5270         if(!suppressEvent){
5271             this.fireEvent("columnlockchange", this, colIndex, value);
5272         }
5273     },
5274
5275     getTotalLockedWidth : function(){
5276         var totalWidth = 0;
5277         for(var i = 0; i < this.config.length; i++){
5278             if(this.isLocked(i) && !this.isHidden(i)){
5279                 this.totalWidth += this.getColumnWidth(i);
5280             }
5281         }
5282         return totalWidth;
5283     },
5284
5285     getLockedCount : function(){
5286         for(var i = 0, len = this.config.length; i < len; i++){
5287             if(!this.isLocked(i)){
5288                 return i;
5289             }
5290         }
5291         
5292         return this.config.length;
5293     },
5294
5295     /**
5296      * Returns the number of columns.
5297      * @return {Number}
5298      */
5299     getColumnCount : function(visibleOnly){
5300         if(visibleOnly === true){
5301             var c = 0;
5302             for(var i = 0, len = this.config.length; i < len; i++){
5303                 if(!this.isHidden(i)){
5304                     c++;
5305                 }
5306             }
5307             return c;
5308         }
5309         return this.config.length;
5310     },
5311
5312     /**
5313      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5314      * @param {Function} fn
5315      * @param {Object} scope (optional)
5316      * @return {Array} result
5317      */
5318     getColumnsBy : function(fn, scope){
5319         var r = [];
5320         for(var i = 0, len = this.config.length; i < len; i++){
5321             var c = this.config[i];
5322             if(fn.call(scope||this, c, i) === true){
5323                 r[r.length] = c;
5324             }
5325         }
5326         return r;
5327     },
5328
5329     /**
5330      * Returns true if the specified column is sortable.
5331      * @param {Number} col The column index
5332      * @return {Boolean}
5333      */
5334     isSortable : function(col){
5335         if(typeof this.config[col].sortable == "undefined"){
5336             return this.defaultSortable;
5337         }
5338         return this.config[col].sortable;
5339     },
5340
5341     /**
5342      * Returns the rendering (formatting) function defined for the column.
5343      * @param {Number} col The column index.
5344      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5345      */
5346     getRenderer : function(col){
5347         if(!this.config[col].renderer){
5348             return Roo.grid.ColumnModel.defaultRenderer;
5349         }
5350         return this.config[col].renderer;
5351     },
5352
5353     /**
5354      * Sets the rendering (formatting) function for a column.
5355      * @param {Number} col The column index
5356      * @param {Function} fn The function to use to process the cell's raw data
5357      * to return HTML markup for the grid view. The render function is called with
5358      * the following parameters:<ul>
5359      * <li>Data value.</li>
5360      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5361      * <li>css A CSS style string to apply to the table cell.</li>
5362      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5363      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5364      * <li>Row index</li>
5365      * <li>Column index</li>
5366      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5367      */
5368     setRenderer : function(col, fn){
5369         this.config[col].renderer = fn;
5370     },
5371
5372     /**
5373      * Returns the width for the specified column.
5374      * @param {Number} col The column index
5375      * @return {Number}
5376      */
5377     getColumnWidth : function(col){
5378         return this.config[col].width * 1 || this.defaultWidth;
5379     },
5380
5381     /**
5382      * Sets the width for a column.
5383      * @param {Number} col The column index
5384      * @param {Number} width The new width
5385      */
5386     setColumnWidth : function(col, width, suppressEvent){
5387         this.config[col].width = width;
5388         this.totalWidth = null;
5389         if(!suppressEvent){
5390              this.fireEvent("widthchange", this, col, width);
5391         }
5392     },
5393
5394     /**
5395      * Returns the total width of all columns.
5396      * @param {Boolean} includeHidden True to include hidden column widths
5397      * @return {Number}
5398      */
5399     getTotalWidth : function(includeHidden){
5400         if(!this.totalWidth){
5401             this.totalWidth = 0;
5402             for(var i = 0, len = this.config.length; i < len; i++){
5403                 if(includeHidden || !this.isHidden(i)){
5404                     this.totalWidth += this.getColumnWidth(i);
5405                 }
5406             }
5407         }
5408         return this.totalWidth;
5409     },
5410
5411     /**
5412      * Returns the header for the specified column.
5413      * @param {Number} col The column index
5414      * @return {String}
5415      */
5416     getColumnHeader : function(col){
5417         return this.config[col].header;
5418     },
5419
5420     /**
5421      * Sets the header for a column.
5422      * @param {Number} col The column index
5423      * @param {String} header The new header
5424      */
5425     setColumnHeader : function(col, header){
5426         this.config[col].header = header;
5427         this.fireEvent("headerchange", this, col, header);
5428     },
5429
5430     /**
5431      * Returns the tooltip for the specified column.
5432      * @param {Number} col The column index
5433      * @return {String}
5434      */
5435     getColumnTooltip : function(col){
5436             return this.config[col].tooltip;
5437     },
5438     /**
5439      * Sets the tooltip for a column.
5440      * @param {Number} col The column index
5441      * @param {String} tooltip The new tooltip
5442      */
5443     setColumnTooltip : function(col, tooltip){
5444             this.config[col].tooltip = tooltip;
5445     },
5446
5447     /**
5448      * Returns the dataIndex for the specified column.
5449      * @param {Number} col The column index
5450      * @return {Number}
5451      */
5452     getDataIndex : function(col){
5453         return this.config[col].dataIndex;
5454     },
5455
5456     /**
5457      * Sets the dataIndex for a column.
5458      * @param {Number} col The column index
5459      * @param {Number} dataIndex The new dataIndex
5460      */
5461     setDataIndex : function(col, dataIndex){
5462         this.config[col].dataIndex = dataIndex;
5463     },
5464
5465     
5466     
5467     /**
5468      * Returns true if the cell is editable.
5469      * @param {Number} colIndex The column index
5470      * @param {Number} rowIndex The row index - this is nto actually used..?
5471      * @return {Boolean}
5472      */
5473     isCellEditable : function(colIndex, rowIndex){
5474         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5475     },
5476
5477     /**
5478      * Returns the editor defined for the cell/column.
5479      * return false or null to disable editing.
5480      * @param {Number} colIndex The column index
5481      * @param {Number} rowIndex The row index
5482      * @return {Object}
5483      */
5484     getCellEditor : function(colIndex, rowIndex){
5485         return this.config[colIndex].editor;
5486     },
5487
5488     /**
5489      * Sets if a column is editable.
5490      * @param {Number} col The column index
5491      * @param {Boolean} editable True if the column is editable
5492      */
5493     setEditable : function(col, editable){
5494         this.config[col].editable = editable;
5495     },
5496
5497
5498     /**
5499      * Returns true if the column is hidden.
5500      * @param {Number} colIndex The column index
5501      * @return {Boolean}
5502      */
5503     isHidden : function(colIndex){
5504         return this.config[colIndex].hidden;
5505     },
5506
5507
5508     /**
5509      * Returns true if the column width cannot be changed
5510      */
5511     isFixed : function(colIndex){
5512         return this.config[colIndex].fixed;
5513     },
5514
5515     /**
5516      * Returns true if the column can be resized
5517      * @return {Boolean}
5518      */
5519     isResizable : function(colIndex){
5520         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5521     },
5522     /**
5523      * Sets if a column is hidden.
5524      * @param {Number} colIndex The column index
5525      * @param {Boolean} hidden True if the column is hidden
5526      */
5527     setHidden : function(colIndex, hidden){
5528         this.config[colIndex].hidden = hidden;
5529         this.totalWidth = null;
5530         this.fireEvent("hiddenchange", this, colIndex, hidden);
5531     },
5532
5533     /**
5534      * Sets the editor for a column.
5535      * @param {Number} col The column index
5536      * @param {Object} editor The editor object
5537      */
5538     setEditor : function(col, editor){
5539         this.config[col].editor = editor;
5540     }
5541 });
5542
5543 Roo.grid.ColumnModel.defaultRenderer = function(value){
5544         if(typeof value == "string" && value.length < 1){
5545             return "&#160;";
5546         }
5547         return value;
5548 };
5549
5550 // Alias for backwards compatibility
5551 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5552 /*
5553  * Based on:
5554  * Ext JS Library 1.1.1
5555  * Copyright(c) 2006-2007, Ext JS, LLC.
5556  *
5557  * Originally Released Under LGPL - original licence link has changed is not relivant.
5558  *
5559  * Fork - LGPL
5560  * <script type="text/javascript">
5561  */
5562  
5563 /**
5564  * @class Roo.LoadMask
5565  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5566  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5567  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5568  * element's UpdateManager load indicator and will be destroyed after the initial load.
5569  * @constructor
5570  * Create a new LoadMask
5571  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5572  * @param {Object} config The config object
5573  */
5574 Roo.LoadMask = function(el, config){
5575     this.el = Roo.get(el);
5576     Roo.apply(this, config);
5577     if(this.store){
5578         this.store.on('beforeload', this.onBeforeLoad, this);
5579         this.store.on('load', this.onLoad, this);
5580         this.store.on('loadexception', this.onLoadException, this);
5581         this.removeMask = false;
5582     }else{
5583         var um = this.el.getUpdateManager();
5584         um.showLoadIndicator = false; // disable the default indicator
5585         um.on('beforeupdate', this.onBeforeLoad, this);
5586         um.on('update', this.onLoad, this);
5587         um.on('failure', this.onLoad, this);
5588         this.removeMask = true;
5589     }
5590 };
5591
5592 Roo.LoadMask.prototype = {
5593     /**
5594      * @cfg {Boolean} removeMask
5595      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5596      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5597      */
5598     /**
5599      * @cfg {String} msg
5600      * The text to display in a centered loading message box (defaults to 'Loading...')
5601      */
5602     msg : 'Loading...',
5603     /**
5604      * @cfg {String} msgCls
5605      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5606      */
5607     msgCls : 'x-mask-loading',
5608
5609     /**
5610      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5611      * @type Boolean
5612      */
5613     disabled: false,
5614
5615     /**
5616      * Disables the mask to prevent it from being displayed
5617      */
5618     disable : function(){
5619        this.disabled = true;
5620     },
5621
5622     /**
5623      * Enables the mask so that it can be displayed
5624      */
5625     enable : function(){
5626         this.disabled = false;
5627     },
5628     
5629     onLoadException : function()
5630     {
5631         Roo.log(arguments);
5632         
5633         if (typeof(arguments[3]) != 'undefined') {
5634             Roo.MessageBox.alert("Error loading",arguments[3]);
5635         } 
5636         /*
5637         try {
5638             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5639                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5640             }   
5641         } catch(e) {
5642             
5643         }
5644         */
5645     
5646         
5647         
5648         this.el.unmask(this.removeMask);
5649     },
5650     // private
5651     onLoad : function()
5652     {
5653         this.el.unmask(this.removeMask);
5654     },
5655
5656     // private
5657     onBeforeLoad : function(){
5658         if(!this.disabled){
5659             this.el.mask(this.msg, this.msgCls);
5660         }
5661     },
5662
5663     // private
5664     destroy : function(){
5665         if(this.store){
5666             this.store.un('beforeload', this.onBeforeLoad, this);
5667             this.store.un('load', this.onLoad, this);
5668             this.store.un('loadexception', this.onLoadException, this);
5669         }else{
5670             var um = this.el.getUpdateManager();
5671             um.un('beforeupdate', this.onBeforeLoad, this);
5672             um.un('update', this.onLoad, this);
5673             um.un('failure', this.onLoad, this);
5674         }
5675     }
5676 };/*
5677  * - LGPL
5678  *
5679  * table
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.Table
5685  * @extends Roo.bootstrap.Component
5686  * Bootstrap Table class
5687  * @cfg {String} cls table class
5688  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5689  * @cfg {String} bgcolor Specifies the background color for a table
5690  * @cfg {Number} border Specifies whether the table cells should have borders or not
5691  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5692  * @cfg {Number} cellspacing Specifies the space between cells
5693  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5694  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5695  * @cfg {String} sortable Specifies that the table should be sortable
5696  * @cfg {String} summary Specifies a summary of the content of a table
5697  * @cfg {Number} width Specifies the width of a table
5698  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5699  * 
5700  * @cfg {boolean} striped Should the rows be alternative striped
5701  * @cfg {boolean} bordered Add borders to the table
5702  * @cfg {boolean} hover Add hover highlighting
5703  * @cfg {boolean} condensed Format condensed
5704  * @cfg {boolean} responsive Format condensed
5705  * @cfg {Boolean} loadMask (true|false) default false
5706  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5707  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5708  * @cfg {Boolean} rowSelection (true|false) default false
5709  * @cfg {Boolean} cellSelection (true|false) default false
5710  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5711  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5712  
5713  * 
5714  * @constructor
5715  * Create a new Table
5716  * @param {Object} config The config object
5717  */
5718
5719 Roo.bootstrap.Table = function(config){
5720     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5721     
5722   
5723     
5724     // BC...
5725     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5726     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5727     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5728     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5729     
5730     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5731     if (this.sm) {
5732         this.sm.grid = this;
5733         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5734         this.sm = this.selModel;
5735         this.sm.xmodule = this.xmodule || false;
5736     }
5737     
5738     if (this.cm && typeof(this.cm.config) == 'undefined') {
5739         this.colModel = new Roo.grid.ColumnModel(this.cm);
5740         this.cm = this.colModel;
5741         this.cm.xmodule = this.xmodule || false;
5742     }
5743     if (this.store) {
5744         this.store= Roo.factory(this.store, Roo.data);
5745         this.ds = this.store;
5746         this.ds.xmodule = this.xmodule || false;
5747          
5748     }
5749     if (this.footer && this.store) {
5750         this.footer.dataSource = this.ds;
5751         this.footer = Roo.factory(this.footer);
5752     }
5753     
5754     /** @private */
5755     this.addEvents({
5756         /**
5757          * @event cellclick
5758          * Fires when a cell is clicked
5759          * @param {Roo.bootstrap.Table} this
5760          * @param {Roo.Element} el
5761          * @param {Number} rowIndex
5762          * @param {Number} columnIndex
5763          * @param {Roo.EventObject} e
5764          */
5765         "cellclick" : true,
5766         /**
5767          * @event celldblclick
5768          * Fires when a cell is double clicked
5769          * @param {Roo.bootstrap.Table} this
5770          * @param {Roo.Element} el
5771          * @param {Number} rowIndex
5772          * @param {Number} columnIndex
5773          * @param {Roo.EventObject} e
5774          */
5775         "celldblclick" : true,
5776         /**
5777          * @event rowclick
5778          * Fires when a row is clicked
5779          * @param {Roo.bootstrap.Table} this
5780          * @param {Roo.Element} el
5781          * @param {Number} rowIndex
5782          * @param {Roo.EventObject} e
5783          */
5784         "rowclick" : true,
5785         /**
5786          * @event rowdblclick
5787          * Fires when a row is double clicked
5788          * @param {Roo.bootstrap.Table} this
5789          * @param {Roo.Element} el
5790          * @param {Number} rowIndex
5791          * @param {Roo.EventObject} e
5792          */
5793         "rowdblclick" : true,
5794         /**
5795          * @event mouseover
5796          * Fires when a mouseover occur
5797          * @param {Roo.bootstrap.Table} this
5798          * @param {Roo.Element} el
5799          * @param {Number} rowIndex
5800          * @param {Number} columnIndex
5801          * @param {Roo.EventObject} e
5802          */
5803         "mouseover" : true,
5804         /**
5805          * @event mouseout
5806          * Fires when a mouseout occur
5807          * @param {Roo.bootstrap.Table} this
5808          * @param {Roo.Element} el
5809          * @param {Number} rowIndex
5810          * @param {Number} columnIndex
5811          * @param {Roo.EventObject} e
5812          */
5813         "mouseout" : true,
5814         /**
5815          * @event rowclass
5816          * Fires when a row is rendered, so you can change add a style to it.
5817          * @param {Roo.bootstrap.Table} this
5818          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5819          */
5820         'rowclass' : true,
5821           /**
5822          * @event rowsrendered
5823          * Fires when all the  rows have been rendered
5824          * @param {Roo.bootstrap.Table} this
5825          */
5826         'rowsrendered' : true,
5827         /**
5828          * @event contextmenu
5829          * The raw contextmenu event for the entire grid.
5830          * @param {Roo.EventObject} e
5831          */
5832         "contextmenu" : true,
5833         /**
5834          * @event rowcontextmenu
5835          * Fires when a row is right clicked
5836          * @param {Roo.bootstrap.Table} this
5837          * @param {Number} rowIndex
5838          * @param {Roo.EventObject} e
5839          */
5840         "rowcontextmenu" : true,
5841         /**
5842          * @event cellcontextmenu
5843          * Fires when a cell is right clicked
5844          * @param {Roo.bootstrap.Table} this
5845          * @param {Number} rowIndex
5846          * @param {Number} cellIndex
5847          * @param {Roo.EventObject} e
5848          */
5849          "cellcontextmenu" : true,
5850          /**
5851          * @event headercontextmenu
5852          * Fires when a header is right clicked
5853          * @param {Roo.bootstrap.Table} this
5854          * @param {Number} columnIndex
5855          * @param {Roo.EventObject} e
5856          */
5857         "headercontextmenu" : true
5858     });
5859 };
5860
5861 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5862     
5863     cls: false,
5864     align: false,
5865     bgcolor: false,
5866     border: false,
5867     cellpadding: false,
5868     cellspacing: false,
5869     frame: false,
5870     rules: false,
5871     sortable: false,
5872     summary: false,
5873     width: false,
5874     striped : false,
5875     scrollBody : false,
5876     bordered: false,
5877     hover:  false,
5878     condensed : false,
5879     responsive : false,
5880     sm : false,
5881     cm : false,
5882     store : false,
5883     loadMask : false,
5884     footerShow : true,
5885     headerShow : true,
5886   
5887     rowSelection : false,
5888     cellSelection : false,
5889     layout : false,
5890     
5891     // Roo.Element - the tbody
5892     mainBody: false,
5893     // Roo.Element - thead element
5894     mainHead: false,
5895     
5896     container: false, // used by gridpanel...
5897     
5898     getAutoCreate : function()
5899     {
5900         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5901         
5902         cfg = {
5903             tag: 'table',
5904             cls : 'table',
5905             cn : []
5906         };
5907         if (this.scrollBody) {
5908             cfg.cls += ' table-body-fixed';
5909         }    
5910         if (this.striped) {
5911             cfg.cls += ' table-striped';
5912         }
5913         
5914         if (this.hover) {
5915             cfg.cls += ' table-hover';
5916         }
5917         if (this.bordered) {
5918             cfg.cls += ' table-bordered';
5919         }
5920         if (this.condensed) {
5921             cfg.cls += ' table-condensed';
5922         }
5923         if (this.responsive) {
5924             cfg.cls += ' table-responsive';
5925         }
5926         
5927         if (this.cls) {
5928             cfg.cls+=  ' ' +this.cls;
5929         }
5930         
5931         // this lot should be simplifed...
5932         
5933         if (this.align) {
5934             cfg.align=this.align;
5935         }
5936         if (this.bgcolor) {
5937             cfg.bgcolor=this.bgcolor;
5938         }
5939         if (this.border) {
5940             cfg.border=this.border;
5941         }
5942         if (this.cellpadding) {
5943             cfg.cellpadding=this.cellpadding;
5944         }
5945         if (this.cellspacing) {
5946             cfg.cellspacing=this.cellspacing;
5947         }
5948         if (this.frame) {
5949             cfg.frame=this.frame;
5950         }
5951         if (this.rules) {
5952             cfg.rules=this.rules;
5953         }
5954         if (this.sortable) {
5955             cfg.sortable=this.sortable;
5956         }
5957         if (this.summary) {
5958             cfg.summary=this.summary;
5959         }
5960         if (this.width) {
5961             cfg.width=this.width;
5962         }
5963         if (this.layout) {
5964             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5965         }
5966         
5967         if(this.store || this.cm){
5968             if(this.headerShow){
5969                 cfg.cn.push(this.renderHeader());
5970             }
5971             
5972             cfg.cn.push(this.renderBody());
5973             
5974             if(this.footerShow){
5975                 cfg.cn.push(this.renderFooter());
5976             }
5977             // where does this come from?
5978             //cfg.cls+=  ' TableGrid';
5979         }
5980         
5981         return { cn : [ cfg ] };
5982     },
5983     
5984     initEvents : function()
5985     {   
5986         if(!this.store || !this.cm){
5987             return;
5988         }
5989         if (this.selModel) {
5990             this.selModel.initEvents();
5991         }
5992         
5993         
5994         //Roo.log('initEvents with ds!!!!');
5995         
5996         this.mainBody = this.el.select('tbody', true).first();
5997         this.mainHead = this.el.select('thead', true).first();
5998         
5999         
6000         
6001         
6002         var _this = this;
6003         
6004         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6005             e.on('click', _this.sort, _this);
6006         });
6007         
6008         this.el.on("click", this.onClick, this);
6009         this.el.on("dblclick", this.onDblClick, this);
6010         
6011         // why is this done????? = it breaks dialogs??
6012         //this.parent().el.setStyle('position', 'relative');
6013         
6014         
6015         if (this.footer) {
6016             this.footer.parentId = this.id;
6017             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6018         } 
6019         
6020         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6021         
6022         this.store.on('load', this.onLoad, this);
6023         this.store.on('beforeload', this.onBeforeLoad, this);
6024         this.store.on('update', this.onUpdate, this);
6025         this.store.on('add', this.onAdd, this);
6026         this.store.on("clear", this.clear, this);
6027         
6028         this.el.on("contextmenu", this.onContextMenu, this);
6029         
6030         this.mainBody.on('scroll', this.onBodyScroll, this);
6031         
6032         
6033     },
6034     
6035     onContextMenu : function(e, t)
6036     {
6037         this.processEvent("contextmenu", e);
6038     },
6039     
6040     processEvent : function(name, e)
6041     {
6042         if (name != 'touchstart' ) {
6043             this.fireEvent(name, e);    
6044         }
6045         
6046         var t = e.getTarget();
6047         
6048         var cell = Roo.get(t);
6049         
6050         if(!cell){
6051             return;
6052         }
6053         
6054         if(cell.findParent('tfoot', false, true)){
6055             return;
6056         }
6057         
6058         if(cell.findParent('thead', false, true)){
6059             
6060             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6061                 cell = Roo.get(t).findParent('th', false, true);
6062                 if (!cell) {
6063                     Roo.log("failed to find th in thead?");
6064                     Roo.log(e.getTarget());
6065                     return;
6066                 }
6067             }
6068             
6069             var cellIndex = cell.dom.cellIndex;
6070             
6071             var ename = name == 'touchstart' ? 'click' : name;
6072             this.fireEvent("header" + ename, this, cellIndex, e);
6073             
6074             return;
6075         }
6076         
6077         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6078             cell = Roo.get(t).findParent('td', false, true);
6079             if (!cell) {
6080                 Roo.log("failed to find th in tbody?");
6081                 Roo.log(e.getTarget());
6082                 return;
6083             }
6084         }
6085         
6086         var row = cell.findParent('tr', false, true);
6087         var cellIndex = cell.dom.cellIndex;
6088         var rowIndex = row.dom.rowIndex - 1;
6089         
6090         if(row !== false){
6091             
6092             this.fireEvent("row" + name, this, rowIndex, e);
6093             
6094             if(cell !== false){
6095             
6096                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6097             }
6098         }
6099         
6100     },
6101     
6102     onMouseover : function(e, el)
6103     {
6104         var cell = Roo.get(el);
6105         
6106         if(!cell){
6107             return;
6108         }
6109         
6110         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6111             cell = cell.findParent('td', false, true);
6112         }
6113         
6114         var row = cell.findParent('tr', false, true);
6115         var cellIndex = cell.dom.cellIndex;
6116         var rowIndex = row.dom.rowIndex - 1; // start from 0
6117         
6118         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6119         
6120     },
6121     
6122     onMouseout : function(e, el)
6123     {
6124         var cell = Roo.get(el);
6125         
6126         if(!cell){
6127             return;
6128         }
6129         
6130         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6131             cell = cell.findParent('td', false, true);
6132         }
6133         
6134         var row = cell.findParent('tr', false, true);
6135         var cellIndex = cell.dom.cellIndex;
6136         var rowIndex = row.dom.rowIndex - 1; // start from 0
6137         
6138         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6139         
6140     },
6141     
6142     onClick : function(e, el)
6143     {
6144         var cell = Roo.get(el);
6145         
6146         if(!cell || (!this.cellSelection && !this.rowSelection)){
6147             return;
6148         }
6149         
6150         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6151             cell = cell.findParent('td', false, true);
6152         }
6153         
6154         if(!cell || typeof(cell) == 'undefined'){
6155             return;
6156         }
6157         
6158         var row = cell.findParent('tr', false, true);
6159         
6160         if(!row || typeof(row) == 'undefined'){
6161             return;
6162         }
6163         
6164         var cellIndex = cell.dom.cellIndex;
6165         var rowIndex = this.getRowIndex(row);
6166         
6167         // why??? - should these not be based on SelectionModel?
6168         if(this.cellSelection){
6169             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6170         }
6171         
6172         if(this.rowSelection){
6173             this.fireEvent('rowclick', this, row, rowIndex, e);
6174         }
6175         
6176         
6177     },
6178         
6179     onDblClick : function(e,el)
6180     {
6181         var cell = Roo.get(el);
6182         
6183         if(!cell || (!this.cellSelection && !this.rowSelection)){
6184             return;
6185         }
6186         
6187         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6188             cell = cell.findParent('td', false, true);
6189         }
6190         
6191         if(!cell || typeof(cell) == 'undefined'){
6192             return;
6193         }
6194         
6195         var row = cell.findParent('tr', false, true);
6196         
6197         if(!row || typeof(row) == 'undefined'){
6198             return;
6199         }
6200         
6201         var cellIndex = cell.dom.cellIndex;
6202         var rowIndex = this.getRowIndex(row);
6203         
6204         if(this.cellSelection){
6205             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6206         }
6207         
6208         if(this.rowSelection){
6209             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6210         }
6211     },
6212     
6213     sort : function(e,el)
6214     {
6215         var col = Roo.get(el);
6216         
6217         if(!col.hasClass('sortable')){
6218             return;
6219         }
6220         
6221         var sort = col.attr('sort');
6222         var dir = 'ASC';
6223         
6224         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6225             dir = 'DESC';
6226         }
6227         
6228         this.store.sortInfo = {field : sort, direction : dir};
6229         
6230         if (this.footer) {
6231             Roo.log("calling footer first");
6232             this.footer.onClick('first');
6233         } else {
6234         
6235             this.store.load({ params : { start : 0 } });
6236         }
6237     },
6238     
6239     renderHeader : function()
6240     {
6241         var header = {
6242             tag: 'thead',
6243             cn : []
6244         };
6245         
6246         var cm = this.cm;
6247         this.totalWidth = 0;
6248         
6249         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6250             
6251             var config = cm.config[i];
6252             
6253             var c = {
6254                 tag: 'th',
6255                 style : '',
6256                 html: cm.getColumnHeader(i)
6257             };
6258             
6259             var hh = '';
6260             
6261             if(typeof(config.sortable) != 'undefined' && config.sortable){
6262                 c.cls = 'sortable';
6263                 c.html = '<i class="glyphicon"></i>' + c.html;
6264             }
6265             
6266             if(typeof(config.lgHeader) != 'undefined'){
6267                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6268             }
6269             
6270             if(typeof(config.mdHeader) != 'undefined'){
6271                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6272             }
6273             
6274             if(typeof(config.smHeader) != 'undefined'){
6275                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6276             }
6277             
6278             if(typeof(config.xsHeader) != 'undefined'){
6279                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6280             }
6281             
6282             if(hh.length){
6283                 c.html = hh;
6284             }
6285             
6286             if(typeof(config.tooltip) != 'undefined'){
6287                 c.tooltip = config.tooltip;
6288             }
6289             
6290             if(typeof(config.colspan) != 'undefined'){
6291                 c.colspan = config.colspan;
6292             }
6293             
6294             if(typeof(config.hidden) != 'undefined' && config.hidden){
6295                 c.style += ' display:none;';
6296             }
6297             
6298             if(typeof(config.dataIndex) != 'undefined'){
6299                 c.sort = config.dataIndex;
6300             }
6301             
6302            
6303             
6304             if(typeof(config.align) != 'undefined' && config.align.length){
6305                 c.style += ' text-align:' + config.align + ';';
6306             }
6307             
6308             if(typeof(config.width) != 'undefined'){
6309                 c.style += ' width:' + config.width + 'px;';
6310                 this.totalWidth += config.width;
6311             } else {
6312                 this.totalWidth += 100; // assume minimum of 100 per column?
6313             }
6314             
6315             if(typeof(config.cls) != 'undefined'){
6316                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6317             }
6318             
6319             ['xs','sm','md','lg'].map(function(size){
6320                 
6321                 if(typeof(config[size]) == 'undefined'){
6322                     return;
6323                 }
6324                 
6325                 if (!config[size]) { // 0 = hidden
6326                     c.cls += ' hidden-' + size;
6327                     return;
6328                 }
6329                 
6330                 c.cls += ' col-' + size + '-' + config[size];
6331
6332             });
6333             
6334             header.cn.push(c)
6335         }
6336         
6337         return header;
6338     },
6339     
6340     renderBody : function()
6341     {
6342         var body = {
6343             tag: 'tbody',
6344             cn : [
6345                 {
6346                     tag: 'tr',
6347                     cn : [
6348                         {
6349                             tag : 'td',
6350                             colspan :  this.cm.getColumnCount()
6351                         }
6352                     ]
6353                 }
6354             ]
6355         };
6356         
6357         return body;
6358     },
6359     
6360     renderFooter : function()
6361     {
6362         var footer = {
6363             tag: 'tfoot',
6364             cn : [
6365                 {
6366                     tag: 'tr',
6367                     cn : [
6368                         {
6369                             tag : 'td',
6370                             colspan :  this.cm.getColumnCount()
6371                         }
6372                     ]
6373                 }
6374             ]
6375         };
6376         
6377         return footer;
6378     },
6379     
6380     
6381     
6382     onLoad : function()
6383     {
6384 //        Roo.log('ds onload');
6385         this.clear();
6386         
6387         var _this = this;
6388         var cm = this.cm;
6389         var ds = this.store;
6390         
6391         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6392             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6393             if (_this.store.sortInfo) {
6394                     
6395                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6396                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6397                 }
6398                 
6399                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6400                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6401                 }
6402             }
6403         });
6404         
6405         var tbody =  this.mainBody;
6406               
6407         if(ds.getCount() > 0){
6408             ds.data.each(function(d,rowIndex){
6409                 var row =  this.renderRow(cm, ds, rowIndex);
6410                 
6411                 tbody.createChild(row);
6412                 
6413                 var _this = this;
6414                 
6415                 if(row.cellObjects.length){
6416                     Roo.each(row.cellObjects, function(r){
6417                         _this.renderCellObject(r);
6418                     })
6419                 }
6420                 
6421             }, this);
6422         }
6423         
6424         Roo.each(this.el.select('tbody td', true).elements, function(e){
6425             e.on('mouseover', _this.onMouseover, _this);
6426         });
6427         
6428         Roo.each(this.el.select('tbody td', true).elements, function(e){
6429             e.on('mouseout', _this.onMouseout, _this);
6430         });
6431         this.fireEvent('rowsrendered', this);
6432         //if(this.loadMask){
6433         //    this.maskEl.hide();
6434         //}
6435         
6436         this.autoSize();
6437     },
6438     
6439     
6440     onUpdate : function(ds,record)
6441     {
6442         this.refreshRow(record);
6443         this.autoSize();
6444     },
6445     
6446     onRemove : function(ds, record, index, isUpdate){
6447         if(isUpdate !== true){
6448             this.fireEvent("beforerowremoved", this, index, record);
6449         }
6450         var bt = this.mainBody.dom;
6451         
6452         var rows = this.el.select('tbody > tr', true).elements;
6453         
6454         if(typeof(rows[index]) != 'undefined'){
6455             bt.removeChild(rows[index].dom);
6456         }
6457         
6458 //        if(bt.rows[index]){
6459 //            bt.removeChild(bt.rows[index]);
6460 //        }
6461         
6462         if(isUpdate !== true){
6463             //this.stripeRows(index);
6464             //this.syncRowHeights(index, index);
6465             //this.layout();
6466             this.fireEvent("rowremoved", this, index, record);
6467         }
6468     },
6469     
6470     onAdd : function(ds, records, rowIndex)
6471     {
6472         //Roo.log('on Add called');
6473         // - note this does not handle multiple adding very well..
6474         var bt = this.mainBody.dom;
6475         for (var i =0 ; i < records.length;i++) {
6476             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6477             //Roo.log(records[i]);
6478             //Roo.log(this.store.getAt(rowIndex+i));
6479             this.insertRow(this.store, rowIndex + i, false);
6480             return;
6481         }
6482         
6483     },
6484     
6485     
6486     refreshRow : function(record){
6487         var ds = this.store, index;
6488         if(typeof record == 'number'){
6489             index = record;
6490             record = ds.getAt(index);
6491         }else{
6492             index = ds.indexOf(record);
6493         }
6494         this.insertRow(ds, index, true);
6495         this.autoSize();
6496         this.onRemove(ds, record, index+1, true);
6497         this.autoSize();
6498         //this.syncRowHeights(index, index);
6499         //this.layout();
6500         this.fireEvent("rowupdated", this, index, record);
6501     },
6502     
6503     insertRow : function(dm, rowIndex, isUpdate){
6504         
6505         if(!isUpdate){
6506             this.fireEvent("beforerowsinserted", this, rowIndex);
6507         }
6508             //var s = this.getScrollState();
6509         var row = this.renderRow(this.cm, this.store, rowIndex);
6510         // insert before rowIndex..
6511         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6512         
6513         var _this = this;
6514                 
6515         if(row.cellObjects.length){
6516             Roo.each(row.cellObjects, function(r){
6517                 _this.renderCellObject(r);
6518             })
6519         }
6520             
6521         if(!isUpdate){
6522             this.fireEvent("rowsinserted", this, rowIndex);
6523             //this.syncRowHeights(firstRow, lastRow);
6524             //this.stripeRows(firstRow);
6525             //this.layout();
6526         }
6527         
6528     },
6529     
6530     
6531     getRowDom : function(rowIndex)
6532     {
6533         var rows = this.el.select('tbody > tr', true).elements;
6534         
6535         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6536         
6537     },
6538     // returns the object tree for a tr..
6539   
6540     
6541     renderRow : function(cm, ds, rowIndex) 
6542     {
6543         
6544         var d = ds.getAt(rowIndex);
6545         
6546         var row = {
6547             tag : 'tr',
6548             cn : []
6549         };
6550             
6551         var cellObjects = [];
6552         
6553         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6554             var config = cm.config[i];
6555             
6556             var renderer = cm.getRenderer(i);
6557             var value = '';
6558             var id = false;
6559             
6560             if(typeof(renderer) !== 'undefined'){
6561                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6562             }
6563             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6564             // and are rendered into the cells after the row is rendered - using the id for the element.
6565             
6566             if(typeof(value) === 'object'){
6567                 id = Roo.id();
6568                 cellObjects.push({
6569                     container : id,
6570                     cfg : value 
6571                 })
6572             }
6573             
6574             var rowcfg = {
6575                 record: d,
6576                 rowIndex : rowIndex,
6577                 colIndex : i,
6578                 rowClass : ''
6579             };
6580
6581             this.fireEvent('rowclass', this, rowcfg);
6582             
6583             var td = {
6584                 tag: 'td',
6585                 cls : rowcfg.rowClass,
6586                 style: '',
6587                 html: (typeof(value) === 'object') ? '' : value
6588             };
6589             
6590             if (id) {
6591                 td.id = id;
6592             }
6593             
6594             if(typeof(config.colspan) != 'undefined'){
6595                 td.colspan = config.colspan;
6596             }
6597             
6598             if(typeof(config.hidden) != 'undefined' && config.hidden){
6599                 td.style += ' display:none;';
6600             }
6601             
6602             if(typeof(config.align) != 'undefined' && config.align.length){
6603                 td.style += ' text-align:' + config.align + ';';
6604             }
6605             
6606             if(typeof(config.width) != 'undefined'){
6607                 td.style += ' width:' +  config.width + 'px;';
6608             }
6609             
6610             if(typeof(config.cursor) != 'undefined'){
6611                 td.style += ' cursor:' +  config.cursor + ';';
6612             }
6613             
6614             if(typeof(config.cls) != 'undefined'){
6615                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6616             }
6617             
6618             ['xs','sm','md','lg'].map(function(size){
6619                 
6620                 if(typeof(config[size]) == 'undefined'){
6621                     return;
6622                 }
6623                 
6624                 if (!config[size]) { // 0 = hidden
6625                     td.cls += ' hidden-' + size;
6626                     return;
6627                 }
6628                 
6629                 td.cls += ' col-' + size + '-' + config[size];
6630
6631             });
6632              
6633             row.cn.push(td);
6634            
6635         }
6636         
6637         row.cellObjects = cellObjects;
6638         
6639         return row;
6640           
6641     },
6642     
6643     
6644     
6645     onBeforeLoad : function()
6646     {
6647         //Roo.log('ds onBeforeLoad');
6648         
6649         //this.clear();
6650         
6651         //if(this.loadMask){
6652         //    this.maskEl.show();
6653         //}
6654     },
6655      /**
6656      * Remove all rows
6657      */
6658     clear : function()
6659     {
6660         this.el.select('tbody', true).first().dom.innerHTML = '';
6661     },
6662     /**
6663      * Show or hide a row.
6664      * @param {Number} rowIndex to show or hide
6665      * @param {Boolean} state hide
6666      */
6667     setRowVisibility : function(rowIndex, state)
6668     {
6669         var bt = this.mainBody.dom;
6670         
6671         var rows = this.el.select('tbody > tr', true).elements;
6672         
6673         if(typeof(rows[rowIndex]) == 'undefined'){
6674             return;
6675         }
6676         rows[rowIndex].dom.style.display = state ? '' : 'none';
6677     },
6678     
6679     
6680     getSelectionModel : function(){
6681         if(!this.selModel){
6682             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6683         }
6684         return this.selModel;
6685     },
6686     /*
6687      * Render the Roo.bootstrap object from renderder
6688      */
6689     renderCellObject : function(r)
6690     {
6691         var _this = this;
6692         
6693         var t = r.cfg.render(r.container);
6694         
6695         if(r.cfg.cn){
6696             Roo.each(r.cfg.cn, function(c){
6697                 var child = {
6698                     container: t.getChildContainer(),
6699                     cfg: c
6700                 };
6701                 _this.renderCellObject(child);
6702             })
6703         }
6704     },
6705     
6706     getRowIndex : function(row)
6707     {
6708         var rowIndex = -1;
6709         
6710         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6711             if(el != row){
6712                 return;
6713             }
6714             
6715             rowIndex = index;
6716         });
6717         
6718         return rowIndex;
6719     },
6720      /**
6721      * Returns the grid's underlying element = used by panel.Grid
6722      * @return {Element} The element
6723      */
6724     getGridEl : function(){
6725         return this.el;
6726     },
6727      /**
6728      * Forces a resize - used by panel.Grid
6729      * @return {Element} The element
6730      */
6731     autoSize : function()
6732     {
6733         //var ctr = Roo.get(this.container.dom.parentElement);
6734         var ctr = Roo.get(this.el.dom);
6735         
6736         var thd = this.getGridEl().select('thead',true).first();
6737         var tbd = this.getGridEl().select('tbody', true).first();
6738         var tfd = this.getGridEl().select('tfoot', true).first();
6739         
6740         var cw = ctr.getWidth();
6741         
6742         if (tbd) {
6743             
6744             tbd.setSize(ctr.getWidth(),
6745                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6746             );
6747             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6748             cw -= barsize;
6749         }
6750         cw = Math.max(cw, this.totalWidth);
6751         this.getGridEl().select('tr',true).setWidth(cw);
6752         // resize 'expandable coloumn?
6753         
6754         return; // we doe not have a view in this design..
6755         
6756     },
6757     onBodyScroll: function()
6758     {
6759         
6760         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6761         this.mainHead.setStyle({
6762                     'position' : 'relative',
6763                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6764         });
6765         
6766         
6767     }
6768 });
6769
6770  
6771
6772  /*
6773  * - LGPL
6774  *
6775  * table cell
6776  * 
6777  */
6778
6779 /**
6780  * @class Roo.bootstrap.TableCell
6781  * @extends Roo.bootstrap.Component
6782  * Bootstrap TableCell class
6783  * @cfg {String} html cell contain text
6784  * @cfg {String} cls cell class
6785  * @cfg {String} tag cell tag (td|th) default td
6786  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6787  * @cfg {String} align Aligns the content in a cell
6788  * @cfg {String} axis Categorizes cells
6789  * @cfg {String} bgcolor Specifies the background color of a cell
6790  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6791  * @cfg {Number} colspan Specifies the number of columns a cell should span
6792  * @cfg {String} headers Specifies one or more header cells a cell is related to
6793  * @cfg {Number} height Sets the height of a cell
6794  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6795  * @cfg {Number} rowspan Sets the number of rows a cell should span
6796  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6797  * @cfg {String} valign Vertical aligns the content in a cell
6798  * @cfg {Number} width Specifies the width of a cell
6799  * 
6800  * @constructor
6801  * Create a new TableCell
6802  * @param {Object} config The config object
6803  */
6804
6805 Roo.bootstrap.TableCell = function(config){
6806     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6807 };
6808
6809 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6810     
6811     html: false,
6812     cls: false,
6813     tag: false,
6814     abbr: false,
6815     align: false,
6816     axis: false,
6817     bgcolor: false,
6818     charoff: false,
6819     colspan: false,
6820     headers: false,
6821     height: false,
6822     nowrap: false,
6823     rowspan: false,
6824     scope: false,
6825     valign: false,
6826     width: false,
6827     
6828     
6829     getAutoCreate : function(){
6830         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6831         
6832         cfg = {
6833             tag: 'td'
6834         };
6835         
6836         if(this.tag){
6837             cfg.tag = this.tag;
6838         }
6839         
6840         if (this.html) {
6841             cfg.html=this.html
6842         }
6843         if (this.cls) {
6844             cfg.cls=this.cls
6845         }
6846         if (this.abbr) {
6847             cfg.abbr=this.abbr
6848         }
6849         if (this.align) {
6850             cfg.align=this.align
6851         }
6852         if (this.axis) {
6853             cfg.axis=this.axis
6854         }
6855         if (this.bgcolor) {
6856             cfg.bgcolor=this.bgcolor
6857         }
6858         if (this.charoff) {
6859             cfg.charoff=this.charoff
6860         }
6861         if (this.colspan) {
6862             cfg.colspan=this.colspan
6863         }
6864         if (this.headers) {
6865             cfg.headers=this.headers
6866         }
6867         if (this.height) {
6868             cfg.height=this.height
6869         }
6870         if (this.nowrap) {
6871             cfg.nowrap=this.nowrap
6872         }
6873         if (this.rowspan) {
6874             cfg.rowspan=this.rowspan
6875         }
6876         if (this.scope) {
6877             cfg.scope=this.scope
6878         }
6879         if (this.valign) {
6880             cfg.valign=this.valign
6881         }
6882         if (this.width) {
6883             cfg.width=this.width
6884         }
6885         
6886         
6887         return cfg;
6888     }
6889    
6890 });
6891
6892  
6893
6894  /*
6895  * - LGPL
6896  *
6897  * table row
6898  * 
6899  */
6900
6901 /**
6902  * @class Roo.bootstrap.TableRow
6903  * @extends Roo.bootstrap.Component
6904  * Bootstrap TableRow class
6905  * @cfg {String} cls row class
6906  * @cfg {String} align Aligns the content in a table row
6907  * @cfg {String} bgcolor Specifies a background color for a table row
6908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6909  * @cfg {String} valign Vertical aligns the content in a table row
6910  * 
6911  * @constructor
6912  * Create a new TableRow
6913  * @param {Object} config The config object
6914  */
6915
6916 Roo.bootstrap.TableRow = function(config){
6917     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6918 };
6919
6920 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6921     
6922     cls: false,
6923     align: false,
6924     bgcolor: false,
6925     charoff: false,
6926     valign: false,
6927     
6928     getAutoCreate : function(){
6929         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6930         
6931         cfg = {
6932             tag: 'tr'
6933         };
6934             
6935         if(this.cls){
6936             cfg.cls = this.cls;
6937         }
6938         if(this.align){
6939             cfg.align = this.align;
6940         }
6941         if(this.bgcolor){
6942             cfg.bgcolor = this.bgcolor;
6943         }
6944         if(this.charoff){
6945             cfg.charoff = this.charoff;
6946         }
6947         if(this.valign){
6948             cfg.valign = this.valign;
6949         }
6950         
6951         return cfg;
6952     }
6953    
6954 });
6955
6956  
6957
6958  /*
6959  * - LGPL
6960  *
6961  * table body
6962  * 
6963  */
6964
6965 /**
6966  * @class Roo.bootstrap.TableBody
6967  * @extends Roo.bootstrap.Component
6968  * Bootstrap TableBody class
6969  * @cfg {String} cls element class
6970  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6971  * @cfg {String} align Aligns the content inside the element
6972  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6973  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6974  * 
6975  * @constructor
6976  * Create a new TableBody
6977  * @param {Object} config The config object
6978  */
6979
6980 Roo.bootstrap.TableBody = function(config){
6981     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6982 };
6983
6984 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6985     
6986     cls: false,
6987     tag: false,
6988     align: false,
6989     charoff: false,
6990     valign: false,
6991     
6992     getAutoCreate : function(){
6993         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6994         
6995         cfg = {
6996             tag: 'tbody'
6997         };
6998             
6999         if (this.cls) {
7000             cfg.cls=this.cls
7001         }
7002         if(this.tag){
7003             cfg.tag = this.tag;
7004         }
7005         
7006         if(this.align){
7007             cfg.align = this.align;
7008         }
7009         if(this.charoff){
7010             cfg.charoff = this.charoff;
7011         }
7012         if(this.valign){
7013             cfg.valign = this.valign;
7014         }
7015         
7016         return cfg;
7017     }
7018     
7019     
7020 //    initEvents : function()
7021 //    {
7022 //        
7023 //        if(!this.store){
7024 //            return;
7025 //        }
7026 //        
7027 //        this.store = Roo.factory(this.store, Roo.data);
7028 //        this.store.on('load', this.onLoad, this);
7029 //        
7030 //        this.store.load();
7031 //        
7032 //    },
7033 //    
7034 //    onLoad: function () 
7035 //    {   
7036 //        this.fireEvent('load', this);
7037 //    }
7038 //    
7039 //   
7040 });
7041
7042  
7043
7044  /*
7045  * Based on:
7046  * Ext JS Library 1.1.1
7047  * Copyright(c) 2006-2007, Ext JS, LLC.
7048  *
7049  * Originally Released Under LGPL - original licence link has changed is not relivant.
7050  *
7051  * Fork - LGPL
7052  * <script type="text/javascript">
7053  */
7054
7055 // as we use this in bootstrap.
7056 Roo.namespace('Roo.form');
7057  /**
7058  * @class Roo.form.Action
7059  * Internal Class used to handle form actions
7060  * @constructor
7061  * @param {Roo.form.BasicForm} el The form element or its id
7062  * @param {Object} config Configuration options
7063  */
7064
7065  
7066  
7067 // define the action interface
7068 Roo.form.Action = function(form, options){
7069     this.form = form;
7070     this.options = options || {};
7071 };
7072 /**
7073  * Client Validation Failed
7074  * @const 
7075  */
7076 Roo.form.Action.CLIENT_INVALID = 'client';
7077 /**
7078  * Server Validation Failed
7079  * @const 
7080  */
7081 Roo.form.Action.SERVER_INVALID = 'server';
7082  /**
7083  * Connect to Server Failed
7084  * @const 
7085  */
7086 Roo.form.Action.CONNECT_FAILURE = 'connect';
7087 /**
7088  * Reading Data from Server Failed
7089  * @const 
7090  */
7091 Roo.form.Action.LOAD_FAILURE = 'load';
7092
7093 Roo.form.Action.prototype = {
7094     type : 'default',
7095     failureType : undefined,
7096     response : undefined,
7097     result : undefined,
7098
7099     // interface method
7100     run : function(options){
7101
7102     },
7103
7104     // interface method
7105     success : function(response){
7106
7107     },
7108
7109     // interface method
7110     handleResponse : function(response){
7111
7112     },
7113
7114     // default connection failure
7115     failure : function(response){
7116         
7117         this.response = response;
7118         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7119         this.form.afterAction(this, false);
7120     },
7121
7122     processResponse : function(response){
7123         this.response = response;
7124         if(!response.responseText){
7125             return true;
7126         }
7127         this.result = this.handleResponse(response);
7128         return this.result;
7129     },
7130
7131     // utility functions used internally
7132     getUrl : function(appendParams){
7133         var url = this.options.url || this.form.url || this.form.el.dom.action;
7134         if(appendParams){
7135             var p = this.getParams();
7136             if(p){
7137                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7138             }
7139         }
7140         return url;
7141     },
7142
7143     getMethod : function(){
7144         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7145     },
7146
7147     getParams : function(){
7148         var bp = this.form.baseParams;
7149         var p = this.options.params;
7150         if(p){
7151             if(typeof p == "object"){
7152                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7153             }else if(typeof p == 'string' && bp){
7154                 p += '&' + Roo.urlEncode(bp);
7155             }
7156         }else if(bp){
7157             p = Roo.urlEncode(bp);
7158         }
7159         return p;
7160     },
7161
7162     createCallback : function(){
7163         return {
7164             success: this.success,
7165             failure: this.failure,
7166             scope: this,
7167             timeout: (this.form.timeout*1000),
7168             upload: this.form.fileUpload ? this.success : undefined
7169         };
7170     }
7171 };
7172
7173 Roo.form.Action.Submit = function(form, options){
7174     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7175 };
7176
7177 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7178     type : 'submit',
7179
7180     haveProgress : false,
7181     uploadComplete : false,
7182     
7183     // uploadProgress indicator.
7184     uploadProgress : function()
7185     {
7186         if (!this.form.progressUrl) {
7187             return;
7188         }
7189         
7190         if (!this.haveProgress) {
7191             Roo.MessageBox.progress("Uploading", "Uploading");
7192         }
7193         if (this.uploadComplete) {
7194            Roo.MessageBox.hide();
7195            return;
7196         }
7197         
7198         this.haveProgress = true;
7199    
7200         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7201         
7202         var c = new Roo.data.Connection();
7203         c.request({
7204             url : this.form.progressUrl,
7205             params: {
7206                 id : uid
7207             },
7208             method: 'GET',
7209             success : function(req){
7210                //console.log(data);
7211                 var rdata = false;
7212                 var edata;
7213                 try  {
7214                    rdata = Roo.decode(req.responseText)
7215                 } catch (e) {
7216                     Roo.log("Invalid data from server..");
7217                     Roo.log(edata);
7218                     return;
7219                 }
7220                 if (!rdata || !rdata.success) {
7221                     Roo.log(rdata);
7222                     Roo.MessageBox.alert(Roo.encode(rdata));
7223                     return;
7224                 }
7225                 var data = rdata.data;
7226                 
7227                 if (this.uploadComplete) {
7228                    Roo.MessageBox.hide();
7229                    return;
7230                 }
7231                    
7232                 if (data){
7233                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7234                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7235                     );
7236                 }
7237                 this.uploadProgress.defer(2000,this);
7238             },
7239        
7240             failure: function(data) {
7241                 Roo.log('progress url failed ');
7242                 Roo.log(data);
7243             },
7244             scope : this
7245         });
7246            
7247     },
7248     
7249     
7250     run : function()
7251     {
7252         // run get Values on the form, so it syncs any secondary forms.
7253         this.form.getValues();
7254         
7255         var o = this.options;
7256         var method = this.getMethod();
7257         var isPost = method == 'POST';
7258         if(o.clientValidation === false || this.form.isValid()){
7259             
7260             if (this.form.progressUrl) {
7261                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7262                     (new Date() * 1) + '' + Math.random());
7263                     
7264             } 
7265             
7266             
7267             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7268                 form:this.form.el.dom,
7269                 url:this.getUrl(!isPost),
7270                 method: method,
7271                 params:isPost ? this.getParams() : null,
7272                 isUpload: this.form.fileUpload
7273             }));
7274             
7275             this.uploadProgress();
7276
7277         }else if (o.clientValidation !== false){ // client validation failed
7278             this.failureType = Roo.form.Action.CLIENT_INVALID;
7279             this.form.afterAction(this, false);
7280         }
7281     },
7282
7283     success : function(response)
7284     {
7285         this.uploadComplete= true;
7286         if (this.haveProgress) {
7287             Roo.MessageBox.hide();
7288         }
7289         
7290         
7291         var result = this.processResponse(response);
7292         if(result === true || result.success){
7293             this.form.afterAction(this, true);
7294             return;
7295         }
7296         if(result.errors){
7297             this.form.markInvalid(result.errors);
7298             this.failureType = Roo.form.Action.SERVER_INVALID;
7299         }
7300         this.form.afterAction(this, false);
7301     },
7302     failure : function(response)
7303     {
7304         this.uploadComplete= true;
7305         if (this.haveProgress) {
7306             Roo.MessageBox.hide();
7307         }
7308         
7309         this.response = response;
7310         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7311         this.form.afterAction(this, false);
7312     },
7313     
7314     handleResponse : function(response){
7315         if(this.form.errorReader){
7316             var rs = this.form.errorReader.read(response);
7317             var errors = [];
7318             if(rs.records){
7319                 for(var i = 0, len = rs.records.length; i < len; i++) {
7320                     var r = rs.records[i];
7321                     errors[i] = r.data;
7322                 }
7323             }
7324             if(errors.length < 1){
7325                 errors = null;
7326             }
7327             return {
7328                 success : rs.success,
7329                 errors : errors
7330             };
7331         }
7332         var ret = false;
7333         try {
7334             ret = Roo.decode(response.responseText);
7335         } catch (e) {
7336             ret = {
7337                 success: false,
7338                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7339                 errors : []
7340             };
7341         }
7342         return ret;
7343         
7344     }
7345 });
7346
7347
7348 Roo.form.Action.Load = function(form, options){
7349     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7350     this.reader = this.form.reader;
7351 };
7352
7353 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7354     type : 'load',
7355
7356     run : function(){
7357         
7358         Roo.Ajax.request(Roo.apply(
7359                 this.createCallback(), {
7360                     method:this.getMethod(),
7361                     url:this.getUrl(false),
7362                     params:this.getParams()
7363         }));
7364     },
7365
7366     success : function(response){
7367         
7368         var result = this.processResponse(response);
7369         if(result === true || !result.success || !result.data){
7370             this.failureType = Roo.form.Action.LOAD_FAILURE;
7371             this.form.afterAction(this, false);
7372             return;
7373         }
7374         this.form.clearInvalid();
7375         this.form.setValues(result.data);
7376         this.form.afterAction(this, true);
7377     },
7378
7379     handleResponse : function(response){
7380         if(this.form.reader){
7381             var rs = this.form.reader.read(response);
7382             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7383             return {
7384                 success : rs.success,
7385                 data : data
7386             };
7387         }
7388         return Roo.decode(response.responseText);
7389     }
7390 });
7391
7392 Roo.form.Action.ACTION_TYPES = {
7393     'load' : Roo.form.Action.Load,
7394     'submit' : Roo.form.Action.Submit
7395 };/*
7396  * - LGPL
7397  *
7398  * form
7399  * 
7400  */
7401
7402 /**
7403  * @class Roo.bootstrap.Form
7404  * @extends Roo.bootstrap.Component
7405  * Bootstrap Form class
7406  * @cfg {String} method  GET | POST (default POST)
7407  * @cfg {String} labelAlign top | left (default top)
7408  * @cfg {String} align left  | right - for navbars
7409  * @cfg {Boolean} loadMask load mask when submit (default true)
7410
7411  * 
7412  * @constructor
7413  * Create a new Form
7414  * @param {Object} config The config object
7415  */
7416
7417
7418 Roo.bootstrap.Form = function(config){
7419     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7420     this.addEvents({
7421         /**
7422          * @event clientvalidation
7423          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7424          * @param {Form} this
7425          * @param {Boolean} valid true if the form has passed client-side validation
7426          */
7427         clientvalidation: true,
7428         /**
7429          * @event beforeaction
7430          * Fires before any action is performed. Return false to cancel the action.
7431          * @param {Form} this
7432          * @param {Action} action The action to be performed
7433          */
7434         beforeaction: true,
7435         /**
7436          * @event actionfailed
7437          * Fires when an action fails.
7438          * @param {Form} this
7439          * @param {Action} action The action that failed
7440          */
7441         actionfailed : true,
7442         /**
7443          * @event actioncomplete
7444          * Fires when an action is completed.
7445          * @param {Form} this
7446          * @param {Action} action The action that completed
7447          */
7448         actioncomplete : true
7449     });
7450     
7451 };
7452
7453 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7454       
7455      /**
7456      * @cfg {String} method
7457      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7458      */
7459     method : 'POST',
7460     /**
7461      * @cfg {String} url
7462      * The URL to use for form actions if one isn't supplied in the action options.
7463      */
7464     /**
7465      * @cfg {Boolean} fileUpload
7466      * Set to true if this form is a file upload.
7467      */
7468      
7469     /**
7470      * @cfg {Object} baseParams
7471      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7472      */
7473       
7474     /**
7475      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7476      */
7477     timeout: 30,
7478     /**
7479      * @cfg {Sting} align (left|right) for navbar forms
7480      */
7481     align : 'left',
7482
7483     // private
7484     activeAction : null,
7485  
7486     /**
7487      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7488      * element by passing it or its id or mask the form itself by passing in true.
7489      * @type Mixed
7490      */
7491     waitMsgTarget : false,
7492     
7493     loadMask : true,
7494     
7495     getAutoCreate : function(){
7496         
7497         var cfg = {
7498             tag: 'form',
7499             method : this.method || 'POST',
7500             id : this.id || Roo.id(),
7501             cls : ''
7502         };
7503         if (this.parent().xtype.match(/^Nav/)) {
7504             cfg.cls = 'navbar-form navbar-' + this.align;
7505             
7506         }
7507         
7508         if (this.labelAlign == 'left' ) {
7509             cfg.cls += ' form-horizontal';
7510         }
7511         
7512         
7513         return cfg;
7514     },
7515     initEvents : function()
7516     {
7517         this.el.on('submit', this.onSubmit, this);
7518         // this was added as random key presses on the form where triggering form submit.
7519         this.el.on('keypress', function(e) {
7520             if (e.getCharCode() != 13) {
7521                 return true;
7522             }
7523             // we might need to allow it for textareas.. and some other items.
7524             // check e.getTarget().
7525             
7526             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7527                 return true;
7528             }
7529         
7530             Roo.log("keypress blocked");
7531             
7532             e.preventDefault();
7533             return false;
7534         });
7535         
7536     },
7537     // private
7538     onSubmit : function(e){
7539         e.stopEvent();
7540     },
7541     
7542      /**
7543      * Returns true if client-side validation on the form is successful.
7544      * @return Boolean
7545      */
7546     isValid : function(){
7547         var items = this.getItems();
7548         var valid = true;
7549         items.each(function(f){
7550            if(!f.validate()){
7551                valid = false;
7552                
7553            }
7554         });
7555         return valid;
7556     },
7557     /**
7558      * Returns true if any fields in this form have changed since their original load.
7559      * @return Boolean
7560      */
7561     isDirty : function(){
7562         var dirty = false;
7563         var items = this.getItems();
7564         items.each(function(f){
7565            if(f.isDirty()){
7566                dirty = true;
7567                return false;
7568            }
7569            return true;
7570         });
7571         return dirty;
7572     },
7573      /**
7574      * Performs a predefined action (submit or load) or custom actions you define on this form.
7575      * @param {String} actionName The name of the action type
7576      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7577      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7578      * accept other config options):
7579      * <pre>
7580 Property          Type             Description
7581 ----------------  ---------------  ----------------------------------------------------------------------------------
7582 url               String           The url for the action (defaults to the form's url)
7583 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7584 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7585 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7586                                    validate the form on the client (defaults to false)
7587      * </pre>
7588      * @return {BasicForm} this
7589      */
7590     doAction : function(action, options){
7591         if(typeof action == 'string'){
7592             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7593         }
7594         if(this.fireEvent('beforeaction', this, action) !== false){
7595             this.beforeAction(action);
7596             action.run.defer(100, action);
7597         }
7598         return this;
7599     },
7600     
7601     // private
7602     beforeAction : function(action){
7603         var o = action.options;
7604         
7605         if(this.loadMask){
7606             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7607         }
7608         // not really supported yet.. ??
7609         
7610         //if(this.waitMsgTarget === true){
7611         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7612         //}else if(this.waitMsgTarget){
7613         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7614         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7615         //}else {
7616         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7617        // }
7618          
7619     },
7620
7621     // private
7622     afterAction : function(action, success){
7623         this.activeAction = null;
7624         var o = action.options;
7625         
7626         //if(this.waitMsgTarget === true){
7627             this.el.unmask();
7628         //}else if(this.waitMsgTarget){
7629         //    this.waitMsgTarget.unmask();
7630         //}else{
7631         //    Roo.MessageBox.updateProgress(1);
7632         //    Roo.MessageBox.hide();
7633        // }
7634         // 
7635         if(success){
7636             if(o.reset){
7637                 this.reset();
7638             }
7639             Roo.callback(o.success, o.scope, [this, action]);
7640             this.fireEvent('actioncomplete', this, action);
7641             
7642         }else{
7643             
7644             // failure condition..
7645             // we have a scenario where updates need confirming.
7646             // eg. if a locking scenario exists..
7647             // we look for { errors : { needs_confirm : true }} in the response.
7648             if (
7649                 (typeof(action.result) != 'undefined')  &&
7650                 (typeof(action.result.errors) != 'undefined')  &&
7651                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7652            ){
7653                 var _t = this;
7654                 Roo.log("not supported yet");
7655                  /*
7656                 
7657                 Roo.MessageBox.confirm(
7658                     "Change requires confirmation",
7659                     action.result.errorMsg,
7660                     function(r) {
7661                         if (r != 'yes') {
7662                             return;
7663                         }
7664                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7665                     }
7666                     
7667                 );
7668                 */
7669                 
7670                 
7671                 return;
7672             }
7673             
7674             Roo.callback(o.failure, o.scope, [this, action]);
7675             // show an error message if no failed handler is set..
7676             if (!this.hasListener('actionfailed')) {
7677                 Roo.log("need to add dialog support");
7678                 /*
7679                 Roo.MessageBox.alert("Error",
7680                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7681                         action.result.errorMsg :
7682                         "Saving Failed, please check your entries or try again"
7683                 );
7684                 */
7685             }
7686             
7687             this.fireEvent('actionfailed', this, action);
7688         }
7689         
7690     },
7691     /**
7692      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7693      * @param {String} id The value to search for
7694      * @return Field
7695      */
7696     findField : function(id){
7697         var items = this.getItems();
7698         var field = items.get(id);
7699         if(!field){
7700              items.each(function(f){
7701                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7702                     field = f;
7703                     return false;
7704                 }
7705                 return true;
7706             });
7707         }
7708         return field || null;
7709     },
7710      /**
7711      * Mark fields in this form invalid in bulk.
7712      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7713      * @return {BasicForm} this
7714      */
7715     markInvalid : function(errors){
7716         if(errors instanceof Array){
7717             for(var i = 0, len = errors.length; i < len; i++){
7718                 var fieldError = errors[i];
7719                 var f = this.findField(fieldError.id);
7720                 if(f){
7721                     f.markInvalid(fieldError.msg);
7722                 }
7723             }
7724         }else{
7725             var field, id;
7726             for(id in errors){
7727                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7728                     field.markInvalid(errors[id]);
7729                 }
7730             }
7731         }
7732         //Roo.each(this.childForms || [], function (f) {
7733         //    f.markInvalid(errors);
7734         //});
7735         
7736         return this;
7737     },
7738
7739     /**
7740      * Set values for fields in this form in bulk.
7741      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7742      * @return {BasicForm} this
7743      */
7744     setValues : function(values){
7745         if(values instanceof Array){ // array of objects
7746             for(var i = 0, len = values.length; i < len; i++){
7747                 var v = values[i];
7748                 var f = this.findField(v.id);
7749                 if(f){
7750                     f.setValue(v.value);
7751                     if(this.trackResetOnLoad){
7752                         f.originalValue = f.getValue();
7753                     }
7754                 }
7755             }
7756         }else{ // object hash
7757             var field, id;
7758             for(id in values){
7759                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7760                     
7761                     if (field.setFromData && 
7762                         field.valueField && 
7763                         field.displayField &&
7764                         // combos' with local stores can 
7765                         // be queried via setValue()
7766                         // to set their value..
7767                         (field.store && !field.store.isLocal)
7768                         ) {
7769                         // it's a combo
7770                         var sd = { };
7771                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7772                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7773                         field.setFromData(sd);
7774                         
7775                     } else {
7776                         field.setValue(values[id]);
7777                     }
7778                     
7779                     
7780                     if(this.trackResetOnLoad){
7781                         field.originalValue = field.getValue();
7782                     }
7783                 }
7784             }
7785         }
7786          
7787         //Roo.each(this.childForms || [], function (f) {
7788         //    f.setValues(values);
7789         //});
7790                 
7791         return this;
7792     },
7793
7794     /**
7795      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7796      * they are returned as an array.
7797      * @param {Boolean} asString
7798      * @return {Object}
7799      */
7800     getValues : function(asString){
7801         //if (this.childForms) {
7802             // copy values from the child forms
7803         //    Roo.each(this.childForms, function (f) {
7804         //        this.setValues(f.getValues());
7805         //    }, this);
7806         //}
7807         
7808         
7809         
7810         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7811         if(asString === true){
7812             return fs;
7813         }
7814         return Roo.urlDecode(fs);
7815     },
7816     
7817     /**
7818      * Returns the fields in this form as an object with key/value pairs. 
7819      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7820      * @return {Object}
7821      */
7822     getFieldValues : function(with_hidden)
7823     {
7824         var items = this.getItems();
7825         var ret = {};
7826         items.each(function(f){
7827             if (!f.getName()) {
7828                 return;
7829             }
7830             var v = f.getValue();
7831             if (f.inputType =='radio') {
7832                 if (typeof(ret[f.getName()]) == 'undefined') {
7833                     ret[f.getName()] = ''; // empty..
7834                 }
7835                 
7836                 if (!f.el.dom.checked) {
7837                     return;
7838                     
7839                 }
7840                 v = f.el.dom.value;
7841                 
7842             }
7843             
7844             // not sure if this supported any more..
7845             if ((typeof(v) == 'object') && f.getRawValue) {
7846                 v = f.getRawValue() ; // dates..
7847             }
7848             // combo boxes where name != hiddenName...
7849             if (f.name != f.getName()) {
7850                 ret[f.name] = f.getRawValue();
7851             }
7852             ret[f.getName()] = v;
7853         });
7854         
7855         return ret;
7856     },
7857
7858     /**
7859      * Clears all invalid messages in this form.
7860      * @return {BasicForm} this
7861      */
7862     clearInvalid : function(){
7863         var items = this.getItems();
7864         
7865         items.each(function(f){
7866            f.clearInvalid();
7867         });
7868         
7869         
7870         
7871         return this;
7872     },
7873
7874     /**
7875      * Resets this form.
7876      * @return {BasicForm} this
7877      */
7878     reset : function(){
7879         var items = this.getItems();
7880         items.each(function(f){
7881             f.reset();
7882         });
7883         
7884         Roo.each(this.childForms || [], function (f) {
7885             f.reset();
7886         });
7887        
7888         
7889         return this;
7890     },
7891     getItems : function()
7892     {
7893         var r=new Roo.util.MixedCollection(false, function(o){
7894             return o.id || (o.id = Roo.id());
7895         });
7896         var iter = function(el) {
7897             if (el.inputEl) {
7898                 r.add(el);
7899             }
7900             if (!el.items) {
7901                 return;
7902             }
7903             Roo.each(el.items,function(e) {
7904                 iter(e);
7905             });
7906             
7907             
7908         };
7909         
7910         iter(this);
7911         return r;
7912         
7913         
7914         
7915         
7916     }
7917     
7918 });
7919
7920  
7921 /*
7922  * Based on:
7923  * Ext JS Library 1.1.1
7924  * Copyright(c) 2006-2007, Ext JS, LLC.
7925  *
7926  * Originally Released Under LGPL - original licence link has changed is not relivant.
7927  *
7928  * Fork - LGPL
7929  * <script type="text/javascript">
7930  */
7931 /**
7932  * @class Roo.form.VTypes
7933  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7934  * @singleton
7935  */
7936 Roo.form.VTypes = function(){
7937     // closure these in so they are only created once.
7938     var alpha = /^[a-zA-Z_]+$/;
7939     var alphanum = /^[a-zA-Z0-9_]+$/;
7940     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7941     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7942
7943     // All these messages and functions are configurable
7944     return {
7945         /**
7946          * The function used to validate email addresses
7947          * @param {String} value The email address
7948          */
7949         'email' : function(v){
7950             return email.test(v);
7951         },
7952         /**
7953          * The error text to display when the email validation function returns false
7954          * @type String
7955          */
7956         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7957         /**
7958          * The keystroke filter mask to be applied on email input
7959          * @type RegExp
7960          */
7961         'emailMask' : /[a-z0-9_\.\-@]/i,
7962
7963         /**
7964          * The function used to validate URLs
7965          * @param {String} value The URL
7966          */
7967         'url' : function(v){
7968             return url.test(v);
7969         },
7970         /**
7971          * The error text to display when the url validation function returns false
7972          * @type String
7973          */
7974         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7975         
7976         /**
7977          * The function used to validate alpha values
7978          * @param {String} value The value
7979          */
7980         'alpha' : function(v){
7981             return alpha.test(v);
7982         },
7983         /**
7984          * The error text to display when the alpha validation function returns false
7985          * @type String
7986          */
7987         'alphaText' : 'This field should only contain letters and _',
7988         /**
7989          * The keystroke filter mask to be applied on alpha input
7990          * @type RegExp
7991          */
7992         'alphaMask' : /[a-z_]/i,
7993
7994         /**
7995          * The function used to validate alphanumeric values
7996          * @param {String} value The value
7997          */
7998         'alphanum' : function(v){
7999             return alphanum.test(v);
8000         },
8001         /**
8002          * The error text to display when the alphanumeric validation function returns false
8003          * @type String
8004          */
8005         'alphanumText' : 'This field should only contain letters, numbers and _',
8006         /**
8007          * The keystroke filter mask to be applied on alphanumeric input
8008          * @type RegExp
8009          */
8010         'alphanumMask' : /[a-z0-9_]/i
8011     };
8012 }();/*
8013  * - LGPL
8014  *
8015  * Input
8016  * 
8017  */
8018
8019 /**
8020  * @class Roo.bootstrap.Input
8021  * @extends Roo.bootstrap.Component
8022  * Bootstrap Input class
8023  * @cfg {Boolean} disabled is it disabled
8024  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8025  * @cfg {String} name name of the input
8026  * @cfg {string} fieldLabel - the label associated
8027  * @cfg {string} placeholder - placeholder to put in text.
8028  * @cfg {string}  before - input group add on before
8029  * @cfg {string} after - input group add on after
8030  * @cfg {string} size - (lg|sm) or leave empty..
8031  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8032  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8033  * @cfg {Number} md colspan out of 12 for computer-sized screens
8034  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8035  * @cfg {string} value default value of the input
8036  * @cfg {Number} labelWidth set the width of label (0-12)
8037  * @cfg {String} labelAlign (top|left)
8038  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8039  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8040  * @cfg {String} indicatorpos (left|right) default left
8041
8042  * @cfg {String} align (left|center|right) Default left
8043  * @cfg {Boolean} forceFeedback (true|false) Default false
8044  * 
8045  * 
8046  * 
8047  * 
8048  * @constructor
8049  * Create a new Input
8050  * @param {Object} config The config object
8051  */
8052
8053 Roo.bootstrap.Input = function(config){
8054     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8055    
8056         this.addEvents({
8057             /**
8058              * @event focus
8059              * Fires when this field receives input focus.
8060              * @param {Roo.form.Field} this
8061              */
8062             focus : true,
8063             /**
8064              * @event blur
8065              * Fires when this field loses input focus.
8066              * @param {Roo.form.Field} this
8067              */
8068             blur : true,
8069             /**
8070              * @event specialkey
8071              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8072              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8073              * @param {Roo.form.Field} this
8074              * @param {Roo.EventObject} e The event object
8075              */
8076             specialkey : true,
8077             /**
8078              * @event change
8079              * Fires just before the field blurs if the field value has changed.
8080              * @param {Roo.form.Field} this
8081              * @param {Mixed} newValue The new value
8082              * @param {Mixed} oldValue The original value
8083              */
8084             change : true,
8085             /**
8086              * @event invalid
8087              * Fires after the field has been marked as invalid.
8088              * @param {Roo.form.Field} this
8089              * @param {String} msg The validation message
8090              */
8091             invalid : true,
8092             /**
8093              * @event valid
8094              * Fires after the field has been validated with no errors.
8095              * @param {Roo.form.Field} this
8096              */
8097             valid : true,
8098              /**
8099              * @event keyup
8100              * Fires after the key up
8101              * @param {Roo.form.Field} this
8102              * @param {Roo.EventObject}  e The event Object
8103              */
8104             keyup : true
8105         });
8106 };
8107
8108 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8109      /**
8110      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8111       automatic validation (defaults to "keyup").
8112      */
8113     validationEvent : "keyup",
8114      /**
8115      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8116      */
8117     validateOnBlur : true,
8118     /**
8119      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8120      */
8121     validationDelay : 250,
8122      /**
8123      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8124      */
8125     focusClass : "x-form-focus",  // not needed???
8126     
8127        
8128     /**
8129      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8130      */
8131     invalidClass : "has-warning",
8132     
8133     /**
8134      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8135      */
8136     validClass : "has-success",
8137     
8138     /**
8139      * @cfg {Boolean} hasFeedback (true|false) default true
8140      */
8141     hasFeedback : true,
8142     
8143     /**
8144      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8145      */
8146     invalidFeedbackClass : "glyphicon-warning-sign",
8147     
8148     /**
8149      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8150      */
8151     validFeedbackClass : "glyphicon-ok",
8152     
8153     /**
8154      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8155      */
8156     selectOnFocus : false,
8157     
8158      /**
8159      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8160      */
8161     maskRe : null,
8162        /**
8163      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8164      */
8165     vtype : null,
8166     
8167       /**
8168      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8169      */
8170     disableKeyFilter : false,
8171     
8172        /**
8173      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8174      */
8175     disabled : false,
8176      /**
8177      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8178      */
8179     allowBlank : true,
8180     /**
8181      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8182      */
8183     blankText : "This field is required",
8184     
8185      /**
8186      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8187      */
8188     minLength : 0,
8189     /**
8190      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8191      */
8192     maxLength : Number.MAX_VALUE,
8193     /**
8194      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8195      */
8196     minLengthText : "The minimum length for this field is {0}",
8197     /**
8198      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8199      */
8200     maxLengthText : "The maximum length for this field is {0}",
8201   
8202     
8203     /**
8204      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8205      * If available, this function will be called only after the basic validators all return true, and will be passed the
8206      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8207      */
8208     validator : null,
8209     /**
8210      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8211      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8212      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8213      */
8214     regex : null,
8215     /**
8216      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8217      */
8218     regexText : "",
8219     
8220     autocomplete: false,
8221     
8222     
8223     fieldLabel : '',
8224     inputType : 'text',
8225     
8226     name : false,
8227     placeholder: false,
8228     before : false,
8229     after : false,
8230     size : false,
8231     hasFocus : false,
8232     preventMark: false,
8233     isFormField : true,
8234     value : '',
8235     labelWidth : 2,
8236     labelAlign : false,
8237     readOnly : false,
8238     align : false,
8239     formatedValue : false,
8240     forceFeedback : false,
8241     
8242     indicatorpos : 'left',
8243     
8244     parentLabelAlign : function()
8245     {
8246         var parent = this;
8247         while (parent.parent()) {
8248             parent = parent.parent();
8249             if (typeof(parent.labelAlign) !='undefined') {
8250                 return parent.labelAlign;
8251             }
8252         }
8253         return 'left';
8254         
8255     },
8256     
8257     getAutoCreate : function()
8258     {
8259         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8260         
8261         var id = Roo.id();
8262         
8263         var cfg = {};
8264         
8265         if(this.inputType != 'hidden'){
8266             cfg.cls = 'form-group' //input-group
8267         }
8268         
8269         var input =  {
8270             tag: 'input',
8271             id : id,
8272             type : this.inputType,
8273             value : this.value,
8274             cls : 'form-control',
8275             placeholder : this.placeholder || '',
8276             autocomplete : this.autocomplete || 'new-password'
8277         };
8278         
8279         if(this.align){
8280             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8281         }
8282         
8283         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8284             input.maxLength = this.maxLength;
8285         }
8286         
8287         if (this.disabled) {
8288             input.disabled=true;
8289         }
8290         
8291         if (this.readOnly) {
8292             input.readonly=true;
8293         }
8294         
8295         if (this.name) {
8296             input.name = this.name;
8297         }
8298         
8299         if (this.size) {
8300             input.cls += ' input-' + this.size;
8301         }
8302         
8303         var settings=this;
8304         ['xs','sm','md','lg'].map(function(size){
8305             if (settings[size]) {
8306                 cfg.cls += ' col-' + size + '-' + settings[size];
8307             }
8308         });
8309         
8310         var inputblock = input;
8311         
8312         var feedback = {
8313             tag: 'span',
8314             cls: 'glyphicon form-control-feedback'
8315         };
8316             
8317         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8318             
8319             inputblock = {
8320                 cls : 'has-feedback',
8321                 cn :  [
8322                     input,
8323                     feedback
8324                 ] 
8325             };  
8326         }
8327         
8328         if (this.before || this.after) {
8329             
8330             inputblock = {
8331                 cls : 'input-group',
8332                 cn :  [] 
8333             };
8334             
8335             if (this.before && typeof(this.before) == 'string') {
8336                 
8337                 inputblock.cn.push({
8338                     tag :'span',
8339                     cls : 'roo-input-before input-group-addon',
8340                     html : this.before
8341                 });
8342             }
8343             if (this.before && typeof(this.before) == 'object') {
8344                 this.before = Roo.factory(this.before);
8345                 
8346                 inputblock.cn.push({
8347                     tag :'span',
8348                     cls : 'roo-input-before input-group-' +
8349                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8350                 });
8351             }
8352             
8353             inputblock.cn.push(input);
8354             
8355             if (this.after && typeof(this.after) == 'string') {
8356                 inputblock.cn.push({
8357                     tag :'span',
8358                     cls : 'roo-input-after input-group-addon',
8359                     html : this.after
8360                 });
8361             }
8362             if (this.after && typeof(this.after) == 'object') {
8363                 this.after = Roo.factory(this.after);
8364                 
8365                 inputblock.cn.push({
8366                     tag :'span',
8367                     cls : 'roo-input-after input-group-' +
8368                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8369                 });
8370             }
8371             
8372             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8373                 inputblock.cls += ' has-feedback';
8374                 inputblock.cn.push(feedback);
8375             }
8376         };
8377         
8378         if (align ==='left' && this.fieldLabel.length) {
8379             
8380             cfg.cn = [
8381                 {
8382                     tag : 'i',
8383                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8384                     tooltip : 'This field is required'
8385                 },
8386                 {
8387                     tag: 'label',
8388                     'for' :  id,
8389                     cls : 'control-label col-sm-' + this.labelWidth,
8390                     html : this.fieldLabel
8391
8392                 },
8393                 {
8394                     cls : "col-sm-" + (12 - this.labelWidth), 
8395                     cn: [
8396                         inputblock
8397                     ]
8398                 }
8399
8400             ];
8401             
8402             if(this.indicatorpos == 'right'){
8403                 cfg.cn = [
8404                     {
8405                         tag: 'label',
8406                         'for' :  id,
8407                         cls : 'control-label col-sm-' + this.labelWidth,
8408                         html : this.fieldLabel
8409
8410                     },
8411                     {
8412                         tag : 'i',
8413                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8414                         tooltip : 'This field is required'
8415                     },
8416                     {
8417                         cls : "col-sm-" + (12 - this.labelWidth), 
8418                         cn: [
8419                             inputblock
8420                         ]
8421                     }
8422
8423                 ];
8424             }
8425             
8426         } else if ( this.fieldLabel.length) {
8427                 
8428             cfg.cn = [
8429                 {
8430                     tag : 'i',
8431                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8432                     tooltip : 'This field is required'
8433                 },
8434                 {
8435                     tag: 'label',
8436                    //cls : 'input-group-addon',
8437                     html : this.fieldLabel
8438
8439                 },
8440
8441                inputblock
8442
8443            ];
8444            
8445            if(this.indicatorpos == 'right'){
8446                 
8447                 cfg.cn = [
8448                     {
8449                         tag: 'label',
8450                        //cls : 'input-group-addon',
8451                         html : this.fieldLabel
8452
8453                     },
8454                     {
8455                         tag : 'i',
8456                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8457                         tooltip : 'This field is required'
8458                     },
8459
8460                    inputblock
8461
8462                ];
8463
8464             }
8465
8466         } else {
8467             
8468             cfg.cn = [
8469
8470                     inputblock
8471
8472             ];
8473                 
8474                 
8475         };
8476         
8477         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8478            cfg.cls += ' navbar-form';
8479         }
8480         
8481         if (this.parentType === 'NavGroup') {
8482            cfg.cls += ' navbar-form';
8483            cfg.tag = 'li';
8484         }
8485         
8486         return cfg;
8487         
8488     },
8489     /**
8490      * return the real input element.
8491      */
8492     inputEl: function ()
8493     {
8494         return this.el.select('input.form-control',true).first();
8495     },
8496     
8497     tooltipEl : function()
8498     {
8499         return this.inputEl();
8500     },
8501     
8502     indicatorEl : function()
8503     {
8504         var indicator = this.el.select('i.roo-required-indicator',true).first();
8505         
8506         if(!indicator){
8507             return false;
8508         }
8509         
8510         return indicator;
8511         
8512     },
8513     
8514     setDisabled : function(v)
8515     {
8516         var i  = this.inputEl().dom;
8517         if (!v) {
8518             i.removeAttribute('disabled');
8519             return;
8520             
8521         }
8522         i.setAttribute('disabled','true');
8523     },
8524     initEvents : function()
8525     {
8526           
8527         this.inputEl().on("keydown" , this.fireKey,  this);
8528         this.inputEl().on("focus", this.onFocus,  this);
8529         this.inputEl().on("blur", this.onBlur,  this);
8530         
8531         this.inputEl().relayEvent('keyup', this);
8532         
8533         this.indicator = this.indicatorEl();
8534         
8535         if(this.indicator){
8536             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8537             this.indicator.hide();
8538         }
8539  
8540         // reference to original value for reset
8541         this.originalValue = this.getValue();
8542         //Roo.form.TextField.superclass.initEvents.call(this);
8543         if(this.validationEvent == 'keyup'){
8544             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8545             this.inputEl().on('keyup', this.filterValidation, this);
8546         }
8547         else if(this.validationEvent !== false){
8548             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8549         }
8550         
8551         if(this.selectOnFocus){
8552             this.on("focus", this.preFocus, this);
8553             
8554         }
8555         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8556             this.inputEl().on("keypress", this.filterKeys, this);
8557         } else {
8558             this.inputEl().relayEvent('keypress', this);
8559         }
8560        /* if(this.grow){
8561             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8562             this.el.on("click", this.autoSize,  this);
8563         }
8564         */
8565         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8566             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8567         }
8568         
8569         if (typeof(this.before) == 'object') {
8570             this.before.render(this.el.select('.roo-input-before',true).first());
8571         }
8572         if (typeof(this.after) == 'object') {
8573             this.after.render(this.el.select('.roo-input-after',true).first());
8574         }
8575         
8576         
8577     },
8578     filterValidation : function(e){
8579         if(!e.isNavKeyPress()){
8580             this.validationTask.delay(this.validationDelay);
8581         }
8582     },
8583      /**
8584      * Validates the field value
8585      * @return {Boolean} True if the value is valid, else false
8586      */
8587     validate : function(){
8588         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8589         if(this.disabled || this.validateValue(this.getRawValue())){
8590             this.markValid();
8591             return true;
8592         }
8593         
8594         this.markInvalid();
8595         return false;
8596     },
8597     
8598     
8599     /**
8600      * Validates a value according to the field's validation rules and marks the field as invalid
8601      * if the validation fails
8602      * @param {Mixed} value The value to validate
8603      * @return {Boolean} True if the value is valid, else false
8604      */
8605     validateValue : function(value){
8606         if(value.length < 1)  { // if it's blank
8607             if(this.allowBlank){
8608                 return true;
8609             }
8610             return false;
8611         }
8612         
8613         if(value.length < this.minLength){
8614             return false;
8615         }
8616         if(value.length > this.maxLength){
8617             return false;
8618         }
8619         if(this.vtype){
8620             var vt = Roo.form.VTypes;
8621             if(!vt[this.vtype](value, this)){
8622                 return false;
8623             }
8624         }
8625         if(typeof this.validator == "function"){
8626             var msg = this.validator(value);
8627             if(msg !== true){
8628                 return false;
8629             }
8630         }
8631         
8632         if(this.regex && !this.regex.test(value)){
8633             return false;
8634         }
8635         
8636         return true;
8637     },
8638
8639     
8640     
8641      // private
8642     fireKey : function(e){
8643         //Roo.log('field ' + e.getKey());
8644         if(e.isNavKeyPress()){
8645             this.fireEvent("specialkey", this, e);
8646         }
8647     },
8648     focus : function (selectText){
8649         if(this.rendered){
8650             this.inputEl().focus();
8651             if(selectText === true){
8652                 this.inputEl().dom.select();
8653             }
8654         }
8655         return this;
8656     } ,
8657     
8658     onFocus : function(){
8659         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8660            // this.el.addClass(this.focusClass);
8661         }
8662         if(!this.hasFocus){
8663             this.hasFocus = true;
8664             this.startValue = this.getValue();
8665             this.fireEvent("focus", this);
8666         }
8667     },
8668     
8669     beforeBlur : Roo.emptyFn,
8670
8671     
8672     // private
8673     onBlur : function(){
8674         this.beforeBlur();
8675         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8676             //this.el.removeClass(this.focusClass);
8677         }
8678         this.hasFocus = false;
8679         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8680             this.validate();
8681         }
8682         var v = this.getValue();
8683         if(String(v) !== String(this.startValue)){
8684             this.fireEvent('change', this, v, this.startValue);
8685         }
8686         this.fireEvent("blur", this);
8687     },
8688     
8689     /**
8690      * Resets the current field value to the originally loaded value and clears any validation messages
8691      */
8692     reset : function(){
8693         this.setValue(this.originalValue);
8694         this.validate();
8695     },
8696      /**
8697      * Returns the name of the field
8698      * @return {Mixed} name The name field
8699      */
8700     getName: function(){
8701         return this.name;
8702     },
8703      /**
8704      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8705      * @return {Mixed} value The field value
8706      */
8707     getValue : function(){
8708         
8709         var v = this.inputEl().getValue();
8710         
8711         return v;
8712     },
8713     /**
8714      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8715      * @return {Mixed} value The field value
8716      */
8717     getRawValue : function(){
8718         var v = this.inputEl().getValue();
8719         
8720         return v;
8721     },
8722     
8723     /**
8724      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8725      * @param {Mixed} value The value to set
8726      */
8727     setRawValue : function(v){
8728         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8729     },
8730     
8731     selectText : function(start, end){
8732         var v = this.getRawValue();
8733         if(v.length > 0){
8734             start = start === undefined ? 0 : start;
8735             end = end === undefined ? v.length : end;
8736             var d = this.inputEl().dom;
8737             if(d.setSelectionRange){
8738                 d.setSelectionRange(start, end);
8739             }else if(d.createTextRange){
8740                 var range = d.createTextRange();
8741                 range.moveStart("character", start);
8742                 range.moveEnd("character", v.length-end);
8743                 range.select();
8744             }
8745         }
8746     },
8747     
8748     /**
8749      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8750      * @param {Mixed} value The value to set
8751      */
8752     setValue : function(v){
8753         this.value = v;
8754         if(this.rendered){
8755             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8756             this.validate();
8757         }
8758     },
8759     
8760     /*
8761     processValue : function(value){
8762         if(this.stripCharsRe){
8763             var newValue = value.replace(this.stripCharsRe, '');
8764             if(newValue !== value){
8765                 this.setRawValue(newValue);
8766                 return newValue;
8767             }
8768         }
8769         return value;
8770     },
8771   */
8772     preFocus : function(){
8773         
8774         if(this.selectOnFocus){
8775             this.inputEl().dom.select();
8776         }
8777     },
8778     filterKeys : function(e){
8779         var k = e.getKey();
8780         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8781             return;
8782         }
8783         var c = e.getCharCode(), cc = String.fromCharCode(c);
8784         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8785             return;
8786         }
8787         if(!this.maskRe.test(cc)){
8788             e.stopEvent();
8789         }
8790     },
8791      /**
8792      * Clear any invalid styles/messages for this field
8793      */
8794     clearInvalid : function(){
8795         
8796         if(!this.el || this.preventMark){ // not rendered
8797             return;
8798         }
8799         
8800         if(this.indicator){
8801             this.indicator.hide();
8802         }
8803         
8804         this.el.removeClass(this.invalidClass);
8805         
8806         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8807             
8808             var feedback = this.el.select('.form-control-feedback', true).first();
8809             
8810             if(feedback){
8811                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8812             }
8813             
8814         }
8815         
8816         this.fireEvent('valid', this);
8817     },
8818     
8819      /**
8820      * Mark this field as valid
8821      */
8822     markValid : function()
8823     {
8824         if(!this.el  || this.preventMark){ // not rendered
8825             return;
8826         }
8827         
8828         this.el.removeClass([this.invalidClass, this.validClass]);
8829         
8830         var feedback = this.el.select('.form-control-feedback', true).first();
8831             
8832         if(feedback){
8833             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8834         }
8835
8836         if(this.disabled || this.allowBlank){
8837             return;
8838         }
8839         
8840         if(this.indicator){
8841             this.indicator.hide();
8842         }
8843         
8844         this.el.addClass(this.validClass);
8845         
8846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8847             
8848             var feedback = this.el.select('.form-control-feedback', true).first();
8849             
8850             if(feedback){
8851                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8852                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8853             }
8854             
8855         }
8856         
8857         this.fireEvent('valid', this);
8858     },
8859     
8860      /**
8861      * Mark this field as invalid
8862      * @param {String} msg The validation message
8863      */
8864     markInvalid : function(msg)
8865     {
8866         if(!this.el  || this.preventMark){ // not rendered
8867             return;
8868         }
8869         
8870         this.el.removeClass([this.invalidClass, this.validClass]);
8871         
8872         var feedback = this.el.select('.form-control-feedback', true).first();
8873             
8874         if(feedback){
8875             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8876         }
8877
8878         if(this.disabled || this.allowBlank){
8879             return;
8880         }
8881         
8882         if(this.indicator){
8883             this.indicator.show();
8884         }
8885         
8886         this.el.addClass(this.invalidClass);
8887         
8888         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8889             
8890             var feedback = this.el.select('.form-control-feedback', true).first();
8891             
8892             if(feedback){
8893                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8894                 
8895                 if(this.getValue().length || this.forceFeedback){
8896                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8897                 }
8898                 
8899             }
8900             
8901         }
8902         
8903         this.fireEvent('invalid', this, msg);
8904     },
8905     // private
8906     SafariOnKeyDown : function(event)
8907     {
8908         // this is a workaround for a password hang bug on chrome/ webkit.
8909         
8910         var isSelectAll = false;
8911         
8912         if(this.inputEl().dom.selectionEnd > 0){
8913             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8914         }
8915         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8916             event.preventDefault();
8917             this.setValue('');
8918             return;
8919         }
8920         
8921         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8922             
8923             event.preventDefault();
8924             // this is very hacky as keydown always get's upper case.
8925             //
8926             var cc = String.fromCharCode(event.getCharCode());
8927             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8928             
8929         }
8930     },
8931     adjustWidth : function(tag, w){
8932         tag = tag.toLowerCase();
8933         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8934             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8935                 if(tag == 'input'){
8936                     return w + 2;
8937                 }
8938                 if(tag == 'textarea'){
8939                     return w-2;
8940                 }
8941             }else if(Roo.isOpera){
8942                 if(tag == 'input'){
8943                     return w + 2;
8944                 }
8945                 if(tag == 'textarea'){
8946                     return w-2;
8947                 }
8948             }
8949         }
8950         return w;
8951     }
8952     
8953 });
8954
8955  
8956 /*
8957  * - LGPL
8958  *
8959  * Input
8960  * 
8961  */
8962
8963 /**
8964  * @class Roo.bootstrap.TextArea
8965  * @extends Roo.bootstrap.Input
8966  * Bootstrap TextArea class
8967  * @cfg {Number} cols Specifies the visible width of a text area
8968  * @cfg {Number} rows Specifies the visible number of lines in a text area
8969  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8970  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8971  * @cfg {string} html text
8972  * 
8973  * @constructor
8974  * Create a new TextArea
8975  * @param {Object} config The config object
8976  */
8977
8978 Roo.bootstrap.TextArea = function(config){
8979     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8980    
8981 };
8982
8983 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8984      
8985     cols : false,
8986     rows : 5,
8987     readOnly : false,
8988     warp : 'soft',
8989     resize : false,
8990     value: false,
8991     html: false,
8992     
8993     getAutoCreate : function(){
8994         
8995         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8996         
8997         var id = Roo.id();
8998         
8999         var cfg = {};
9000         
9001         var input =  {
9002             tag: 'textarea',
9003             id : id,
9004             warp : this.warp,
9005             rows : this.rows,
9006             value : this.value || '',
9007             html: this.html || '',
9008             cls : 'form-control',
9009             placeholder : this.placeholder || '' 
9010             
9011         };
9012         
9013         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9014             input.maxLength = this.maxLength;
9015         }
9016         
9017         if(this.resize){
9018             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9019         }
9020         
9021         if(this.cols){
9022             input.cols = this.cols;
9023         }
9024         
9025         if (this.readOnly) {
9026             input.readonly = true;
9027         }
9028         
9029         if (this.name) {
9030             input.name = this.name;
9031         }
9032         
9033         if (this.size) {
9034             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9035         }
9036         
9037         var settings=this;
9038         ['xs','sm','md','lg'].map(function(size){
9039             if (settings[size]) {
9040                 cfg.cls += ' col-' + size + '-' + settings[size];
9041             }
9042         });
9043         
9044         var inputblock = input;
9045         
9046         if(this.hasFeedback && !this.allowBlank){
9047             
9048             var feedback = {
9049                 tag: 'span',
9050                 cls: 'glyphicon form-control-feedback'
9051             };
9052
9053             inputblock = {
9054                 cls : 'has-feedback',
9055                 cn :  [
9056                     input,
9057                     feedback
9058                 ] 
9059             };  
9060         }
9061         
9062         
9063         if (this.before || this.after) {
9064             
9065             inputblock = {
9066                 cls : 'input-group',
9067                 cn :  [] 
9068             };
9069             if (this.before) {
9070                 inputblock.cn.push({
9071                     tag :'span',
9072                     cls : 'input-group-addon',
9073                     html : this.before
9074                 });
9075             }
9076             
9077             inputblock.cn.push(input);
9078             
9079             if(this.hasFeedback && !this.allowBlank){
9080                 inputblock.cls += ' has-feedback';
9081                 inputblock.cn.push(feedback);
9082             }
9083             
9084             if (this.after) {
9085                 inputblock.cn.push({
9086                     tag :'span',
9087                     cls : 'input-group-addon',
9088                     html : this.after
9089                 });
9090             }
9091             
9092         }
9093         
9094         if (align ==='left' && this.fieldLabel.length) {
9095 //                Roo.log("left and has label");
9096                 cfg.cn = [
9097                     
9098                     {
9099                         tag: 'label',
9100                         'for' :  id,
9101                         cls : 'control-label col-sm-' + this.labelWidth,
9102                         html : this.fieldLabel
9103                         
9104                     },
9105                     {
9106                         cls : "col-sm-" + (12 - this.labelWidth), 
9107                         cn: [
9108                             inputblock
9109                         ]
9110                     }
9111                     
9112                 ];
9113         } else if ( this.fieldLabel.length) {
9114 //                Roo.log(" label");
9115                  cfg.cn = [
9116                    
9117                     {
9118                         tag: 'label',
9119                         //cls : 'input-group-addon',
9120                         html : this.fieldLabel
9121                         
9122                     },
9123                     
9124                     inputblock
9125                     
9126                 ];
9127
9128         } else {
9129             
9130 //                   Roo.log(" no label && no align");
9131                 cfg.cn = [
9132                     
9133                         inputblock
9134                     
9135                 ];
9136                 
9137                 
9138         }
9139         
9140         if (this.disabled) {
9141             input.disabled=true;
9142         }
9143         
9144         return cfg;
9145         
9146     },
9147     /**
9148      * return the real textarea element.
9149      */
9150     inputEl: function ()
9151     {
9152         return this.el.select('textarea.form-control',true).first();
9153     },
9154     
9155     /**
9156      * Clear any invalid styles/messages for this field
9157      */
9158     clearInvalid : function()
9159     {
9160         
9161         if(!this.el || this.preventMark){ // not rendered
9162             return;
9163         }
9164         
9165         var label = this.el.select('label', true).first();
9166         var icon = this.el.select('i.fa-star', true).first();
9167         
9168         if(label && icon){
9169             icon.remove();
9170         }
9171         
9172         this.el.removeClass(this.invalidClass);
9173         
9174         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9175             
9176             var feedback = this.el.select('.form-control-feedback', true).first();
9177             
9178             if(feedback){
9179                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9180             }
9181             
9182         }
9183         
9184         this.fireEvent('valid', this);
9185     },
9186     
9187      /**
9188      * Mark this field as valid
9189      */
9190     markValid : function()
9191     {
9192         if(!this.el  || this.preventMark){ // not rendered
9193             return;
9194         }
9195         
9196         this.el.removeClass([this.invalidClass, this.validClass]);
9197         
9198         var feedback = this.el.select('.form-control-feedback', true).first();
9199             
9200         if(feedback){
9201             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9202         }
9203
9204         if(this.disabled || this.allowBlank){
9205             return;
9206         }
9207         
9208         var label = this.el.select('label', true).first();
9209         var icon = this.el.select('i.fa-star', true).first();
9210         
9211         if(label && icon){
9212             icon.remove();
9213         }
9214         
9215         this.el.addClass(this.validClass);
9216         
9217         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9218             
9219             var feedback = this.el.select('.form-control-feedback', true).first();
9220             
9221             if(feedback){
9222                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9223                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9224             }
9225             
9226         }
9227         
9228         this.fireEvent('valid', this);
9229     },
9230     
9231      /**
9232      * Mark this field as invalid
9233      * @param {String} msg The validation message
9234      */
9235     markInvalid : function(msg)
9236     {
9237         if(!this.el  || this.preventMark){ // not rendered
9238             return;
9239         }
9240         
9241         this.el.removeClass([this.invalidClass, this.validClass]);
9242         
9243         var feedback = this.el.select('.form-control-feedback', true).first();
9244             
9245         if(feedback){
9246             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9247         }
9248
9249         if(this.disabled || this.allowBlank){
9250             return;
9251         }
9252         
9253         var label = this.el.select('label', true).first();
9254         var icon = this.el.select('i.fa-star', true).first();
9255         
9256         if(!this.getValue().length && label && !icon){
9257             this.el.createChild({
9258                 tag : 'i',
9259                 cls : 'text-danger fa fa-lg fa-star',
9260                 tooltip : 'This field is required',
9261                 style : 'margin-right:5px;'
9262             }, label, true);
9263         }
9264
9265         this.el.addClass(this.invalidClass);
9266         
9267         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9268             
9269             var feedback = this.el.select('.form-control-feedback', true).first();
9270             
9271             if(feedback){
9272                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9273                 
9274                 if(this.getValue().length || this.forceFeedback){
9275                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9276                 }
9277                 
9278             }
9279             
9280         }
9281         
9282         this.fireEvent('invalid', this, msg);
9283     }
9284 });
9285
9286  
9287 /*
9288  * - LGPL
9289  *
9290  * trigger field - base class for combo..
9291  * 
9292  */
9293  
9294 /**
9295  * @class Roo.bootstrap.TriggerField
9296  * @extends Roo.bootstrap.Input
9297  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9298  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9299  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9300  * for which you can provide a custom implementation.  For example:
9301  * <pre><code>
9302 var trigger = new Roo.bootstrap.TriggerField();
9303 trigger.onTriggerClick = myTriggerFn;
9304 trigger.applyTo('my-field');
9305 </code></pre>
9306  *
9307  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9308  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9309  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9310  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9311  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9312
9313  * @constructor
9314  * Create a new TriggerField.
9315  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9316  * to the base TextField)
9317  */
9318 Roo.bootstrap.TriggerField = function(config){
9319     this.mimicing = false;
9320     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9321 };
9322
9323 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9324     /**
9325      * @cfg {String} triggerClass A CSS class to apply to the trigger
9326      */
9327      /**
9328      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9329      */
9330     hideTrigger:false,
9331
9332     /**
9333      * @cfg {Boolean} removable (true|false) special filter default false
9334      */
9335     removable : false,
9336     
9337     /** @cfg {Boolean} grow @hide */
9338     /** @cfg {Number} growMin @hide */
9339     /** @cfg {Number} growMax @hide */
9340
9341     /**
9342      * @hide 
9343      * @method
9344      */
9345     autoSize: Roo.emptyFn,
9346     // private
9347     monitorTab : true,
9348     // private
9349     deferHeight : true,
9350
9351     
9352     actionMode : 'wrap',
9353     
9354     caret : false,
9355     
9356     
9357     getAutoCreate : function(){
9358        
9359         var align = this.labelAlign || this.parentLabelAlign();
9360         
9361         var id = Roo.id();
9362         
9363         var cfg = {
9364             cls: 'form-group' //input-group
9365         };
9366         
9367         
9368         var input =  {
9369             tag: 'input',
9370             id : id,
9371             type : this.inputType,
9372             cls : 'form-control',
9373             autocomplete: 'new-password',
9374             placeholder : this.placeholder || '' 
9375             
9376         };
9377         if (this.name) {
9378             input.name = this.name;
9379         }
9380         if (this.size) {
9381             input.cls += ' input-' + this.size;
9382         }
9383         
9384         if (this.disabled) {
9385             input.disabled=true;
9386         }
9387         
9388         var inputblock = input;
9389         
9390         if(this.hasFeedback && !this.allowBlank){
9391             
9392             var feedback = {
9393                 tag: 'span',
9394                 cls: 'glyphicon form-control-feedback'
9395             };
9396             
9397             if(this.removable && !this.editable && !this.tickable){
9398                 inputblock = {
9399                     cls : 'has-feedback',
9400                     cn :  [
9401                         inputblock,
9402                         {
9403                             tag: 'button',
9404                             html : 'x',
9405                             cls : 'roo-combo-removable-btn close'
9406                         },
9407                         feedback
9408                     ] 
9409                 };
9410             } else {
9411                 inputblock = {
9412                     cls : 'has-feedback',
9413                     cn :  [
9414                         inputblock,
9415                         feedback
9416                     ] 
9417                 };
9418             }
9419
9420         } else {
9421             if(this.removable && !this.editable && !this.tickable){
9422                 inputblock = {
9423                     cls : 'roo-removable',
9424                     cn :  [
9425                         inputblock,
9426                         {
9427                             tag: 'button',
9428                             html : 'x',
9429                             cls : 'roo-combo-removable-btn close'
9430                         }
9431                     ] 
9432                 };
9433             }
9434         }
9435         
9436         if (this.before || this.after) {
9437             
9438             inputblock = {
9439                 cls : 'input-group',
9440                 cn :  [] 
9441             };
9442             if (this.before) {
9443                 inputblock.cn.push({
9444                     tag :'span',
9445                     cls : 'input-group-addon',
9446                     html : this.before
9447                 });
9448             }
9449             
9450             inputblock.cn.push(input);
9451             
9452             if(this.hasFeedback && !this.allowBlank){
9453                 inputblock.cls += ' has-feedback';
9454                 inputblock.cn.push(feedback);
9455             }
9456             
9457             if (this.after) {
9458                 inputblock.cn.push({
9459                     tag :'span',
9460                     cls : 'input-group-addon',
9461                     html : this.after
9462                 });
9463             }
9464             
9465         };
9466         
9467         var box = {
9468             tag: 'div',
9469             cn: [
9470                 {
9471                     tag: 'input',
9472                     type : 'hidden',
9473                     cls: 'form-hidden-field'
9474                 },
9475                 inputblock
9476             ]
9477             
9478         };
9479         
9480         if(this.multiple){
9481             box = {
9482                 tag: 'div',
9483                 cn: [
9484                     {
9485                         tag: 'input',
9486                         type : 'hidden',
9487                         cls: 'form-hidden-field'
9488                     },
9489                     {
9490                         tag: 'ul',
9491                         cls: 'roo-select2-choices',
9492                         cn:[
9493                             {
9494                                 tag: 'li',
9495                                 cls: 'roo-select2-search-field',
9496                                 cn: [
9497
9498                                     inputblock
9499                                 ]
9500                             }
9501                         ]
9502                     }
9503                 ]
9504             }
9505         };
9506         
9507         var combobox = {
9508             cls: 'roo-select2-container input-group',
9509             cn: [
9510                 box
9511 //                {
9512 //                    tag: 'ul',
9513 //                    cls: 'typeahead typeahead-long dropdown-menu',
9514 //                    style: 'display:none'
9515 //                }
9516             ]
9517         };
9518         
9519         if(!this.multiple && this.showToggleBtn){
9520             
9521             var caret = {
9522                         tag: 'span',
9523                         cls: 'caret'
9524              };
9525             if (this.caret != false) {
9526                 caret = {
9527                      tag: 'i',
9528                      cls: 'fa fa-' + this.caret
9529                 };
9530                 
9531             }
9532             
9533             combobox.cn.push({
9534                 tag :'span',
9535                 cls : 'input-group-addon btn dropdown-toggle',
9536                 cn : [
9537                     caret,
9538                     {
9539                         tag: 'span',
9540                         cls: 'combobox-clear',
9541                         cn  : [
9542                             {
9543                                 tag : 'i',
9544                                 cls: 'icon-remove'
9545                             }
9546                         ]
9547                     }
9548                 ]
9549
9550             })
9551         }
9552         
9553         if(this.multiple){
9554             combobox.cls += ' roo-select2-container-multi';
9555         }
9556         
9557         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9558             
9559 //                Roo.log("left and has label");
9560             cfg.cn = [
9561                 {
9562                     tag : 'i',
9563                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9564                     tooltip : 'This field is required'
9565                 },
9566                 {
9567                     tag: 'label',
9568                     'for' :  id,
9569                     cls : 'control-label col-sm-' + this.labelWidth,
9570                     html : this.fieldLabel
9571
9572                 },
9573                 {
9574                     cls : "col-sm-" + (12 - this.labelWidth), 
9575                     cn: [
9576                         combobox
9577                     ]
9578                 }
9579
9580             ];
9581             
9582             if(this.indicatorpos == 'right'){
9583                 cfg.cn = [
9584                     {
9585                         tag: 'label',
9586                         'for' :  id,
9587                         cls : 'control-label col-sm-' + this.labelWidth,
9588                         html : this.fieldLabel
9589
9590                     },
9591                     {
9592                         tag : 'i',
9593                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9594                         tooltip : 'This field is required'
9595                     },
9596                     {
9597                         cls : "col-sm-" + (12 - this.labelWidth), 
9598                         cn: [
9599                             combobox
9600                         ]
9601                     }
9602
9603                 ];
9604             }
9605             
9606         } else if ( this.fieldLabel.length) {
9607 //                Roo.log(" label");
9608             cfg.cn = [
9609                 {
9610                    tag : 'i',
9611                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9612                    tooltip : 'This field is required'
9613                },
9614                {
9615                    tag: 'label',
9616                    //cls : 'input-group-addon',
9617                    html : this.fieldLabel
9618
9619                },
9620
9621                combobox
9622
9623             ];
9624             
9625             if(this.indicatorpos == 'right'){
9626                 
9627                 cfg.cn = [
9628                     {
9629                        tag: 'label',
9630                        //cls : 'input-group-addon',
9631                        html : this.fieldLabel
9632
9633                     },
9634                     {
9635                        tag : 'i',
9636                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9637                        tooltip : 'This field is required'
9638                     },
9639                     
9640                     combobox
9641
9642                 ];
9643
9644             }
9645
9646         } else {
9647             
9648 //                Roo.log(" no label && no align");
9649                 cfg = combobox
9650                      
9651                 
9652         }
9653          
9654         var settings=this;
9655         ['xs','sm','md','lg'].map(function(size){
9656             if (settings[size]) {
9657                 cfg.cls += ' col-' + size + '-' + settings[size];
9658             }
9659         });
9660         
9661         return cfg;
9662         
9663     },
9664     
9665     
9666     
9667     // private
9668     onResize : function(w, h){
9669 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9670 //        if(typeof w == 'number'){
9671 //            var x = w - this.trigger.getWidth();
9672 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9673 //            this.trigger.setStyle('left', x+'px');
9674 //        }
9675     },
9676
9677     // private
9678     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9679
9680     // private
9681     getResizeEl : function(){
9682         return this.inputEl();
9683     },
9684
9685     // private
9686     getPositionEl : function(){
9687         return this.inputEl();
9688     },
9689
9690     // private
9691     alignErrorIcon : function(){
9692         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9693     },
9694
9695     // private
9696     initEvents : function(){
9697         
9698         this.createList();
9699         
9700         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9701         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9702         if(!this.multiple && this.showToggleBtn){
9703             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9704             if(this.hideTrigger){
9705                 this.trigger.setDisplayed(false);
9706             }
9707             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9708         }
9709         
9710         if(this.multiple){
9711             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9712         }
9713         
9714         if(this.removable && !this.editable && !this.tickable){
9715             var close = this.closeTriggerEl();
9716             
9717             if(close){
9718                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9719                 close.on('click', this.removeBtnClick, this, close);
9720             }
9721         }
9722         
9723         //this.trigger.addClassOnOver('x-form-trigger-over');
9724         //this.trigger.addClassOnClick('x-form-trigger-click');
9725         
9726         //if(!this.width){
9727         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9728         //}
9729     },
9730     
9731     closeTriggerEl : function()
9732     {
9733         var close = this.el.select('.roo-combo-removable-btn', true).first();
9734         return close ? close : false;
9735     },
9736     
9737     removeBtnClick : function(e, h, el)
9738     {
9739         e.preventDefault();
9740         
9741         if(this.fireEvent("remove", this) !== false){
9742             this.reset();
9743             this.fireEvent("afterremove", this)
9744         }
9745     },
9746     
9747     createList : function()
9748     {
9749         this.list = Roo.get(document.body).createChild({
9750             tag: 'ul',
9751             cls: 'typeahead typeahead-long dropdown-menu',
9752             style: 'display:none'
9753         });
9754         
9755         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9756         
9757     },
9758
9759     // private
9760     initTrigger : function(){
9761        
9762     },
9763
9764     // private
9765     onDestroy : function(){
9766         if(this.trigger){
9767             this.trigger.removeAllListeners();
9768           //  this.trigger.remove();
9769         }
9770         //if(this.wrap){
9771         //    this.wrap.remove();
9772         //}
9773         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9774     },
9775
9776     // private
9777     onFocus : function(){
9778         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9779         /*
9780         if(!this.mimicing){
9781             this.wrap.addClass('x-trigger-wrap-focus');
9782             this.mimicing = true;
9783             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9784             if(this.monitorTab){
9785                 this.el.on("keydown", this.checkTab, this);
9786             }
9787         }
9788         */
9789     },
9790
9791     // private
9792     checkTab : function(e){
9793         if(e.getKey() == e.TAB){
9794             this.triggerBlur();
9795         }
9796     },
9797
9798     // private
9799     onBlur : function(){
9800         // do nothing
9801     },
9802
9803     // private
9804     mimicBlur : function(e, t){
9805         /*
9806         if(!this.wrap.contains(t) && this.validateBlur()){
9807             this.triggerBlur();
9808         }
9809         */
9810     },
9811
9812     // private
9813     triggerBlur : function(){
9814         this.mimicing = false;
9815         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9816         if(this.monitorTab){
9817             this.el.un("keydown", this.checkTab, this);
9818         }
9819         //this.wrap.removeClass('x-trigger-wrap-focus');
9820         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9821     },
9822
9823     // private
9824     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9825     validateBlur : function(e, t){
9826         return true;
9827     },
9828
9829     // private
9830     onDisable : function(){
9831         this.inputEl().dom.disabled = true;
9832         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9833         //if(this.wrap){
9834         //    this.wrap.addClass('x-item-disabled');
9835         //}
9836     },
9837
9838     // private
9839     onEnable : function(){
9840         this.inputEl().dom.disabled = false;
9841         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9842         //if(this.wrap){
9843         //    this.el.removeClass('x-item-disabled');
9844         //}
9845     },
9846
9847     // private
9848     onShow : function(){
9849         var ae = this.getActionEl();
9850         
9851         if(ae){
9852             ae.dom.style.display = '';
9853             ae.dom.style.visibility = 'visible';
9854         }
9855     },
9856
9857     // private
9858     
9859     onHide : function(){
9860         var ae = this.getActionEl();
9861         ae.dom.style.display = 'none';
9862     },
9863
9864     /**
9865      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9866      * by an implementing function.
9867      * @method
9868      * @param {EventObject} e
9869      */
9870     onTriggerClick : Roo.emptyFn
9871 });
9872  /*
9873  * Based on:
9874  * Ext JS Library 1.1.1
9875  * Copyright(c) 2006-2007, Ext JS, LLC.
9876  *
9877  * Originally Released Under LGPL - original licence link has changed is not relivant.
9878  *
9879  * Fork - LGPL
9880  * <script type="text/javascript">
9881  */
9882
9883
9884 /**
9885  * @class Roo.data.SortTypes
9886  * @singleton
9887  * Defines the default sorting (casting?) comparison functions used when sorting data.
9888  */
9889 Roo.data.SortTypes = {
9890     /**
9891      * Default sort that does nothing
9892      * @param {Mixed} s The value being converted
9893      * @return {Mixed} The comparison value
9894      */
9895     none : function(s){
9896         return s;
9897     },
9898     
9899     /**
9900      * The regular expression used to strip tags
9901      * @type {RegExp}
9902      * @property
9903      */
9904     stripTagsRE : /<\/?[^>]+>/gi,
9905     
9906     /**
9907      * Strips all HTML tags to sort on text only
9908      * @param {Mixed} s The value being converted
9909      * @return {String} The comparison value
9910      */
9911     asText : function(s){
9912         return String(s).replace(this.stripTagsRE, "");
9913     },
9914     
9915     /**
9916      * Strips all HTML tags to sort on text only - Case insensitive
9917      * @param {Mixed} s The value being converted
9918      * @return {String} The comparison value
9919      */
9920     asUCText : function(s){
9921         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9922     },
9923     
9924     /**
9925      * Case insensitive string
9926      * @param {Mixed} s The value being converted
9927      * @return {String} The comparison value
9928      */
9929     asUCString : function(s) {
9930         return String(s).toUpperCase();
9931     },
9932     
9933     /**
9934      * Date sorting
9935      * @param {Mixed} s The value being converted
9936      * @return {Number} The comparison value
9937      */
9938     asDate : function(s) {
9939         if(!s){
9940             return 0;
9941         }
9942         if(s instanceof Date){
9943             return s.getTime();
9944         }
9945         return Date.parse(String(s));
9946     },
9947     
9948     /**
9949      * Float sorting
9950      * @param {Mixed} s The value being converted
9951      * @return {Float} The comparison value
9952      */
9953     asFloat : function(s) {
9954         var val = parseFloat(String(s).replace(/,/g, ""));
9955         if(isNaN(val)) {
9956             val = 0;
9957         }
9958         return val;
9959     },
9960     
9961     /**
9962      * Integer sorting
9963      * @param {Mixed} s The value being converted
9964      * @return {Number} The comparison value
9965      */
9966     asInt : function(s) {
9967         var val = parseInt(String(s).replace(/,/g, ""));
9968         if(isNaN(val)) {
9969             val = 0;
9970         }
9971         return val;
9972     }
9973 };/*
9974  * Based on:
9975  * Ext JS Library 1.1.1
9976  * Copyright(c) 2006-2007, Ext JS, LLC.
9977  *
9978  * Originally Released Under LGPL - original licence link has changed is not relivant.
9979  *
9980  * Fork - LGPL
9981  * <script type="text/javascript">
9982  */
9983
9984 /**
9985 * @class Roo.data.Record
9986  * Instances of this class encapsulate both record <em>definition</em> information, and record
9987  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9988  * to access Records cached in an {@link Roo.data.Store} object.<br>
9989  * <p>
9990  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9991  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9992  * objects.<br>
9993  * <p>
9994  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9995  * @constructor
9996  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9997  * {@link #create}. The parameters are the same.
9998  * @param {Array} data An associative Array of data values keyed by the field name.
9999  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10000  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10001  * not specified an integer id is generated.
10002  */
10003 Roo.data.Record = function(data, id){
10004     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10005     this.data = data;
10006 };
10007
10008 /**
10009  * Generate a constructor for a specific record layout.
10010  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10011  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10012  * Each field definition object may contain the following properties: <ul>
10013  * <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,
10014  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10015  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10016  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10017  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10018  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10019  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10020  * this may be omitted.</p></li>
10021  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10022  * <ul><li>auto (Default, implies no conversion)</li>
10023  * <li>string</li>
10024  * <li>int</li>
10025  * <li>float</li>
10026  * <li>boolean</li>
10027  * <li>date</li></ul></p></li>
10028  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10029  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10030  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10031  * by the Reader into an object that will be stored in the Record. It is passed the
10032  * following parameters:<ul>
10033  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10034  * </ul></p></li>
10035  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10036  * </ul>
10037  * <br>usage:<br><pre><code>
10038 var TopicRecord = Roo.data.Record.create(
10039     {name: 'title', mapping: 'topic_title'},
10040     {name: 'author', mapping: 'username'},
10041     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10042     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10043     {name: 'lastPoster', mapping: 'user2'},
10044     {name: 'excerpt', mapping: 'post_text'}
10045 );
10046
10047 var myNewRecord = new TopicRecord({
10048     title: 'Do my job please',
10049     author: 'noobie',
10050     totalPosts: 1,
10051     lastPost: new Date(),
10052     lastPoster: 'Animal',
10053     excerpt: 'No way dude!'
10054 });
10055 myStore.add(myNewRecord);
10056 </code></pre>
10057  * @method create
10058  * @static
10059  */
10060 Roo.data.Record.create = function(o){
10061     var f = function(){
10062         f.superclass.constructor.apply(this, arguments);
10063     };
10064     Roo.extend(f, Roo.data.Record);
10065     var p = f.prototype;
10066     p.fields = new Roo.util.MixedCollection(false, function(field){
10067         return field.name;
10068     });
10069     for(var i = 0, len = o.length; i < len; i++){
10070         p.fields.add(new Roo.data.Field(o[i]));
10071     }
10072     f.getField = function(name){
10073         return p.fields.get(name);  
10074     };
10075     return f;
10076 };
10077
10078 Roo.data.Record.AUTO_ID = 1000;
10079 Roo.data.Record.EDIT = 'edit';
10080 Roo.data.Record.REJECT = 'reject';
10081 Roo.data.Record.COMMIT = 'commit';
10082
10083 Roo.data.Record.prototype = {
10084     /**
10085      * Readonly flag - true if this record has been modified.
10086      * @type Boolean
10087      */
10088     dirty : false,
10089     editing : false,
10090     error: null,
10091     modified: null,
10092
10093     // private
10094     join : function(store){
10095         this.store = store;
10096     },
10097
10098     /**
10099      * Set the named field to the specified value.
10100      * @param {String} name The name of the field to set.
10101      * @param {Object} value The value to set the field to.
10102      */
10103     set : function(name, value){
10104         if(this.data[name] == value){
10105             return;
10106         }
10107         this.dirty = true;
10108         if(!this.modified){
10109             this.modified = {};
10110         }
10111         if(typeof this.modified[name] == 'undefined'){
10112             this.modified[name] = this.data[name];
10113         }
10114         this.data[name] = value;
10115         if(!this.editing && this.store){
10116             this.store.afterEdit(this);
10117         }       
10118     },
10119
10120     /**
10121      * Get the value of the named field.
10122      * @param {String} name The name of the field to get the value of.
10123      * @return {Object} The value of the field.
10124      */
10125     get : function(name){
10126         return this.data[name]; 
10127     },
10128
10129     // private
10130     beginEdit : function(){
10131         this.editing = true;
10132         this.modified = {}; 
10133     },
10134
10135     // private
10136     cancelEdit : function(){
10137         this.editing = false;
10138         delete this.modified;
10139     },
10140
10141     // private
10142     endEdit : function(){
10143         this.editing = false;
10144         if(this.dirty && this.store){
10145             this.store.afterEdit(this);
10146         }
10147     },
10148
10149     /**
10150      * Usually called by the {@link Roo.data.Store} which owns the Record.
10151      * Rejects all changes made to the Record since either creation, or the last commit operation.
10152      * Modified fields are reverted to their original values.
10153      * <p>
10154      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10155      * of reject operations.
10156      */
10157     reject : function(){
10158         var m = this.modified;
10159         for(var n in m){
10160             if(typeof m[n] != "function"){
10161                 this.data[n] = m[n];
10162             }
10163         }
10164         this.dirty = false;
10165         delete this.modified;
10166         this.editing = false;
10167         if(this.store){
10168             this.store.afterReject(this);
10169         }
10170     },
10171
10172     /**
10173      * Usually called by the {@link Roo.data.Store} which owns the Record.
10174      * Commits all changes made to the Record since either creation, or the last commit operation.
10175      * <p>
10176      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10177      * of commit operations.
10178      */
10179     commit : function(){
10180         this.dirty = false;
10181         delete this.modified;
10182         this.editing = false;
10183         if(this.store){
10184             this.store.afterCommit(this);
10185         }
10186     },
10187
10188     // private
10189     hasError : function(){
10190         return this.error != null;
10191     },
10192
10193     // private
10194     clearError : function(){
10195         this.error = null;
10196     },
10197
10198     /**
10199      * Creates a copy of this record.
10200      * @param {String} id (optional) A new record id if you don't want to use this record's id
10201      * @return {Record}
10202      */
10203     copy : function(newId) {
10204         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10205     }
10206 };/*
10207  * Based on:
10208  * Ext JS Library 1.1.1
10209  * Copyright(c) 2006-2007, Ext JS, LLC.
10210  *
10211  * Originally Released Under LGPL - original licence link has changed is not relivant.
10212  *
10213  * Fork - LGPL
10214  * <script type="text/javascript">
10215  */
10216
10217
10218
10219 /**
10220  * @class Roo.data.Store
10221  * @extends Roo.util.Observable
10222  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10223  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10224  * <p>
10225  * 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
10226  * has no knowledge of the format of the data returned by the Proxy.<br>
10227  * <p>
10228  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10229  * instances from the data object. These records are cached and made available through accessor functions.
10230  * @constructor
10231  * Creates a new Store.
10232  * @param {Object} config A config object containing the objects needed for the Store to access data,
10233  * and read the data into Records.
10234  */
10235 Roo.data.Store = function(config){
10236     this.data = new Roo.util.MixedCollection(false);
10237     this.data.getKey = function(o){
10238         return o.id;
10239     };
10240     this.baseParams = {};
10241     // private
10242     this.paramNames = {
10243         "start" : "start",
10244         "limit" : "limit",
10245         "sort" : "sort",
10246         "dir" : "dir",
10247         "multisort" : "_multisort"
10248     };
10249
10250     if(config && config.data){
10251         this.inlineData = config.data;
10252         delete config.data;
10253     }
10254
10255     Roo.apply(this, config);
10256     
10257     if(this.reader){ // reader passed
10258         this.reader = Roo.factory(this.reader, Roo.data);
10259         this.reader.xmodule = this.xmodule || false;
10260         if(!this.recordType){
10261             this.recordType = this.reader.recordType;
10262         }
10263         if(this.reader.onMetaChange){
10264             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10265         }
10266     }
10267
10268     if(this.recordType){
10269         this.fields = this.recordType.prototype.fields;
10270     }
10271     this.modified = [];
10272
10273     this.addEvents({
10274         /**
10275          * @event datachanged
10276          * Fires when the data cache has changed, and a widget which is using this Store
10277          * as a Record cache should refresh its view.
10278          * @param {Store} this
10279          */
10280         datachanged : true,
10281         /**
10282          * @event metachange
10283          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10284          * @param {Store} this
10285          * @param {Object} meta The JSON metadata
10286          */
10287         metachange : true,
10288         /**
10289          * @event add
10290          * Fires when Records have been added to the Store
10291          * @param {Store} this
10292          * @param {Roo.data.Record[]} records The array of Records added
10293          * @param {Number} index The index at which the record(s) were added
10294          */
10295         add : true,
10296         /**
10297          * @event remove
10298          * Fires when a Record has been removed from the Store
10299          * @param {Store} this
10300          * @param {Roo.data.Record} record The Record that was removed
10301          * @param {Number} index The index at which the record was removed
10302          */
10303         remove : true,
10304         /**
10305          * @event update
10306          * Fires when a Record has been updated
10307          * @param {Store} this
10308          * @param {Roo.data.Record} record The Record that was updated
10309          * @param {String} operation The update operation being performed.  Value may be one of:
10310          * <pre><code>
10311  Roo.data.Record.EDIT
10312  Roo.data.Record.REJECT
10313  Roo.data.Record.COMMIT
10314          * </code></pre>
10315          */
10316         update : true,
10317         /**
10318          * @event clear
10319          * Fires when the data cache has been cleared.
10320          * @param {Store} this
10321          */
10322         clear : true,
10323         /**
10324          * @event beforeload
10325          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10326          * the load action will be canceled.
10327          * @param {Store} this
10328          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10329          */
10330         beforeload : true,
10331         /**
10332          * @event beforeloadadd
10333          * Fires after a new set of Records has been loaded.
10334          * @param {Store} this
10335          * @param {Roo.data.Record[]} records The Records that were loaded
10336          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10337          */
10338         beforeloadadd : true,
10339         /**
10340          * @event load
10341          * Fires after a new set of Records has been loaded, before they are added to the store.
10342          * @param {Store} this
10343          * @param {Roo.data.Record[]} records The Records that were loaded
10344          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10345          * @params {Object} return from reader
10346          */
10347         load : true,
10348         /**
10349          * @event loadexception
10350          * Fires if an exception occurs in the Proxy during loading.
10351          * Called with the signature of the Proxy's "loadexception" event.
10352          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10353          * 
10354          * @param {Proxy} 
10355          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10356          * @param {Object} load options 
10357          * @param {Object} jsonData from your request (normally this contains the Exception)
10358          */
10359         loadexception : true
10360     });
10361     
10362     if(this.proxy){
10363         this.proxy = Roo.factory(this.proxy, Roo.data);
10364         this.proxy.xmodule = this.xmodule || false;
10365         this.relayEvents(this.proxy,  ["loadexception"]);
10366     }
10367     this.sortToggle = {};
10368     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10369
10370     Roo.data.Store.superclass.constructor.call(this);
10371
10372     if(this.inlineData){
10373         this.loadData(this.inlineData);
10374         delete this.inlineData;
10375     }
10376 };
10377
10378 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10379      /**
10380     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10381     * without a remote query - used by combo/forms at present.
10382     */
10383     
10384     /**
10385     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10386     */
10387     /**
10388     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10389     */
10390     /**
10391     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10392     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10393     */
10394     /**
10395     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10396     * on any HTTP request
10397     */
10398     /**
10399     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10400     */
10401     /**
10402     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10403     */
10404     multiSort: false,
10405     /**
10406     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10407     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10408     */
10409     remoteSort : false,
10410
10411     /**
10412     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10413      * loaded or when a record is removed. (defaults to false).
10414     */
10415     pruneModifiedRecords : false,
10416
10417     // private
10418     lastOptions : null,
10419
10420     /**
10421      * Add Records to the Store and fires the add event.
10422      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10423      */
10424     add : function(records){
10425         records = [].concat(records);
10426         for(var i = 0, len = records.length; i < len; i++){
10427             records[i].join(this);
10428         }
10429         var index = this.data.length;
10430         this.data.addAll(records);
10431         this.fireEvent("add", this, records, index);
10432     },
10433
10434     /**
10435      * Remove a Record from the Store and fires the remove event.
10436      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10437      */
10438     remove : function(record){
10439         var index = this.data.indexOf(record);
10440         this.data.removeAt(index);
10441         if(this.pruneModifiedRecords){
10442             this.modified.remove(record);
10443         }
10444         this.fireEvent("remove", this, record, index);
10445     },
10446
10447     /**
10448      * Remove all Records from the Store and fires the clear event.
10449      */
10450     removeAll : function(){
10451         this.data.clear();
10452         if(this.pruneModifiedRecords){
10453             this.modified = [];
10454         }
10455         this.fireEvent("clear", this);
10456     },
10457
10458     /**
10459      * Inserts Records to the Store at the given index and fires the add event.
10460      * @param {Number} index The start index at which to insert the passed Records.
10461      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10462      */
10463     insert : function(index, records){
10464         records = [].concat(records);
10465         for(var i = 0, len = records.length; i < len; i++){
10466             this.data.insert(index, records[i]);
10467             records[i].join(this);
10468         }
10469         this.fireEvent("add", this, records, index);
10470     },
10471
10472     /**
10473      * Get the index within the cache of the passed Record.
10474      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10475      * @return {Number} The index of the passed Record. Returns -1 if not found.
10476      */
10477     indexOf : function(record){
10478         return this.data.indexOf(record);
10479     },
10480
10481     /**
10482      * Get the index within the cache of the Record with the passed id.
10483      * @param {String} id The id of the Record to find.
10484      * @return {Number} The index of the Record. Returns -1 if not found.
10485      */
10486     indexOfId : function(id){
10487         return this.data.indexOfKey(id);
10488     },
10489
10490     /**
10491      * Get the Record with the specified id.
10492      * @param {String} id The id of the Record to find.
10493      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10494      */
10495     getById : function(id){
10496         return this.data.key(id);
10497     },
10498
10499     /**
10500      * Get the Record at the specified index.
10501      * @param {Number} index The index of the Record to find.
10502      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10503      */
10504     getAt : function(index){
10505         return this.data.itemAt(index);
10506     },
10507
10508     /**
10509      * Returns a range of Records between specified indices.
10510      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10511      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10512      * @return {Roo.data.Record[]} An array of Records
10513      */
10514     getRange : function(start, end){
10515         return this.data.getRange(start, end);
10516     },
10517
10518     // private
10519     storeOptions : function(o){
10520         o = Roo.apply({}, o);
10521         delete o.callback;
10522         delete o.scope;
10523         this.lastOptions = o;
10524     },
10525
10526     /**
10527      * Loads the Record cache from the configured Proxy using the configured Reader.
10528      * <p>
10529      * If using remote paging, then the first load call must specify the <em>start</em>
10530      * and <em>limit</em> properties in the options.params property to establish the initial
10531      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10532      * <p>
10533      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10534      * and this call will return before the new data has been loaded. Perform any post-processing
10535      * in a callback function, or in a "load" event handler.</strong>
10536      * <p>
10537      * @param {Object} options An object containing properties which control loading options:<ul>
10538      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10539      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10540      * passed the following arguments:<ul>
10541      * <li>r : Roo.data.Record[]</li>
10542      * <li>options: Options object from the load call</li>
10543      * <li>success: Boolean success indicator</li></ul></li>
10544      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10545      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10546      * </ul>
10547      */
10548     load : function(options){
10549         options = options || {};
10550         if(this.fireEvent("beforeload", this, options) !== false){
10551             this.storeOptions(options);
10552             var p = Roo.apply(options.params || {}, this.baseParams);
10553             // if meta was not loaded from remote source.. try requesting it.
10554             if (!this.reader.metaFromRemote) {
10555                 p._requestMeta = 1;
10556             }
10557             if(this.sortInfo && this.remoteSort){
10558                 var pn = this.paramNames;
10559                 p[pn["sort"]] = this.sortInfo.field;
10560                 p[pn["dir"]] = this.sortInfo.direction;
10561             }
10562             if (this.multiSort) {
10563                 var pn = this.paramNames;
10564                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10565             }
10566             
10567             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10568         }
10569     },
10570
10571     /**
10572      * Reloads the Record cache from the configured Proxy using the configured Reader and
10573      * the options from the last load operation performed.
10574      * @param {Object} options (optional) An object containing properties which may override the options
10575      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10576      * the most recently used options are reused).
10577      */
10578     reload : function(options){
10579         this.load(Roo.applyIf(options||{}, this.lastOptions));
10580     },
10581
10582     // private
10583     // Called as a callback by the Reader during a load operation.
10584     loadRecords : function(o, options, success){
10585         if(!o || success === false){
10586             if(success !== false){
10587                 this.fireEvent("load", this, [], options, o);
10588             }
10589             if(options.callback){
10590                 options.callback.call(options.scope || this, [], options, false);
10591             }
10592             return;
10593         }
10594         // if data returned failure - throw an exception.
10595         if (o.success === false) {
10596             // show a message if no listener is registered.
10597             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10598                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10599             }
10600             // loadmask wil be hooked into this..
10601             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10602             return;
10603         }
10604         var r = o.records, t = o.totalRecords || r.length;
10605         
10606         this.fireEvent("beforeloadadd", this, r, options, o);
10607         
10608         if(!options || options.add !== true){
10609             if(this.pruneModifiedRecords){
10610                 this.modified = [];
10611             }
10612             for(var i = 0, len = r.length; i < len; i++){
10613                 r[i].join(this);
10614             }
10615             if(this.snapshot){
10616                 this.data = this.snapshot;
10617                 delete this.snapshot;
10618             }
10619             this.data.clear();
10620             this.data.addAll(r);
10621             this.totalLength = t;
10622             this.applySort();
10623             this.fireEvent("datachanged", this);
10624         }else{
10625             this.totalLength = Math.max(t, this.data.length+r.length);
10626             this.add(r);
10627         }
10628         this.fireEvent("load", this, r, options, o);
10629         if(options.callback){
10630             options.callback.call(options.scope || this, r, options, true);
10631         }
10632     },
10633
10634
10635     /**
10636      * Loads data from a passed data block. A Reader which understands the format of the data
10637      * must have been configured in the constructor.
10638      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10639      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10640      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10641      */
10642     loadData : function(o, append){
10643         var r = this.reader.readRecords(o);
10644         this.loadRecords(r, {add: append}, true);
10645     },
10646
10647     /**
10648      * Gets the number of cached records.
10649      * <p>
10650      * <em>If using paging, this may not be the total size of the dataset. If the data object
10651      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10652      * the data set size</em>
10653      */
10654     getCount : function(){
10655         return this.data.length || 0;
10656     },
10657
10658     /**
10659      * Gets the total number of records in the dataset as returned by the server.
10660      * <p>
10661      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10662      * the dataset size</em>
10663      */
10664     getTotalCount : function(){
10665         return this.totalLength || 0;
10666     },
10667
10668     /**
10669      * Returns the sort state of the Store as an object with two properties:
10670      * <pre><code>
10671  field {String} The name of the field by which the Records are sorted
10672  direction {String} The sort order, "ASC" or "DESC"
10673      * </code></pre>
10674      */
10675     getSortState : function(){
10676         return this.sortInfo;
10677     },
10678
10679     // private
10680     applySort : function(){
10681         if(this.sortInfo && !this.remoteSort){
10682             var s = this.sortInfo, f = s.field;
10683             var st = this.fields.get(f).sortType;
10684             var fn = function(r1, r2){
10685                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10686                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10687             };
10688             this.data.sort(s.direction, fn);
10689             if(this.snapshot && this.snapshot != this.data){
10690                 this.snapshot.sort(s.direction, fn);
10691             }
10692         }
10693     },
10694
10695     /**
10696      * Sets the default sort column and order to be used by the next load operation.
10697      * @param {String} fieldName The name of the field to sort by.
10698      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10699      */
10700     setDefaultSort : function(field, dir){
10701         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10702     },
10703
10704     /**
10705      * Sort the Records.
10706      * If remote sorting is used, the sort is performed on the server, and the cache is
10707      * reloaded. If local sorting is used, the cache is sorted internally.
10708      * @param {String} fieldName The name of the field to sort by.
10709      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10710      */
10711     sort : function(fieldName, dir){
10712         var f = this.fields.get(fieldName);
10713         if(!dir){
10714             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10715             
10716             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10717                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10718             }else{
10719                 dir = f.sortDir;
10720             }
10721         }
10722         this.sortToggle[f.name] = dir;
10723         this.sortInfo = {field: f.name, direction: dir};
10724         if(!this.remoteSort){
10725             this.applySort();
10726             this.fireEvent("datachanged", this);
10727         }else{
10728             this.load(this.lastOptions);
10729         }
10730     },
10731
10732     /**
10733      * Calls the specified function for each of the Records in the cache.
10734      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10735      * Returning <em>false</em> aborts and exits the iteration.
10736      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10737      */
10738     each : function(fn, scope){
10739         this.data.each(fn, scope);
10740     },
10741
10742     /**
10743      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10744      * (e.g., during paging).
10745      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10746      */
10747     getModifiedRecords : function(){
10748         return this.modified;
10749     },
10750
10751     // private
10752     createFilterFn : function(property, value, anyMatch){
10753         if(!value.exec){ // not a regex
10754             value = String(value);
10755             if(value.length == 0){
10756                 return false;
10757             }
10758             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10759         }
10760         return function(r){
10761             return value.test(r.data[property]);
10762         };
10763     },
10764
10765     /**
10766      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10767      * @param {String} property A field on your records
10768      * @param {Number} start The record index to start at (defaults to 0)
10769      * @param {Number} end The last record index to include (defaults to length - 1)
10770      * @return {Number} The sum
10771      */
10772     sum : function(property, start, end){
10773         var rs = this.data.items, v = 0;
10774         start = start || 0;
10775         end = (end || end === 0) ? end : rs.length-1;
10776
10777         for(var i = start; i <= end; i++){
10778             v += (rs[i].data[property] || 0);
10779         }
10780         return v;
10781     },
10782
10783     /**
10784      * Filter the records by a specified property.
10785      * @param {String} field A field on your records
10786      * @param {String/RegExp} value Either a string that the field
10787      * should start with or a RegExp to test against the field
10788      * @param {Boolean} anyMatch True to match any part not just the beginning
10789      */
10790     filter : function(property, value, anyMatch){
10791         var fn = this.createFilterFn(property, value, anyMatch);
10792         return fn ? this.filterBy(fn) : this.clearFilter();
10793     },
10794
10795     /**
10796      * Filter by a function. The specified function will be called with each
10797      * record in this data source. If the function returns true the record is included,
10798      * otherwise it is filtered.
10799      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10800      * @param {Object} scope (optional) The scope of the function (defaults to this)
10801      */
10802     filterBy : function(fn, scope){
10803         this.snapshot = this.snapshot || this.data;
10804         this.data = this.queryBy(fn, scope||this);
10805         this.fireEvent("datachanged", this);
10806     },
10807
10808     /**
10809      * Query the records by a specified property.
10810      * @param {String} field A field on your records
10811      * @param {String/RegExp} value Either a string that the field
10812      * should start with or a RegExp to test against the field
10813      * @param {Boolean} anyMatch True to match any part not just the beginning
10814      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10815      */
10816     query : function(property, value, anyMatch){
10817         var fn = this.createFilterFn(property, value, anyMatch);
10818         return fn ? this.queryBy(fn) : this.data.clone();
10819     },
10820
10821     /**
10822      * Query by a function. The specified function will be called with each
10823      * record in this data source. If the function returns true the record is included
10824      * in the results.
10825      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10826      * @param {Object} scope (optional) The scope of the function (defaults to this)
10827       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10828      **/
10829     queryBy : function(fn, scope){
10830         var data = this.snapshot || this.data;
10831         return data.filterBy(fn, scope||this);
10832     },
10833
10834     /**
10835      * Collects unique values for a particular dataIndex from this store.
10836      * @param {String} dataIndex The property to collect
10837      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10838      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10839      * @return {Array} An array of the unique values
10840      **/
10841     collect : function(dataIndex, allowNull, bypassFilter){
10842         var d = (bypassFilter === true && this.snapshot) ?
10843                 this.snapshot.items : this.data.items;
10844         var v, sv, r = [], l = {};
10845         for(var i = 0, len = d.length; i < len; i++){
10846             v = d[i].data[dataIndex];
10847             sv = String(v);
10848             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10849                 l[sv] = true;
10850                 r[r.length] = v;
10851             }
10852         }
10853         return r;
10854     },
10855
10856     /**
10857      * Revert to a view of the Record cache with no filtering applied.
10858      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10859      */
10860     clearFilter : function(suppressEvent){
10861         if(this.snapshot && this.snapshot != this.data){
10862             this.data = this.snapshot;
10863             delete this.snapshot;
10864             if(suppressEvent !== true){
10865                 this.fireEvent("datachanged", this);
10866             }
10867         }
10868     },
10869
10870     // private
10871     afterEdit : function(record){
10872         if(this.modified.indexOf(record) == -1){
10873             this.modified.push(record);
10874         }
10875         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10876     },
10877     
10878     // private
10879     afterReject : function(record){
10880         this.modified.remove(record);
10881         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10882     },
10883
10884     // private
10885     afterCommit : function(record){
10886         this.modified.remove(record);
10887         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10888     },
10889
10890     /**
10891      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10892      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10893      */
10894     commitChanges : function(){
10895         var m = this.modified.slice(0);
10896         this.modified = [];
10897         for(var i = 0, len = m.length; i < len; i++){
10898             m[i].commit();
10899         }
10900     },
10901
10902     /**
10903      * Cancel outstanding changes on all changed records.
10904      */
10905     rejectChanges : function(){
10906         var m = this.modified.slice(0);
10907         this.modified = [];
10908         for(var i = 0, len = m.length; i < len; i++){
10909             m[i].reject();
10910         }
10911     },
10912
10913     onMetaChange : function(meta, rtype, o){
10914         this.recordType = rtype;
10915         this.fields = rtype.prototype.fields;
10916         delete this.snapshot;
10917         this.sortInfo = meta.sortInfo || this.sortInfo;
10918         this.modified = [];
10919         this.fireEvent('metachange', this, this.reader.meta);
10920     },
10921     
10922     moveIndex : function(data, type)
10923     {
10924         var index = this.indexOf(data);
10925         
10926         var newIndex = index + type;
10927         
10928         this.remove(data);
10929         
10930         this.insert(newIndex, data);
10931         
10932     }
10933 });/*
10934  * Based on:
10935  * Ext JS Library 1.1.1
10936  * Copyright(c) 2006-2007, Ext JS, LLC.
10937  *
10938  * Originally Released Under LGPL - original licence link has changed is not relivant.
10939  *
10940  * Fork - LGPL
10941  * <script type="text/javascript">
10942  */
10943
10944 /**
10945  * @class Roo.data.SimpleStore
10946  * @extends Roo.data.Store
10947  * Small helper class to make creating Stores from Array data easier.
10948  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10949  * @cfg {Array} fields An array of field definition objects, or field name strings.
10950  * @cfg {Array} data The multi-dimensional array of data
10951  * @constructor
10952  * @param {Object} config
10953  */
10954 Roo.data.SimpleStore = function(config){
10955     Roo.data.SimpleStore.superclass.constructor.call(this, {
10956         isLocal : true,
10957         reader: new Roo.data.ArrayReader({
10958                 id: config.id
10959             },
10960             Roo.data.Record.create(config.fields)
10961         ),
10962         proxy : new Roo.data.MemoryProxy(config.data)
10963     });
10964     this.load();
10965 };
10966 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10967  * Based on:
10968  * Ext JS Library 1.1.1
10969  * Copyright(c) 2006-2007, Ext JS, LLC.
10970  *
10971  * Originally Released Under LGPL - original licence link has changed is not relivant.
10972  *
10973  * Fork - LGPL
10974  * <script type="text/javascript">
10975  */
10976
10977 /**
10978 /**
10979  * @extends Roo.data.Store
10980  * @class Roo.data.JsonStore
10981  * Small helper class to make creating Stores for JSON data easier. <br/>
10982 <pre><code>
10983 var store = new Roo.data.JsonStore({
10984     url: 'get-images.php',
10985     root: 'images',
10986     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10987 });
10988 </code></pre>
10989  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10990  * JsonReader and HttpProxy (unless inline data is provided).</b>
10991  * @cfg {Array} fields An array of field definition objects, or field name strings.
10992  * @constructor
10993  * @param {Object} config
10994  */
10995 Roo.data.JsonStore = function(c){
10996     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10997         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10998         reader: new Roo.data.JsonReader(c, c.fields)
10999     }));
11000 };
11001 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11002  * Based on:
11003  * Ext JS Library 1.1.1
11004  * Copyright(c) 2006-2007, Ext JS, LLC.
11005  *
11006  * Originally Released Under LGPL - original licence link has changed is not relivant.
11007  *
11008  * Fork - LGPL
11009  * <script type="text/javascript">
11010  */
11011
11012  
11013 Roo.data.Field = function(config){
11014     if(typeof config == "string"){
11015         config = {name: config};
11016     }
11017     Roo.apply(this, config);
11018     
11019     if(!this.type){
11020         this.type = "auto";
11021     }
11022     
11023     var st = Roo.data.SortTypes;
11024     // named sortTypes are supported, here we look them up
11025     if(typeof this.sortType == "string"){
11026         this.sortType = st[this.sortType];
11027     }
11028     
11029     // set default sortType for strings and dates
11030     if(!this.sortType){
11031         switch(this.type){
11032             case "string":
11033                 this.sortType = st.asUCString;
11034                 break;
11035             case "date":
11036                 this.sortType = st.asDate;
11037                 break;
11038             default:
11039                 this.sortType = st.none;
11040         }
11041     }
11042
11043     // define once
11044     var stripRe = /[\$,%]/g;
11045
11046     // prebuilt conversion function for this field, instead of
11047     // switching every time we're reading a value
11048     if(!this.convert){
11049         var cv, dateFormat = this.dateFormat;
11050         switch(this.type){
11051             case "":
11052             case "auto":
11053             case undefined:
11054                 cv = function(v){ return v; };
11055                 break;
11056             case "string":
11057                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11058                 break;
11059             case "int":
11060                 cv = function(v){
11061                     return v !== undefined && v !== null && v !== '' ?
11062                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11063                     };
11064                 break;
11065             case "float":
11066                 cv = function(v){
11067                     return v !== undefined && v !== null && v !== '' ?
11068                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11069                     };
11070                 break;
11071             case "bool":
11072             case "boolean":
11073                 cv = function(v){ return v === true || v === "true" || v == 1; };
11074                 break;
11075             case "date":
11076                 cv = function(v){
11077                     if(!v){
11078                         return '';
11079                     }
11080                     if(v instanceof Date){
11081                         return v;
11082                     }
11083                     if(dateFormat){
11084                         if(dateFormat == "timestamp"){
11085                             return new Date(v*1000);
11086                         }
11087                         return Date.parseDate(v, dateFormat);
11088                     }
11089                     var parsed = Date.parse(v);
11090                     return parsed ? new Date(parsed) : null;
11091                 };
11092              break;
11093             
11094         }
11095         this.convert = cv;
11096     }
11097 };
11098
11099 Roo.data.Field.prototype = {
11100     dateFormat: null,
11101     defaultValue: "",
11102     mapping: null,
11103     sortType : null,
11104     sortDir : "ASC"
11105 };/*
11106  * Based on:
11107  * Ext JS Library 1.1.1
11108  * Copyright(c) 2006-2007, Ext JS, LLC.
11109  *
11110  * Originally Released Under LGPL - original licence link has changed is not relivant.
11111  *
11112  * Fork - LGPL
11113  * <script type="text/javascript">
11114  */
11115  
11116 // Base class for reading structured data from a data source.  This class is intended to be
11117 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11118
11119 /**
11120  * @class Roo.data.DataReader
11121  * Base class for reading structured data from a data source.  This class is intended to be
11122  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11123  */
11124
11125 Roo.data.DataReader = function(meta, recordType){
11126     
11127     this.meta = meta;
11128     
11129     this.recordType = recordType instanceof Array ? 
11130         Roo.data.Record.create(recordType) : recordType;
11131 };
11132
11133 Roo.data.DataReader.prototype = {
11134      /**
11135      * Create an empty record
11136      * @param {Object} data (optional) - overlay some values
11137      * @return {Roo.data.Record} record created.
11138      */
11139     newRow :  function(d) {
11140         var da =  {};
11141         this.recordType.prototype.fields.each(function(c) {
11142             switch( c.type) {
11143                 case 'int' : da[c.name] = 0; break;
11144                 case 'date' : da[c.name] = new Date(); break;
11145                 case 'float' : da[c.name] = 0.0; break;
11146                 case 'boolean' : da[c.name] = false; break;
11147                 default : da[c.name] = ""; break;
11148             }
11149             
11150         });
11151         return new this.recordType(Roo.apply(da, d));
11152     }
11153     
11154 };/*
11155  * Based on:
11156  * Ext JS Library 1.1.1
11157  * Copyright(c) 2006-2007, Ext JS, LLC.
11158  *
11159  * Originally Released Under LGPL - original licence link has changed is not relivant.
11160  *
11161  * Fork - LGPL
11162  * <script type="text/javascript">
11163  */
11164
11165 /**
11166  * @class Roo.data.DataProxy
11167  * @extends Roo.data.Observable
11168  * This class is an abstract base class for implementations which provide retrieval of
11169  * unformatted data objects.<br>
11170  * <p>
11171  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11172  * (of the appropriate type which knows how to parse the data object) to provide a block of
11173  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11174  * <p>
11175  * Custom implementations must implement the load method as described in
11176  * {@link Roo.data.HttpProxy#load}.
11177  */
11178 Roo.data.DataProxy = function(){
11179     this.addEvents({
11180         /**
11181          * @event beforeload
11182          * Fires before a network request is made to retrieve a data object.
11183          * @param {Object} This DataProxy object.
11184          * @param {Object} params The params parameter to the load function.
11185          */
11186         beforeload : true,
11187         /**
11188          * @event load
11189          * Fires before the load method's callback is called.
11190          * @param {Object} This DataProxy object.
11191          * @param {Object} o The data object.
11192          * @param {Object} arg The callback argument object passed to the load function.
11193          */
11194         load : true,
11195         /**
11196          * @event loadexception
11197          * Fires if an Exception occurs during data retrieval.
11198          * @param {Object} This DataProxy object.
11199          * @param {Object} o The data object.
11200          * @param {Object} arg The callback argument object passed to the load function.
11201          * @param {Object} e The Exception.
11202          */
11203         loadexception : true
11204     });
11205     Roo.data.DataProxy.superclass.constructor.call(this);
11206 };
11207
11208 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11209
11210     /**
11211      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11212      */
11213 /*
11214  * Based on:
11215  * Ext JS Library 1.1.1
11216  * Copyright(c) 2006-2007, Ext JS, LLC.
11217  *
11218  * Originally Released Under LGPL - original licence link has changed is not relivant.
11219  *
11220  * Fork - LGPL
11221  * <script type="text/javascript">
11222  */
11223 /**
11224  * @class Roo.data.MemoryProxy
11225  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11226  * to the Reader when its load method is called.
11227  * @constructor
11228  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11229  */
11230 Roo.data.MemoryProxy = function(data){
11231     if (data.data) {
11232         data = data.data;
11233     }
11234     Roo.data.MemoryProxy.superclass.constructor.call(this);
11235     this.data = data;
11236 };
11237
11238 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11239     
11240     /**
11241      * Load data from the requested source (in this case an in-memory
11242      * data object passed to the constructor), read the data object into
11243      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11244      * process that block using the passed callback.
11245      * @param {Object} params This parameter is not used by the MemoryProxy class.
11246      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11247      * object into a block of Roo.data.Records.
11248      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11249      * The function must be passed <ul>
11250      * <li>The Record block object</li>
11251      * <li>The "arg" argument from the load function</li>
11252      * <li>A boolean success indicator</li>
11253      * </ul>
11254      * @param {Object} scope The scope in which to call the callback
11255      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11256      */
11257     load : function(params, reader, callback, scope, arg){
11258         params = params || {};
11259         var result;
11260         try {
11261             result = reader.readRecords(this.data);
11262         }catch(e){
11263             this.fireEvent("loadexception", this, arg, null, e);
11264             callback.call(scope, null, arg, false);
11265             return;
11266         }
11267         callback.call(scope, result, arg, true);
11268     },
11269     
11270     // private
11271     update : function(params, records){
11272         
11273     }
11274 });/*
11275  * Based on:
11276  * Ext JS Library 1.1.1
11277  * Copyright(c) 2006-2007, Ext JS, LLC.
11278  *
11279  * Originally Released Under LGPL - original licence link has changed is not relivant.
11280  *
11281  * Fork - LGPL
11282  * <script type="text/javascript">
11283  */
11284 /**
11285  * @class Roo.data.HttpProxy
11286  * @extends Roo.data.DataProxy
11287  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11288  * configured to reference a certain URL.<br><br>
11289  * <p>
11290  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11291  * from which the running page was served.<br><br>
11292  * <p>
11293  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11294  * <p>
11295  * Be aware that to enable the browser to parse an XML document, the server must set
11296  * the Content-Type header in the HTTP response to "text/xml".
11297  * @constructor
11298  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11299  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11300  * will be used to make the request.
11301  */
11302 Roo.data.HttpProxy = function(conn){
11303     Roo.data.HttpProxy.superclass.constructor.call(this);
11304     // is conn a conn config or a real conn?
11305     this.conn = conn;
11306     this.useAjax = !conn || !conn.events;
11307   
11308 };
11309
11310 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11311     // thse are take from connection...
11312     
11313     /**
11314      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11315      */
11316     /**
11317      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11318      * extra parameters to each request made by this object. (defaults to undefined)
11319      */
11320     /**
11321      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11322      *  to each request made by this object. (defaults to undefined)
11323      */
11324     /**
11325      * @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)
11326      */
11327     /**
11328      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11329      */
11330      /**
11331      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11332      * @type Boolean
11333      */
11334   
11335
11336     /**
11337      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11338      * @type Boolean
11339      */
11340     /**
11341      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11342      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11343      * a finer-grained basis than the DataProxy events.
11344      */
11345     getConnection : function(){
11346         return this.useAjax ? Roo.Ajax : this.conn;
11347     },
11348
11349     /**
11350      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11351      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11352      * process that block using the passed callback.
11353      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11354      * for the request to the remote server.
11355      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11356      * object into a block of Roo.data.Records.
11357      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11358      * The function must be passed <ul>
11359      * <li>The Record block object</li>
11360      * <li>The "arg" argument from the load function</li>
11361      * <li>A boolean success indicator</li>
11362      * </ul>
11363      * @param {Object} scope The scope in which to call the callback
11364      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11365      */
11366     load : function(params, reader, callback, scope, arg){
11367         if(this.fireEvent("beforeload", this, params) !== false){
11368             var  o = {
11369                 params : params || {},
11370                 request: {
11371                     callback : callback,
11372                     scope : scope,
11373                     arg : arg
11374                 },
11375                 reader: reader,
11376                 callback : this.loadResponse,
11377                 scope: this
11378             };
11379             if(this.useAjax){
11380                 Roo.applyIf(o, this.conn);
11381                 if(this.activeRequest){
11382                     Roo.Ajax.abort(this.activeRequest);
11383                 }
11384                 this.activeRequest = Roo.Ajax.request(o);
11385             }else{
11386                 this.conn.request(o);
11387             }
11388         }else{
11389             callback.call(scope||this, null, arg, false);
11390         }
11391     },
11392
11393     // private
11394     loadResponse : function(o, success, response){
11395         delete this.activeRequest;
11396         if(!success){
11397             this.fireEvent("loadexception", this, o, response);
11398             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11399             return;
11400         }
11401         var result;
11402         try {
11403             result = o.reader.read(response);
11404         }catch(e){
11405             this.fireEvent("loadexception", this, o, response, e);
11406             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11407             return;
11408         }
11409         
11410         this.fireEvent("load", this, o, o.request.arg);
11411         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11412     },
11413
11414     // private
11415     update : function(dataSet){
11416
11417     },
11418
11419     // private
11420     updateResponse : function(dataSet){
11421
11422     }
11423 });/*
11424  * Based on:
11425  * Ext JS Library 1.1.1
11426  * Copyright(c) 2006-2007, Ext JS, LLC.
11427  *
11428  * Originally Released Under LGPL - original licence link has changed is not relivant.
11429  *
11430  * Fork - LGPL
11431  * <script type="text/javascript">
11432  */
11433
11434 /**
11435  * @class Roo.data.ScriptTagProxy
11436  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11437  * other than the originating domain of the running page.<br><br>
11438  * <p>
11439  * <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
11440  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11441  * <p>
11442  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11443  * source code that is used as the source inside a &lt;script> tag.<br><br>
11444  * <p>
11445  * In order for the browser to process the returned data, the server must wrap the data object
11446  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11447  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11448  * depending on whether the callback name was passed:
11449  * <p>
11450  * <pre><code>
11451 boolean scriptTag = false;
11452 String cb = request.getParameter("callback");
11453 if (cb != null) {
11454     scriptTag = true;
11455     response.setContentType("text/javascript");
11456 } else {
11457     response.setContentType("application/x-json");
11458 }
11459 Writer out = response.getWriter();
11460 if (scriptTag) {
11461     out.write(cb + "(");
11462 }
11463 out.print(dataBlock.toJsonString());
11464 if (scriptTag) {
11465     out.write(");");
11466 }
11467 </pre></code>
11468  *
11469  * @constructor
11470  * @param {Object} config A configuration object.
11471  */
11472 Roo.data.ScriptTagProxy = function(config){
11473     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11474     Roo.apply(this, config);
11475     this.head = document.getElementsByTagName("head")[0];
11476 };
11477
11478 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11479
11480 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11481     /**
11482      * @cfg {String} url The URL from which to request the data object.
11483      */
11484     /**
11485      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11486      */
11487     timeout : 30000,
11488     /**
11489      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11490      * the server the name of the callback function set up by the load call to process the returned data object.
11491      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11492      * javascript output which calls this named function passing the data object as its only parameter.
11493      */
11494     callbackParam : "callback",
11495     /**
11496      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11497      * name to the request.
11498      */
11499     nocache : true,
11500
11501     /**
11502      * Load data from the configured URL, read the data object into
11503      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11504      * process that block using the passed callback.
11505      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11506      * for the request to the remote server.
11507      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11508      * object into a block of Roo.data.Records.
11509      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11510      * The function must be passed <ul>
11511      * <li>The Record block object</li>
11512      * <li>The "arg" argument from the load function</li>
11513      * <li>A boolean success indicator</li>
11514      * </ul>
11515      * @param {Object} scope The scope in which to call the callback
11516      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11517      */
11518     load : function(params, reader, callback, scope, arg){
11519         if(this.fireEvent("beforeload", this, params) !== false){
11520
11521             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11522
11523             var url = this.url;
11524             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11525             if(this.nocache){
11526                 url += "&_dc=" + (new Date().getTime());
11527             }
11528             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11529             var trans = {
11530                 id : transId,
11531                 cb : "stcCallback"+transId,
11532                 scriptId : "stcScript"+transId,
11533                 params : params,
11534                 arg : arg,
11535                 url : url,
11536                 callback : callback,
11537                 scope : scope,
11538                 reader : reader
11539             };
11540             var conn = this;
11541
11542             window[trans.cb] = function(o){
11543                 conn.handleResponse(o, trans);
11544             };
11545
11546             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11547
11548             if(this.autoAbort !== false){
11549                 this.abort();
11550             }
11551
11552             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11553
11554             var script = document.createElement("script");
11555             script.setAttribute("src", url);
11556             script.setAttribute("type", "text/javascript");
11557             script.setAttribute("id", trans.scriptId);
11558             this.head.appendChild(script);
11559
11560             this.trans = trans;
11561         }else{
11562             callback.call(scope||this, null, arg, false);
11563         }
11564     },
11565
11566     // private
11567     isLoading : function(){
11568         return this.trans ? true : false;
11569     },
11570
11571     /**
11572      * Abort the current server request.
11573      */
11574     abort : function(){
11575         if(this.isLoading()){
11576             this.destroyTrans(this.trans);
11577         }
11578     },
11579
11580     // private
11581     destroyTrans : function(trans, isLoaded){
11582         this.head.removeChild(document.getElementById(trans.scriptId));
11583         clearTimeout(trans.timeoutId);
11584         if(isLoaded){
11585             window[trans.cb] = undefined;
11586             try{
11587                 delete window[trans.cb];
11588             }catch(e){}
11589         }else{
11590             // if hasn't been loaded, wait for load to remove it to prevent script error
11591             window[trans.cb] = function(){
11592                 window[trans.cb] = undefined;
11593                 try{
11594                     delete window[trans.cb];
11595                 }catch(e){}
11596             };
11597         }
11598     },
11599
11600     // private
11601     handleResponse : function(o, trans){
11602         this.trans = false;
11603         this.destroyTrans(trans, true);
11604         var result;
11605         try {
11606             result = trans.reader.readRecords(o);
11607         }catch(e){
11608             this.fireEvent("loadexception", this, o, trans.arg, e);
11609             trans.callback.call(trans.scope||window, null, trans.arg, false);
11610             return;
11611         }
11612         this.fireEvent("load", this, o, trans.arg);
11613         trans.callback.call(trans.scope||window, result, trans.arg, true);
11614     },
11615
11616     // private
11617     handleFailure : function(trans){
11618         this.trans = false;
11619         this.destroyTrans(trans, false);
11620         this.fireEvent("loadexception", this, null, trans.arg);
11621         trans.callback.call(trans.scope||window, null, trans.arg, false);
11622     }
11623 });/*
11624  * Based on:
11625  * Ext JS Library 1.1.1
11626  * Copyright(c) 2006-2007, Ext JS, LLC.
11627  *
11628  * Originally Released Under LGPL - original licence link has changed is not relivant.
11629  *
11630  * Fork - LGPL
11631  * <script type="text/javascript">
11632  */
11633
11634 /**
11635  * @class Roo.data.JsonReader
11636  * @extends Roo.data.DataReader
11637  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11638  * based on mappings in a provided Roo.data.Record constructor.
11639  * 
11640  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11641  * in the reply previously. 
11642  * 
11643  * <p>
11644  * Example code:
11645  * <pre><code>
11646 var RecordDef = Roo.data.Record.create([
11647     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11648     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11649 ]);
11650 var myReader = new Roo.data.JsonReader({
11651     totalProperty: "results",    // The property which contains the total dataset size (optional)
11652     root: "rows",                // The property which contains an Array of row objects
11653     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11654 }, RecordDef);
11655 </code></pre>
11656  * <p>
11657  * This would consume a JSON file like this:
11658  * <pre><code>
11659 { 'results': 2, 'rows': [
11660     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11661     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11662 }
11663 </code></pre>
11664  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11665  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11666  * paged from the remote server.
11667  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11668  * @cfg {String} root name of the property which contains the Array of row objects.
11669  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11670  * @cfg {Array} fields Array of field definition objects
11671  * @constructor
11672  * Create a new JsonReader
11673  * @param {Object} meta Metadata configuration options
11674  * @param {Object} recordType Either an Array of field definition objects,
11675  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11676  */
11677 Roo.data.JsonReader = function(meta, recordType){
11678     
11679     meta = meta || {};
11680     // set some defaults:
11681     Roo.applyIf(meta, {
11682         totalProperty: 'total',
11683         successProperty : 'success',
11684         root : 'data',
11685         id : 'id'
11686     });
11687     
11688     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11689 };
11690 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11691     
11692     /**
11693      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11694      * Used by Store query builder to append _requestMeta to params.
11695      * 
11696      */
11697     metaFromRemote : false,
11698     /**
11699      * This method is only used by a DataProxy which has retrieved data from a remote server.
11700      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11701      * @return {Object} data A data block which is used by an Roo.data.Store object as
11702      * a cache of Roo.data.Records.
11703      */
11704     read : function(response){
11705         var json = response.responseText;
11706        
11707         var o = /* eval:var:o */ eval("("+json+")");
11708         if(!o) {
11709             throw {message: "JsonReader.read: Json object not found"};
11710         }
11711         
11712         if(o.metaData){
11713             
11714             delete this.ef;
11715             this.metaFromRemote = true;
11716             this.meta = o.metaData;
11717             this.recordType = Roo.data.Record.create(o.metaData.fields);
11718             this.onMetaChange(this.meta, this.recordType, o);
11719         }
11720         return this.readRecords(o);
11721     },
11722
11723     // private function a store will implement
11724     onMetaChange : function(meta, recordType, o){
11725
11726     },
11727
11728     /**
11729          * @ignore
11730          */
11731     simpleAccess: function(obj, subsc) {
11732         return obj[subsc];
11733     },
11734
11735         /**
11736          * @ignore
11737          */
11738     getJsonAccessor: function(){
11739         var re = /[\[\.]/;
11740         return function(expr) {
11741             try {
11742                 return(re.test(expr))
11743                     ? new Function("obj", "return obj." + expr)
11744                     : function(obj){
11745                         return obj[expr];
11746                     };
11747             } catch(e){}
11748             return Roo.emptyFn;
11749         };
11750     }(),
11751
11752     /**
11753      * Create a data block containing Roo.data.Records from an XML document.
11754      * @param {Object} o An object which contains an Array of row objects in the property specified
11755      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11756      * which contains the total size of the dataset.
11757      * @return {Object} data A data block which is used by an Roo.data.Store object as
11758      * a cache of Roo.data.Records.
11759      */
11760     readRecords : function(o){
11761         /**
11762          * After any data loads, the raw JSON data is available for further custom processing.
11763          * @type Object
11764          */
11765         this.o = o;
11766         var s = this.meta, Record = this.recordType,
11767             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11768
11769 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11770         if (!this.ef) {
11771             if(s.totalProperty) {
11772                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11773                 }
11774                 if(s.successProperty) {
11775                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11776                 }
11777                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11778                 if (s.id) {
11779                         var g = this.getJsonAccessor(s.id);
11780                         this.getId = function(rec) {
11781                                 var r = g(rec);  
11782                                 return (r === undefined || r === "") ? null : r;
11783                         };
11784                 } else {
11785                         this.getId = function(){return null;};
11786                 }
11787             this.ef = [];
11788             for(var jj = 0; jj < fl; jj++){
11789                 f = fi[jj];
11790                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11791                 this.ef[jj] = this.getJsonAccessor(map);
11792             }
11793         }
11794
11795         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11796         if(s.totalProperty){
11797             var vt = parseInt(this.getTotal(o), 10);
11798             if(!isNaN(vt)){
11799                 totalRecords = vt;
11800             }
11801         }
11802         if(s.successProperty){
11803             var vs = this.getSuccess(o);
11804             if(vs === false || vs === 'false'){
11805                 success = false;
11806             }
11807         }
11808         var records = [];
11809         for(var i = 0; i < c; i++){
11810                 var n = root[i];
11811             var values = {};
11812             var id = this.getId(n);
11813             for(var j = 0; j < fl; j++){
11814                 f = fi[j];
11815             var v = this.ef[j](n);
11816             if (!f.convert) {
11817                 Roo.log('missing convert for ' + f.name);
11818                 Roo.log(f);
11819                 continue;
11820             }
11821             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11822             }
11823             var record = new Record(values, id);
11824             record.json = n;
11825             records[i] = record;
11826         }
11827         return {
11828             raw : o,
11829             success : success,
11830             records : records,
11831             totalRecords : totalRecords
11832         };
11833     }
11834 });/*
11835  * Based on:
11836  * Ext JS Library 1.1.1
11837  * Copyright(c) 2006-2007, Ext JS, LLC.
11838  *
11839  * Originally Released Under LGPL - original licence link has changed is not relivant.
11840  *
11841  * Fork - LGPL
11842  * <script type="text/javascript">
11843  */
11844
11845 /**
11846  * @class Roo.data.ArrayReader
11847  * @extends Roo.data.DataReader
11848  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11849  * Each element of that Array represents a row of data fields. The
11850  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11851  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11852  * <p>
11853  * Example code:.
11854  * <pre><code>
11855 var RecordDef = Roo.data.Record.create([
11856     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11857     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11858 ]);
11859 var myReader = new Roo.data.ArrayReader({
11860     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11861 }, RecordDef);
11862 </code></pre>
11863  * <p>
11864  * This would consume an Array like this:
11865  * <pre><code>
11866 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11867   </code></pre>
11868  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11869  * @constructor
11870  * Create a new JsonReader
11871  * @param {Object} meta Metadata configuration options.
11872  * @param {Object} recordType Either an Array of field definition objects
11873  * as specified to {@link Roo.data.Record#create},
11874  * or an {@link Roo.data.Record} object
11875  * created using {@link Roo.data.Record#create}.
11876  */
11877 Roo.data.ArrayReader = function(meta, recordType){
11878     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11879 };
11880
11881 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11882     /**
11883      * Create a data block containing Roo.data.Records from an XML document.
11884      * @param {Object} o An Array of row objects which represents the dataset.
11885      * @return {Object} data A data block which is used by an Roo.data.Store object as
11886      * a cache of Roo.data.Records.
11887      */
11888     readRecords : function(o){
11889         var sid = this.meta ? this.meta.id : null;
11890         var recordType = this.recordType, fields = recordType.prototype.fields;
11891         var records = [];
11892         var root = o;
11893             for(var i = 0; i < root.length; i++){
11894                     var n = root[i];
11895                 var values = {};
11896                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11897                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11898                 var f = fields.items[j];
11899                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11900                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11901                 v = f.convert(v);
11902                 values[f.name] = v;
11903             }
11904                 var record = new recordType(values, id);
11905                 record.json = n;
11906                 records[records.length] = record;
11907             }
11908             return {
11909                 records : records,
11910                 totalRecords : records.length
11911             };
11912     }
11913 });/*
11914  * - LGPL
11915  * * 
11916  */
11917
11918 /**
11919  * @class Roo.bootstrap.ComboBox
11920  * @extends Roo.bootstrap.TriggerField
11921  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11922  * @cfg {Boolean} append (true|false) default false
11923  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11924  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11925  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11926  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11927  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11928  * @cfg {Boolean} animate default true
11929  * @cfg {Boolean} emptyResultText only for touch device
11930  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11931  * @constructor
11932  * Create a new ComboBox.
11933  * @param {Object} config Configuration options
11934  */
11935 Roo.bootstrap.ComboBox = function(config){
11936     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11937     this.addEvents({
11938         /**
11939          * @event expand
11940          * Fires when the dropdown list is expanded
11941              * @param {Roo.bootstrap.ComboBox} combo This combo box
11942              */
11943         'expand' : true,
11944         /**
11945          * @event collapse
11946          * Fires when the dropdown list is collapsed
11947              * @param {Roo.bootstrap.ComboBox} combo This combo box
11948              */
11949         'collapse' : true,
11950         /**
11951          * @event beforeselect
11952          * Fires before a list item is selected. Return false to cancel the selection.
11953              * @param {Roo.bootstrap.ComboBox} combo This combo box
11954              * @param {Roo.data.Record} record The data record returned from the underlying store
11955              * @param {Number} index The index of the selected item in the dropdown list
11956              */
11957         'beforeselect' : true,
11958         /**
11959          * @event select
11960          * Fires when a list item is selected
11961              * @param {Roo.bootstrap.ComboBox} combo This combo box
11962              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11963              * @param {Number} index The index of the selected item in the dropdown list
11964              */
11965         'select' : true,
11966         /**
11967          * @event beforequery
11968          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11969          * The event object passed has these properties:
11970              * @param {Roo.bootstrap.ComboBox} combo This combo box
11971              * @param {String} query The query
11972              * @param {Boolean} forceAll true to force "all" query
11973              * @param {Boolean} cancel true to cancel the query
11974              * @param {Object} e The query event object
11975              */
11976         'beforequery': true,
11977          /**
11978          * @event add
11979          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11980              * @param {Roo.bootstrap.ComboBox} combo This combo box
11981              */
11982         'add' : true,
11983         /**
11984          * @event edit
11985          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11986              * @param {Roo.bootstrap.ComboBox} combo This combo box
11987              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11988              */
11989         'edit' : true,
11990         /**
11991          * @event remove
11992          * Fires when the remove value from the combobox array
11993              * @param {Roo.bootstrap.ComboBox} combo This combo box
11994              */
11995         'remove' : true,
11996         /**
11997          * @event afterremove
11998          * Fires when the remove value from the combobox array
11999              * @param {Roo.bootstrap.ComboBox} combo This combo box
12000              */
12001         'afterremove' : true,
12002         /**
12003          * @event specialfilter
12004          * Fires when specialfilter
12005             * @param {Roo.bootstrap.ComboBox} combo This combo box
12006             */
12007         'specialfilter' : true,
12008         /**
12009          * @event tick
12010          * Fires when tick the element
12011             * @param {Roo.bootstrap.ComboBox} combo This combo box
12012             */
12013         'tick' : true,
12014         /**
12015          * @event touchviewdisplay
12016          * Fires when touch view require special display (default is using displayField)
12017             * @param {Roo.bootstrap.ComboBox} combo This combo box
12018             * @param {Object} cfg set html .
12019             */
12020         'touchviewdisplay' : true
12021         
12022     });
12023     
12024     this.item = [];
12025     this.tickItems = [];
12026     
12027     this.selectedIndex = -1;
12028     if(this.mode == 'local'){
12029         if(config.queryDelay === undefined){
12030             this.queryDelay = 10;
12031         }
12032         if(config.minChars === undefined){
12033             this.minChars = 0;
12034         }
12035     }
12036 };
12037
12038 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12039      
12040     /**
12041      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12042      * rendering into an Roo.Editor, defaults to false)
12043      */
12044     /**
12045      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12046      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12047      */
12048     /**
12049      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12050      */
12051     /**
12052      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12053      * the dropdown list (defaults to undefined, with no header element)
12054      */
12055
12056      /**
12057      * @cfg {String/Roo.Template} tpl The template to use to render the output
12058      */
12059      
12060      /**
12061      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12062      */
12063     listWidth: undefined,
12064     /**
12065      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12066      * mode = 'remote' or 'text' if mode = 'local')
12067      */
12068     displayField: undefined,
12069     
12070     /**
12071      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12072      * mode = 'remote' or 'value' if mode = 'local'). 
12073      * Note: use of a valueField requires the user make a selection
12074      * in order for a value to be mapped.
12075      */
12076     valueField: undefined,
12077     /**
12078      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12079      */
12080     modalTitle : '',
12081     
12082     /**
12083      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12084      * field's data value (defaults to the underlying DOM element's name)
12085      */
12086     hiddenName: undefined,
12087     /**
12088      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12089      */
12090     listClass: '',
12091     /**
12092      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12093      */
12094     selectedClass: 'active',
12095     
12096     /**
12097      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12098      */
12099     shadow:'sides',
12100     /**
12101      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12102      * anchor positions (defaults to 'tl-bl')
12103      */
12104     listAlign: 'tl-bl?',
12105     /**
12106      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12107      */
12108     maxHeight: 300,
12109     /**
12110      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12111      * query specified by the allQuery config option (defaults to 'query')
12112      */
12113     triggerAction: 'query',
12114     /**
12115      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12116      * (defaults to 4, does not apply if editable = false)
12117      */
12118     minChars : 4,
12119     /**
12120      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12121      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12122      */
12123     typeAhead: false,
12124     /**
12125      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12126      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12127      */
12128     queryDelay: 500,
12129     /**
12130      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12131      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12132      */
12133     pageSize: 0,
12134     /**
12135      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12136      * when editable = true (defaults to false)
12137      */
12138     selectOnFocus:false,
12139     /**
12140      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12141      */
12142     queryParam: 'query',
12143     /**
12144      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12145      * when mode = 'remote' (defaults to 'Loading...')
12146      */
12147     loadingText: 'Loading...',
12148     /**
12149      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12150      */
12151     resizable: false,
12152     /**
12153      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12154      */
12155     handleHeight : 8,
12156     /**
12157      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12158      * traditional select (defaults to true)
12159      */
12160     editable: true,
12161     /**
12162      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12163      */
12164     allQuery: '',
12165     /**
12166      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12167      */
12168     mode: 'remote',
12169     /**
12170      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12171      * listWidth has a higher value)
12172      */
12173     minListWidth : 70,
12174     /**
12175      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12176      * allow the user to set arbitrary text into the field (defaults to false)
12177      */
12178     forceSelection:false,
12179     /**
12180      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12181      * if typeAhead = true (defaults to 250)
12182      */
12183     typeAheadDelay : 250,
12184     /**
12185      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12186      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12187      */
12188     valueNotFoundText : undefined,
12189     /**
12190      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12191      */
12192     blockFocus : false,
12193     
12194     /**
12195      * @cfg {Boolean} disableClear Disable showing of clear button.
12196      */
12197     disableClear : false,
12198     /**
12199      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12200      */
12201     alwaysQuery : false,
12202     
12203     /**
12204      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12205      */
12206     multiple : false,
12207     
12208     /**
12209      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12210      */
12211     invalidClass : "has-warning",
12212     
12213     /**
12214      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12215      */
12216     validClass : "has-success",
12217     
12218     /**
12219      * @cfg {Boolean} specialFilter (true|false) special filter default false
12220      */
12221     specialFilter : false,
12222     
12223     /**
12224      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12225      */
12226     mobileTouchView : true,
12227     
12228     //private
12229     addicon : false,
12230     editicon: false,
12231     
12232     page: 0,
12233     hasQuery: false,
12234     append: false,
12235     loadNext: false,
12236     autoFocus : true,
12237     tickable : false,
12238     btnPosition : 'right',
12239     triggerList : true,
12240     showToggleBtn : true,
12241     animate : true,
12242     emptyResultText: 'Empty',
12243     triggerText : 'Select',
12244     
12245     // element that contains real text value.. (when hidden is used..)
12246     
12247     getAutoCreate : function()
12248     {
12249         var cfg = false;
12250         
12251         /*
12252          * Touch Devices
12253          */
12254         
12255         if(Roo.isTouch && this.mobileTouchView){
12256             cfg = this.getAutoCreateTouchView();
12257             return cfg;;
12258         }
12259         
12260         /*
12261          *  Normal ComboBox
12262          */
12263         if(!this.tickable){
12264             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12265             return cfg;
12266         }
12267         
12268         /*
12269          *  ComboBox with tickable selections
12270          */
12271              
12272         var align = this.labelAlign || this.parentLabelAlign();
12273         
12274         cfg = {
12275             cls : 'form-group roo-combobox-tickable' //input-group
12276         };
12277         
12278         var buttons = {
12279             tag : 'div',
12280             cls : 'tickable-buttons',
12281             cn : [
12282                 {
12283                     tag : 'button',
12284                     type : 'button',
12285                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12286                     html : this.triggerText
12287                 },
12288                 {
12289                     tag : 'button',
12290                     type : 'button',
12291                     name : 'ok',
12292                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12293                     html : 'Done'
12294                 },
12295                 {
12296                     tag : 'button',
12297                     type : 'button',
12298                     name : 'cancel',
12299                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12300                     html : 'Cancel'
12301                 }
12302             ]
12303         };
12304         
12305         if(this.editable){
12306             buttons.cn.unshift({
12307                 tag: 'input',
12308                 cls: 'roo-select2-search-field-input'
12309             });
12310         }
12311         
12312         var _this = this;
12313         
12314         Roo.each(buttons.cn, function(c){
12315             if (_this.size) {
12316                 c.cls += ' btn-' + _this.size;
12317             }
12318
12319             if (_this.disabled) {
12320                 c.disabled = true;
12321             }
12322         });
12323         
12324         var box = {
12325             tag: 'div',
12326             cn: [
12327                 {
12328                     tag: 'input',
12329                     type : 'hidden',
12330                     cls: 'form-hidden-field'
12331                 },
12332                 {
12333                     tag: 'ul',
12334                     cls: 'roo-select2-choices',
12335                     cn:[
12336                         {
12337                             tag: 'li',
12338                             cls: 'roo-select2-search-field',
12339                             cn: [
12340
12341                                 buttons
12342                             ]
12343                         }
12344                     ]
12345                 }
12346             ]
12347         };
12348         
12349         var combobox = {
12350             cls: 'roo-select2-container input-group roo-select2-container-multi',
12351             cn: [
12352                 box
12353 //                {
12354 //                    tag: 'ul',
12355 //                    cls: 'typeahead typeahead-long dropdown-menu',
12356 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12357 //                }
12358             ]
12359         };
12360         
12361         if(this.hasFeedback && !this.allowBlank){
12362             
12363             var feedback = {
12364                 tag: 'span',
12365                 cls: 'glyphicon form-control-feedback'
12366             };
12367
12368             combobox.cn.push(feedback);
12369         }
12370         
12371         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12372             
12373 //                Roo.log("left and has label");
12374             cfg.cn = [
12375                 {
12376                     tag : 'i',
12377                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12378                     tooltip : 'This field is required'
12379                 },
12380                 {
12381                     tag: 'label',
12382                     'for' :  id,
12383                     cls : 'control-label col-sm-' + this.labelWidth,
12384                     html : this.fieldLabel
12385
12386                 },
12387                 {
12388                     cls : "col-sm-" + (12 - this.labelWidth), 
12389                     cn: [
12390                         combobox
12391                     ]
12392                 }
12393
12394             ];
12395
12396             if(this.indicatorpos == 'right'){
12397                 
12398                 cfg.cn = [
12399                     {
12400                         tag: 'label',
12401                         'for' :  id,
12402                         cls : 'control-label col-sm-' + this.labelWidth,
12403                         html : this.fieldLabel
12404
12405                     },
12406                     {
12407                         tag : 'i',
12408                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12409                         tooltip : 'This field is required'
12410                     },
12411                     {
12412                         cls : "col-sm-" + (12 - this.labelWidth), 
12413                         cn: [
12414                             combobox
12415                         ]
12416                     }
12417
12418                 ];
12419             
12420             }
12421                 
12422                 
12423         } else if ( this.fieldLabel.length) {
12424 //                Roo.log(" label");
12425                  cfg.cn = [
12426                     {
12427                         tag : 'i',
12428                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12429                         tooltip : 'This field is required'
12430                     },
12431                     {
12432                         tag: 'label',
12433                         //cls : 'input-group-addon',
12434                         html : this.fieldLabel
12435                         
12436                     },
12437                     
12438                     combobox
12439                     
12440                 ];
12441                 
12442                 if(this.indicatorpos == 'right'){
12443                     
12444                     cfg.cn = [
12445                         {
12446                             tag: 'label',
12447                             //cls : 'input-group-addon',
12448                             html : this.fieldLabel
12449
12450                         },
12451                         
12452                         {
12453                             tag : 'i',
12454                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12455                             tooltip : 'This field is required'
12456                         },
12457                         
12458                         combobox
12459
12460                     ];
12461                 
12462                 }
12463
12464         } else {
12465             
12466 //                Roo.log(" no label && no align");
12467                 cfg = combobox
12468                      
12469                 
12470         }
12471          
12472         var settings=this;
12473         ['xs','sm','md','lg'].map(function(size){
12474             if (settings[size]) {
12475                 cfg.cls += ' col-' + size + '-' + settings[size];
12476             }
12477         });
12478         
12479         return cfg;
12480         
12481     },
12482     
12483     _initEventsCalled : false,
12484     
12485     // private
12486     initEvents: function()
12487     {
12488         
12489         if (this._initEventsCalled) { // as we call render... prevent looping...
12490             return;
12491         }
12492         this._initEventsCalled = true;
12493         
12494         if (!this.store) {
12495             throw "can not find store for combo";
12496         }
12497         
12498         this.store = Roo.factory(this.store, Roo.data);
12499         
12500         // if we are building from html. then this element is so complex, that we can not really
12501         // use the rendered HTML.
12502         // so we have to trash and replace the previous code.
12503         if (Roo.XComponent.build_from_html) {
12504             
12505             // remove this element....
12506             var e = this.el.dom, k=0;
12507             while (e ) { e = e.previousSibling;  ++k;}
12508
12509             this.el.remove();
12510             
12511             this.el=false;
12512             this.rendered = false;
12513             
12514             this.render(this.parent().getChildContainer(true), k);
12515             
12516             
12517             
12518         }
12519         
12520         
12521         /*
12522          * Touch Devices
12523          */
12524         
12525         if(Roo.isTouch && this.mobileTouchView){
12526             this.initTouchView();
12527             return;
12528         }
12529         
12530         if(this.tickable){
12531             this.initTickableEvents();
12532             return;
12533         }
12534         
12535         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12536         
12537         if(this.hiddenName){
12538             
12539             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12540             
12541             this.hiddenField.dom.value =
12542                 this.hiddenValue !== undefined ? this.hiddenValue :
12543                 this.value !== undefined ? this.value : '';
12544
12545             // prevent input submission
12546             this.el.dom.removeAttribute('name');
12547             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12548              
12549              
12550         }
12551         //if(Roo.isGecko){
12552         //    this.el.dom.setAttribute('autocomplete', 'off');
12553         //}
12554         
12555         var cls = 'x-combo-list';
12556         
12557         //this.list = new Roo.Layer({
12558         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12559         //});
12560         
12561         var _this = this;
12562         
12563         (function(){
12564             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12565             _this.list.setWidth(lw);
12566         }).defer(100);
12567         
12568         this.list.on('mouseover', this.onViewOver, this);
12569         this.list.on('mousemove', this.onViewMove, this);
12570         
12571         this.list.on('scroll', this.onViewScroll, this);
12572         
12573         /*
12574         this.list.swallowEvent('mousewheel');
12575         this.assetHeight = 0;
12576
12577         if(this.title){
12578             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12579             this.assetHeight += this.header.getHeight();
12580         }
12581
12582         this.innerList = this.list.createChild({cls:cls+'-inner'});
12583         this.innerList.on('mouseover', this.onViewOver, this);
12584         this.innerList.on('mousemove', this.onViewMove, this);
12585         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12586         
12587         if(this.allowBlank && !this.pageSize && !this.disableClear){
12588             this.footer = this.list.createChild({cls:cls+'-ft'});
12589             this.pageTb = new Roo.Toolbar(this.footer);
12590            
12591         }
12592         if(this.pageSize){
12593             this.footer = this.list.createChild({cls:cls+'-ft'});
12594             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12595                     {pageSize: this.pageSize});
12596             
12597         }
12598         
12599         if (this.pageTb && this.allowBlank && !this.disableClear) {
12600             var _this = this;
12601             this.pageTb.add(new Roo.Toolbar.Fill(), {
12602                 cls: 'x-btn-icon x-btn-clear',
12603                 text: '&#160;',
12604                 handler: function()
12605                 {
12606                     _this.collapse();
12607                     _this.clearValue();
12608                     _this.onSelect(false, -1);
12609                 }
12610             });
12611         }
12612         if (this.footer) {
12613             this.assetHeight += this.footer.getHeight();
12614         }
12615         */
12616             
12617         if(!this.tpl){
12618             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12619         }
12620
12621         this.view = new Roo.View(this.list, this.tpl, {
12622             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12623         });
12624         //this.view.wrapEl.setDisplayed(false);
12625         this.view.on('click', this.onViewClick, this);
12626         
12627         
12628         
12629         this.store.on('beforeload', this.onBeforeLoad, this);
12630         this.store.on('load', this.onLoad, this);
12631         this.store.on('loadexception', this.onLoadException, this);
12632         /*
12633         if(this.resizable){
12634             this.resizer = new Roo.Resizable(this.list,  {
12635                pinned:true, handles:'se'
12636             });
12637             this.resizer.on('resize', function(r, w, h){
12638                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12639                 this.listWidth = w;
12640                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12641                 this.restrictHeight();
12642             }, this);
12643             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12644         }
12645         */
12646         if(!this.editable){
12647             this.editable = true;
12648             this.setEditable(false);
12649         }
12650         
12651         /*
12652         
12653         if (typeof(this.events.add.listeners) != 'undefined') {
12654             
12655             this.addicon = this.wrap.createChild(
12656                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12657        
12658             this.addicon.on('click', function(e) {
12659                 this.fireEvent('add', this);
12660             }, this);
12661         }
12662         if (typeof(this.events.edit.listeners) != 'undefined') {
12663             
12664             this.editicon = this.wrap.createChild(
12665                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12666             if (this.addicon) {
12667                 this.editicon.setStyle('margin-left', '40px');
12668             }
12669             this.editicon.on('click', function(e) {
12670                 
12671                 // we fire even  if inothing is selected..
12672                 this.fireEvent('edit', this, this.lastData );
12673                 
12674             }, this);
12675         }
12676         */
12677         
12678         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12679             "up" : function(e){
12680                 this.inKeyMode = true;
12681                 this.selectPrev();
12682             },
12683
12684             "down" : function(e){
12685                 if(!this.isExpanded()){
12686                     this.onTriggerClick();
12687                 }else{
12688                     this.inKeyMode = true;
12689                     this.selectNext();
12690                 }
12691             },
12692
12693             "enter" : function(e){
12694 //                this.onViewClick();
12695                 //return true;
12696                 this.collapse();
12697                 
12698                 if(this.fireEvent("specialkey", this, e)){
12699                     this.onViewClick(false);
12700                 }
12701                 
12702                 return true;
12703             },
12704
12705             "esc" : function(e){
12706                 this.collapse();
12707             },
12708
12709             "tab" : function(e){
12710                 this.collapse();
12711                 
12712                 if(this.fireEvent("specialkey", this, e)){
12713                     this.onViewClick(false);
12714                 }
12715                 
12716                 return true;
12717             },
12718
12719             scope : this,
12720
12721             doRelay : function(foo, bar, hname){
12722                 if(hname == 'down' || this.scope.isExpanded()){
12723                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12724                 }
12725                 return true;
12726             },
12727
12728             forceKeyDown: true
12729         });
12730         
12731         
12732         this.queryDelay = Math.max(this.queryDelay || 10,
12733                 this.mode == 'local' ? 10 : 250);
12734         
12735         
12736         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12737         
12738         if(this.typeAhead){
12739             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12740         }
12741         if(this.editable !== false){
12742             this.inputEl().on("keyup", this.onKeyUp, this);
12743         }
12744         if(this.forceSelection){
12745             this.inputEl().on('blur', this.doForce, this);
12746         }
12747         
12748         if(this.multiple){
12749             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12750             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12751         }
12752     },
12753     
12754     initTickableEvents: function()
12755     {   
12756         this.createList();
12757         
12758         if(this.hiddenName){
12759             
12760             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12761             
12762             this.hiddenField.dom.value =
12763                 this.hiddenValue !== undefined ? this.hiddenValue :
12764                 this.value !== undefined ? this.value : '';
12765
12766             // prevent input submission
12767             this.el.dom.removeAttribute('name');
12768             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12769              
12770              
12771         }
12772         
12773 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12774         
12775         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12776         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12777         if(this.triggerList){
12778             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12779         }
12780          
12781         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12782         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12783         
12784         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12785         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12786         
12787         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12788         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12789         
12790         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12791         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12792         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12793         
12794         this.okBtn.hide();
12795         this.cancelBtn.hide();
12796         
12797         var _this = this;
12798         
12799         (function(){
12800             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12801             _this.list.setWidth(lw);
12802         }).defer(100);
12803         
12804         this.list.on('mouseover', this.onViewOver, this);
12805         this.list.on('mousemove', this.onViewMove, this);
12806         
12807         this.list.on('scroll', this.onViewScroll, this);
12808         
12809         if(!this.tpl){
12810             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>';
12811         }
12812
12813         this.view = new Roo.View(this.list, this.tpl, {
12814             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12815         });
12816         
12817         //this.view.wrapEl.setDisplayed(false);
12818         this.view.on('click', this.onViewClick, this);
12819         
12820         
12821         
12822         this.store.on('beforeload', this.onBeforeLoad, this);
12823         this.store.on('load', this.onLoad, this);
12824         this.store.on('loadexception', this.onLoadException, this);
12825         
12826         if(this.editable){
12827             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12828                 "up" : function(e){
12829                     this.inKeyMode = true;
12830                     this.selectPrev();
12831                 },
12832
12833                 "down" : function(e){
12834                     this.inKeyMode = true;
12835                     this.selectNext();
12836                 },
12837
12838                 "enter" : function(e){
12839                     if(this.fireEvent("specialkey", this, e)){
12840                         this.onViewClick(false);
12841                     }
12842                     
12843                     return true;
12844                 },
12845
12846                 "esc" : function(e){
12847                     this.onTickableFooterButtonClick(e, false, false);
12848                 },
12849
12850                 "tab" : function(e){
12851                     this.fireEvent("specialkey", this, e);
12852                     
12853                     this.onTickableFooterButtonClick(e, false, false);
12854                     
12855                     return true;
12856                 },
12857
12858                 scope : this,
12859
12860                 doRelay : function(e, fn, key){
12861                     if(this.scope.isExpanded()){
12862                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12863                     }
12864                     return true;
12865                 },
12866
12867                 forceKeyDown: true
12868             });
12869         }
12870         
12871         this.queryDelay = Math.max(this.queryDelay || 10,
12872                 this.mode == 'local' ? 10 : 250);
12873         
12874         
12875         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12876         
12877         if(this.typeAhead){
12878             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12879         }
12880         
12881         if(this.editable !== false){
12882             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12883         }
12884         
12885     },
12886
12887     onDestroy : function(){
12888         if(this.view){
12889             this.view.setStore(null);
12890             this.view.el.removeAllListeners();
12891             this.view.el.remove();
12892             this.view.purgeListeners();
12893         }
12894         if(this.list){
12895             this.list.dom.innerHTML  = '';
12896         }
12897         
12898         if(this.store){
12899             this.store.un('beforeload', this.onBeforeLoad, this);
12900             this.store.un('load', this.onLoad, this);
12901             this.store.un('loadexception', this.onLoadException, this);
12902         }
12903         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12904     },
12905
12906     // private
12907     fireKey : function(e){
12908         if(e.isNavKeyPress() && !this.list.isVisible()){
12909             this.fireEvent("specialkey", this, e);
12910         }
12911     },
12912
12913     // private
12914     onResize: function(w, h){
12915 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12916 //        
12917 //        if(typeof w != 'number'){
12918 //            // we do not handle it!?!?
12919 //            return;
12920 //        }
12921 //        var tw = this.trigger.getWidth();
12922 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12923 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12924 //        var x = w - tw;
12925 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12926 //            
12927 //        //this.trigger.setStyle('left', x+'px');
12928 //        
12929 //        if(this.list && this.listWidth === undefined){
12930 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12931 //            this.list.setWidth(lw);
12932 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12933 //        }
12934         
12935     
12936         
12937     },
12938
12939     /**
12940      * Allow or prevent the user from directly editing the field text.  If false is passed,
12941      * the user will only be able to select from the items defined in the dropdown list.  This method
12942      * is the runtime equivalent of setting the 'editable' config option at config time.
12943      * @param {Boolean} value True to allow the user to directly edit the field text
12944      */
12945     setEditable : function(value){
12946         if(value == this.editable){
12947             return;
12948         }
12949         this.editable = value;
12950         if(!value){
12951             this.inputEl().dom.setAttribute('readOnly', true);
12952             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12953             this.inputEl().addClass('x-combo-noedit');
12954         }else{
12955             this.inputEl().dom.setAttribute('readOnly', false);
12956             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12957             this.inputEl().removeClass('x-combo-noedit');
12958         }
12959     },
12960
12961     // private
12962     
12963     onBeforeLoad : function(combo,opts){
12964         if(!this.hasFocus){
12965             return;
12966         }
12967          if (!opts.add) {
12968             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12969          }
12970         this.restrictHeight();
12971         this.selectedIndex = -1;
12972     },
12973
12974     // private
12975     onLoad : function(){
12976         
12977         this.hasQuery = false;
12978         
12979         if(!this.hasFocus){
12980             return;
12981         }
12982         
12983         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12984             this.loading.hide();
12985         }
12986              
12987         if(this.store.getCount() > 0){
12988             this.expand();
12989             this.restrictHeight();
12990             if(this.lastQuery == this.allQuery){
12991                 if(this.editable && !this.tickable){
12992                     this.inputEl().dom.select();
12993                 }
12994                 
12995                 if(
12996                     !this.selectByValue(this.value, true) &&
12997                     this.autoFocus && 
12998                     (
12999                         !this.store.lastOptions ||
13000                         typeof(this.store.lastOptions.add) == 'undefined' || 
13001                         this.store.lastOptions.add != true
13002                     )
13003                 ){
13004                     this.select(0, true);
13005                 }
13006             }else{
13007                 if(this.autoFocus){
13008                     this.selectNext();
13009                 }
13010                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13011                     this.taTask.delay(this.typeAheadDelay);
13012                 }
13013             }
13014         }else{
13015             this.onEmptyResults();
13016         }
13017         
13018         //this.el.focus();
13019     },
13020     // private
13021     onLoadException : function()
13022     {
13023         this.hasQuery = false;
13024         
13025         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13026             this.loading.hide();
13027         }
13028         
13029         if(this.tickable && this.editable){
13030             return;
13031         }
13032         
13033         this.collapse();
13034         // only causes errors at present
13035         //Roo.log(this.store.reader.jsonData);
13036         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13037             // fixme
13038             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13039         //}
13040         
13041         
13042     },
13043     // private
13044     onTypeAhead : function(){
13045         if(this.store.getCount() > 0){
13046             var r = this.store.getAt(0);
13047             var newValue = r.data[this.displayField];
13048             var len = newValue.length;
13049             var selStart = this.getRawValue().length;
13050             
13051             if(selStart != len){
13052                 this.setRawValue(newValue);
13053                 this.selectText(selStart, newValue.length);
13054             }
13055         }
13056     },
13057
13058     // private
13059     onSelect : function(record, index){
13060         
13061         if(this.fireEvent('beforeselect', this, record, index) !== false){
13062         
13063             this.setFromData(index > -1 ? record.data : false);
13064             
13065             this.collapse();
13066             this.fireEvent('select', this, record, index);
13067         }
13068     },
13069
13070     /**
13071      * Returns the currently selected field value or empty string if no value is set.
13072      * @return {String} value The selected value
13073      */
13074     getValue : function(){
13075         
13076         if(this.multiple){
13077             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13078         }
13079         
13080         if(this.valueField){
13081             return typeof this.value != 'undefined' ? this.value : '';
13082         }else{
13083             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13084         }
13085     },
13086
13087     /**
13088      * Clears any text/value currently set in the field
13089      */
13090     clearValue : function(){
13091         if(this.hiddenField){
13092             this.hiddenField.dom.value = '';
13093         }
13094         this.value = '';
13095         this.setRawValue('');
13096         this.lastSelectionText = '';
13097         this.lastData = false;
13098         
13099         var close = this.closeTriggerEl();
13100         
13101         if(close){
13102             close.hide();
13103         }
13104         
13105         this.validate();
13106         
13107     },
13108
13109     /**
13110      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13111      * will be displayed in the field.  If the value does not match the data value of an existing item,
13112      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13113      * Otherwise the field will be blank (although the value will still be set).
13114      * @param {String} value The value to match
13115      */
13116     setValue : function(v){
13117         if(this.multiple){
13118             this.syncValue();
13119             return;
13120         }
13121         
13122         var text = v;
13123         if(this.valueField){
13124             var r = this.findRecord(this.valueField, v);
13125             if(r){
13126                 text = r.data[this.displayField];
13127             }else if(this.valueNotFoundText !== undefined){
13128                 text = this.valueNotFoundText;
13129             }
13130         }
13131         this.lastSelectionText = text;
13132         if(this.hiddenField){
13133             this.hiddenField.dom.value = v;
13134         }
13135         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13136         this.value = v;
13137         
13138         var close = this.closeTriggerEl();
13139         
13140         if(close){
13141             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13142         }
13143         
13144         this.validate();
13145     },
13146     /**
13147      * @property {Object} the last set data for the element
13148      */
13149     
13150     lastData : false,
13151     /**
13152      * Sets the value of the field based on a object which is related to the record format for the store.
13153      * @param {Object} value the value to set as. or false on reset?
13154      */
13155     setFromData : function(o){
13156         
13157         if(this.multiple){
13158             this.addItem(o);
13159             return;
13160         }
13161             
13162         var dv = ''; // display value
13163         var vv = ''; // value value..
13164         this.lastData = o;
13165         if (this.displayField) {
13166             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13167         } else {
13168             // this is an error condition!!!
13169             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13170         }
13171         
13172         if(this.valueField){
13173             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13174         }
13175         
13176         var close = this.closeTriggerEl();
13177         
13178         if(close){
13179             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13180         }
13181         
13182         if(this.hiddenField){
13183             this.hiddenField.dom.value = vv;
13184             
13185             this.lastSelectionText = dv;
13186             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13187             this.value = vv;
13188             return;
13189         }
13190         // no hidden field.. - we store the value in 'value', but still display
13191         // display field!!!!
13192         this.lastSelectionText = dv;
13193         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13194         this.value = vv;
13195         
13196         
13197         
13198     },
13199     // private
13200     reset : function(){
13201         // overridden so that last data is reset..
13202         
13203         if(this.multiple){
13204             this.clearItem();
13205             return;
13206         }
13207         
13208         this.setValue(this.originalValue);
13209         //this.clearInvalid();
13210         this.lastData = false;
13211         if (this.view) {
13212             this.view.clearSelections();
13213         }
13214         
13215         this.validate();
13216     },
13217     // private
13218     findRecord : function(prop, value){
13219         var record;
13220         if(this.store.getCount() > 0){
13221             this.store.each(function(r){
13222                 if(r.data[prop] == value){
13223                     record = r;
13224                     return false;
13225                 }
13226                 return true;
13227             });
13228         }
13229         return record;
13230     },
13231     
13232     getName: function()
13233     {
13234         // returns hidden if it's set..
13235         if (!this.rendered) {return ''};
13236         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13237         
13238     },
13239     // private
13240     onViewMove : function(e, t){
13241         this.inKeyMode = false;
13242     },
13243
13244     // private
13245     onViewOver : function(e, t){
13246         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13247             return;
13248         }
13249         var item = this.view.findItemFromChild(t);
13250         
13251         if(item){
13252             var index = this.view.indexOf(item);
13253             this.select(index, false);
13254         }
13255     },
13256
13257     // private
13258     onViewClick : function(view, doFocus, el, e)
13259     {
13260         var index = this.view.getSelectedIndexes()[0];
13261         
13262         var r = this.store.getAt(index);
13263         
13264         if(this.tickable){
13265             
13266             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13267                 return;
13268             }
13269             
13270             var rm = false;
13271             var _this = this;
13272             
13273             Roo.each(this.tickItems, function(v,k){
13274                 
13275                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13276                     Roo.log(v);
13277                     _this.tickItems.splice(k, 1);
13278                     
13279                     if(typeof(e) == 'undefined' && view == false){
13280                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13281                     }
13282                     
13283                     rm = true;
13284                     return;
13285                 }
13286             });
13287             
13288             if(rm){
13289                 return;
13290             }
13291             
13292             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13293                 this.tickItems.push(r.data);
13294             }
13295             
13296             if(typeof(e) == 'undefined' && view == false){
13297                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13298             }
13299                     
13300             return;
13301         }
13302         
13303         if(r){
13304             this.onSelect(r, index);
13305         }
13306         if(doFocus !== false && !this.blockFocus){
13307             this.inputEl().focus();
13308         }
13309     },
13310
13311     // private
13312     restrictHeight : function(){
13313         //this.innerList.dom.style.height = '';
13314         //var inner = this.innerList.dom;
13315         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13316         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13317         //this.list.beginUpdate();
13318         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13319         this.list.alignTo(this.inputEl(), this.listAlign);
13320         this.list.alignTo(this.inputEl(), this.listAlign);
13321         //this.list.endUpdate();
13322     },
13323
13324     // private
13325     onEmptyResults : function(){
13326         
13327         if(this.tickable && this.editable){
13328             this.restrictHeight();
13329             return;
13330         }
13331         
13332         this.collapse();
13333     },
13334
13335     /**
13336      * Returns true if the dropdown list is expanded, else false.
13337      */
13338     isExpanded : function(){
13339         return this.list.isVisible();
13340     },
13341
13342     /**
13343      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13344      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13345      * @param {String} value The data value of the item to select
13346      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13347      * selected item if it is not currently in view (defaults to true)
13348      * @return {Boolean} True if the value matched an item in the list, else false
13349      */
13350     selectByValue : function(v, scrollIntoView){
13351         if(v !== undefined && v !== null){
13352             var r = this.findRecord(this.valueField || this.displayField, v);
13353             if(r){
13354                 this.select(this.store.indexOf(r), scrollIntoView);
13355                 return true;
13356             }
13357         }
13358         return false;
13359     },
13360
13361     /**
13362      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13363      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13364      * @param {Number} index The zero-based index of the list item to select
13365      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13366      * selected item if it is not currently in view (defaults to true)
13367      */
13368     select : function(index, scrollIntoView){
13369         this.selectedIndex = index;
13370         this.view.select(index);
13371         if(scrollIntoView !== false){
13372             var el = this.view.getNode(index);
13373             /*
13374              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13375              */
13376             if(el){
13377                 this.list.scrollChildIntoView(el, false);
13378             }
13379         }
13380     },
13381
13382     // private
13383     selectNext : function(){
13384         var ct = this.store.getCount();
13385         if(ct > 0){
13386             if(this.selectedIndex == -1){
13387                 this.select(0);
13388             }else if(this.selectedIndex < ct-1){
13389                 this.select(this.selectedIndex+1);
13390             }
13391         }
13392     },
13393
13394     // private
13395     selectPrev : function(){
13396         var ct = this.store.getCount();
13397         if(ct > 0){
13398             if(this.selectedIndex == -1){
13399                 this.select(0);
13400             }else if(this.selectedIndex != 0){
13401                 this.select(this.selectedIndex-1);
13402             }
13403         }
13404     },
13405
13406     // private
13407     onKeyUp : function(e){
13408         if(this.editable !== false && !e.isSpecialKey()){
13409             this.lastKey = e.getKey();
13410             this.dqTask.delay(this.queryDelay);
13411         }
13412     },
13413
13414     // private
13415     validateBlur : function(){
13416         return !this.list || !this.list.isVisible();   
13417     },
13418
13419     // private
13420     initQuery : function(){
13421         
13422         var v = this.getRawValue();
13423         
13424         if(this.tickable && this.editable){
13425             v = this.tickableInputEl().getValue();
13426         }
13427         
13428         this.doQuery(v);
13429     },
13430
13431     // private
13432     doForce : function(){
13433         if(this.inputEl().dom.value.length > 0){
13434             this.inputEl().dom.value =
13435                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13436              
13437         }
13438     },
13439
13440     /**
13441      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13442      * query allowing the query action to be canceled if needed.
13443      * @param {String} query The SQL query to execute
13444      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13445      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13446      * saved in the current store (defaults to false)
13447      */
13448     doQuery : function(q, forceAll){
13449         
13450         if(q === undefined || q === null){
13451             q = '';
13452         }
13453         var qe = {
13454             query: q,
13455             forceAll: forceAll,
13456             combo: this,
13457             cancel:false
13458         };
13459         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13460             return false;
13461         }
13462         q = qe.query;
13463         
13464         forceAll = qe.forceAll;
13465         if(forceAll === true || (q.length >= this.minChars)){
13466             
13467             this.hasQuery = true;
13468             
13469             if(this.lastQuery != q || this.alwaysQuery){
13470                 this.lastQuery = q;
13471                 if(this.mode == 'local'){
13472                     this.selectedIndex = -1;
13473                     if(forceAll){
13474                         this.store.clearFilter();
13475                     }else{
13476                         
13477                         if(this.specialFilter){
13478                             this.fireEvent('specialfilter', this);
13479                             this.onLoad();
13480                             return;
13481                         }
13482                         
13483                         this.store.filter(this.displayField, q);
13484                     }
13485                     
13486                     this.store.fireEvent("datachanged", this.store);
13487                     
13488                     this.onLoad();
13489                     
13490                     
13491                 }else{
13492                     
13493                     this.store.baseParams[this.queryParam] = q;
13494                     
13495                     var options = {params : this.getParams(q)};
13496                     
13497                     if(this.loadNext){
13498                         options.add = true;
13499                         options.params.start = this.page * this.pageSize;
13500                     }
13501                     
13502                     this.store.load(options);
13503                     
13504                     /*
13505                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13506                      *  we should expand the list on onLoad
13507                      *  so command out it
13508                      */
13509 //                    this.expand();
13510                 }
13511             }else{
13512                 this.selectedIndex = -1;
13513                 this.onLoad();   
13514             }
13515         }
13516         
13517         this.loadNext = false;
13518     },
13519     
13520     // private
13521     getParams : function(q){
13522         var p = {};
13523         //p[this.queryParam] = q;
13524         
13525         if(this.pageSize){
13526             p.start = 0;
13527             p.limit = this.pageSize;
13528         }
13529         return p;
13530     },
13531
13532     /**
13533      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13534      */
13535     collapse : function(){
13536         if(!this.isExpanded()){
13537             return;
13538         }
13539         
13540         this.list.hide();
13541         
13542         if(this.tickable){
13543             this.hasFocus = false;
13544             this.okBtn.hide();
13545             this.cancelBtn.hide();
13546             this.trigger.show();
13547             
13548             if(this.editable){
13549                 this.tickableInputEl().dom.value = '';
13550                 this.tickableInputEl().blur();
13551             }
13552             
13553         }
13554         
13555         Roo.get(document).un('mousedown', this.collapseIf, this);
13556         Roo.get(document).un('mousewheel', this.collapseIf, this);
13557         if (!this.editable) {
13558             Roo.get(document).un('keydown', this.listKeyPress, this);
13559         }
13560         this.fireEvent('collapse', this);
13561         
13562         this.validate();
13563     },
13564
13565     // private
13566     collapseIf : function(e){
13567         var in_combo  = e.within(this.el);
13568         var in_list =  e.within(this.list);
13569         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13570         
13571         if (in_combo || in_list || is_list) {
13572             //e.stopPropagation();
13573             return;
13574         }
13575         
13576         if(this.tickable){
13577             this.onTickableFooterButtonClick(e, false, false);
13578         }
13579
13580         this.collapse();
13581         
13582     },
13583
13584     /**
13585      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13586      */
13587     expand : function(){
13588        
13589         if(this.isExpanded() || !this.hasFocus){
13590             return;
13591         }
13592         
13593         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13594         this.list.setWidth(lw);
13595         
13596         
13597          Roo.log('expand');
13598         
13599         this.list.show();
13600         
13601         this.restrictHeight();
13602         
13603         if(this.tickable){
13604             
13605             this.tickItems = Roo.apply([], this.item);
13606             
13607             this.okBtn.show();
13608             this.cancelBtn.show();
13609             this.trigger.hide();
13610             
13611             if(this.editable){
13612                 this.tickableInputEl().focus();
13613             }
13614             
13615         }
13616         
13617         Roo.get(document).on('mousedown', this.collapseIf, this);
13618         Roo.get(document).on('mousewheel', this.collapseIf, this);
13619         if (!this.editable) {
13620             Roo.get(document).on('keydown', this.listKeyPress, this);
13621         }
13622         
13623         this.fireEvent('expand', this);
13624     },
13625
13626     // private
13627     // Implements the default empty TriggerField.onTriggerClick function
13628     onTriggerClick : function(e)
13629     {
13630         Roo.log('trigger click');
13631         
13632         if(this.disabled || !this.triggerList){
13633             return;
13634         }
13635         
13636         this.page = 0;
13637         this.loadNext = false;
13638         
13639         if(this.isExpanded()){
13640             this.collapse();
13641             if (!this.blockFocus) {
13642                 this.inputEl().focus();
13643             }
13644             
13645         }else {
13646             this.hasFocus = true;
13647             if(this.triggerAction == 'all') {
13648                 this.doQuery(this.allQuery, true);
13649             } else {
13650                 this.doQuery(this.getRawValue());
13651             }
13652             if (!this.blockFocus) {
13653                 this.inputEl().focus();
13654             }
13655         }
13656     },
13657     
13658     onTickableTriggerClick : function(e)
13659     {
13660         if(this.disabled){
13661             return;
13662         }
13663         
13664         this.page = 0;
13665         this.loadNext = false;
13666         this.hasFocus = true;
13667         
13668         if(this.triggerAction == 'all') {
13669             this.doQuery(this.allQuery, true);
13670         } else {
13671             this.doQuery(this.getRawValue());
13672         }
13673     },
13674     
13675     onSearchFieldClick : function(e)
13676     {
13677         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13678             this.onTickableFooterButtonClick(e, false, false);
13679             return;
13680         }
13681         
13682         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13683             return;
13684         }
13685         
13686         this.page = 0;
13687         this.loadNext = false;
13688         this.hasFocus = true;
13689         
13690         if(this.triggerAction == 'all') {
13691             this.doQuery(this.allQuery, true);
13692         } else {
13693             this.doQuery(this.getRawValue());
13694         }
13695     },
13696     
13697     listKeyPress : function(e)
13698     {
13699         //Roo.log('listkeypress');
13700         // scroll to first matching element based on key pres..
13701         if (e.isSpecialKey()) {
13702             return false;
13703         }
13704         var k = String.fromCharCode(e.getKey()).toUpperCase();
13705         //Roo.log(k);
13706         var match  = false;
13707         var csel = this.view.getSelectedNodes();
13708         var cselitem = false;
13709         if (csel.length) {
13710             var ix = this.view.indexOf(csel[0]);
13711             cselitem  = this.store.getAt(ix);
13712             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13713                 cselitem = false;
13714             }
13715             
13716         }
13717         
13718         this.store.each(function(v) { 
13719             if (cselitem) {
13720                 // start at existing selection.
13721                 if (cselitem.id == v.id) {
13722                     cselitem = false;
13723                 }
13724                 return true;
13725             }
13726                 
13727             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13728                 match = this.store.indexOf(v);
13729                 return false;
13730             }
13731             return true;
13732         }, this);
13733         
13734         if (match === false) {
13735             return true; // no more action?
13736         }
13737         // scroll to?
13738         this.view.select(match);
13739         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13740         sn.scrollIntoView(sn.dom.parentNode, false);
13741     },
13742     
13743     onViewScroll : function(e, t){
13744         
13745         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){
13746             return;
13747         }
13748         
13749         this.hasQuery = true;
13750         
13751         this.loading = this.list.select('.loading', true).first();
13752         
13753         if(this.loading === null){
13754             this.list.createChild({
13755                 tag: 'div',
13756                 cls: 'loading roo-select2-more-results roo-select2-active',
13757                 html: 'Loading more results...'
13758             });
13759             
13760             this.loading = this.list.select('.loading', true).first();
13761             
13762             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13763             
13764             this.loading.hide();
13765         }
13766         
13767         this.loading.show();
13768         
13769         var _combo = this;
13770         
13771         this.page++;
13772         this.loadNext = true;
13773         
13774         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13775         
13776         return;
13777     },
13778     
13779     addItem : function(o)
13780     {   
13781         var dv = ''; // display value
13782         
13783         if (this.displayField) {
13784             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13785         } else {
13786             // this is an error condition!!!
13787             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13788         }
13789         
13790         if(!dv.length){
13791             return;
13792         }
13793         
13794         var choice = this.choices.createChild({
13795             tag: 'li',
13796             cls: 'roo-select2-search-choice',
13797             cn: [
13798                 {
13799                     tag: 'div',
13800                     html: dv
13801                 },
13802                 {
13803                     tag: 'a',
13804                     href: '#',
13805                     cls: 'roo-select2-search-choice-close',
13806                     tabindex: '-1'
13807                 }
13808             ]
13809             
13810         }, this.searchField);
13811         
13812         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13813         
13814         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13815         
13816         this.item.push(o);
13817         
13818         this.lastData = o;
13819         
13820         this.syncValue();
13821         
13822         this.inputEl().dom.value = '';
13823         
13824         this.validate();
13825     },
13826     
13827     onRemoveItem : function(e, _self, o)
13828     {
13829         e.preventDefault();
13830         
13831         this.lastItem = Roo.apply([], this.item);
13832         
13833         var index = this.item.indexOf(o.data) * 1;
13834         
13835         if( index < 0){
13836             Roo.log('not this item?!');
13837             return;
13838         }
13839         
13840         this.item.splice(index, 1);
13841         o.item.remove();
13842         
13843         this.syncValue();
13844         
13845         this.fireEvent('remove', this, e);
13846         
13847         this.validate();
13848         
13849     },
13850     
13851     syncValue : function()
13852     {
13853         if(!this.item.length){
13854             this.clearValue();
13855             return;
13856         }
13857             
13858         var value = [];
13859         var _this = this;
13860         Roo.each(this.item, function(i){
13861             if(_this.valueField){
13862                 value.push(i[_this.valueField]);
13863                 return;
13864             }
13865
13866             value.push(i);
13867         });
13868
13869         this.value = value.join(',');
13870
13871         if(this.hiddenField){
13872             this.hiddenField.dom.value = this.value;
13873         }
13874         
13875         this.store.fireEvent("datachanged", this.store);
13876         
13877         this.validate();
13878     },
13879     
13880     clearItem : function()
13881     {
13882         if(!this.multiple){
13883             return;
13884         }
13885         
13886         this.item = [];
13887         
13888         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13889            c.remove();
13890         });
13891         
13892         this.syncValue();
13893         
13894         this.validate();
13895         
13896         if(this.tickable && !Roo.isTouch){
13897             this.view.refresh();
13898         }
13899     },
13900     
13901     inputEl: function ()
13902     {
13903         if(Roo.isTouch && this.mobileTouchView){
13904             return this.el.select('input.form-control',true).first();
13905         }
13906         
13907         if(this.tickable){
13908             return this.searchField;
13909         }
13910         
13911         return this.el.select('input.form-control',true).first();
13912     },
13913     
13914     
13915     onTickableFooterButtonClick : function(e, btn, el)
13916     {
13917         e.preventDefault();
13918         
13919         this.lastItem = Roo.apply([], this.item);
13920         
13921         if(btn && btn.name == 'cancel'){
13922             this.tickItems = Roo.apply([], this.item);
13923             this.collapse();
13924             return;
13925         }
13926         
13927         this.clearItem();
13928         
13929         var _this = this;
13930         
13931         Roo.each(this.tickItems, function(o){
13932             _this.addItem(o);
13933         });
13934         
13935         this.collapse();
13936         
13937     },
13938     
13939     validate : function()
13940     {
13941         var v = this.getRawValue();
13942         
13943         if(this.multiple){
13944             v = this.getValue();
13945         }
13946         
13947         if(this.disabled || this.allowBlank || v.length){
13948             this.markValid();
13949             return true;
13950         }
13951         
13952         this.markInvalid();
13953         return false;
13954     },
13955     
13956     tickableInputEl : function()
13957     {
13958         if(!this.tickable || !this.editable){
13959             return this.inputEl();
13960         }
13961         
13962         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13963     },
13964     
13965     
13966     getAutoCreateTouchView : function()
13967     {
13968         var id = Roo.id();
13969         
13970         var cfg = {
13971             cls: 'form-group' //input-group
13972         };
13973         
13974         var input =  {
13975             tag: 'input',
13976             id : id,
13977             type : this.inputType,
13978             cls : 'form-control x-combo-noedit',
13979             autocomplete: 'new-password',
13980             placeholder : this.placeholder || '',
13981             readonly : true
13982         };
13983         
13984         if (this.name) {
13985             input.name = this.name;
13986         }
13987         
13988         if (this.size) {
13989             input.cls += ' input-' + this.size;
13990         }
13991         
13992         if (this.disabled) {
13993             input.disabled = true;
13994         }
13995         
13996         var inputblock = {
13997             cls : '',
13998             cn : [
13999                 input
14000             ]
14001         };
14002         
14003         if(this.before){
14004             inputblock.cls += ' input-group';
14005             
14006             inputblock.cn.unshift({
14007                 tag :'span',
14008                 cls : 'input-group-addon',
14009                 html : this.before
14010             });
14011         }
14012         
14013         if(this.removable && !this.multiple){
14014             inputblock.cls += ' roo-removable';
14015             
14016             inputblock.cn.push({
14017                 tag: 'button',
14018                 html : 'x',
14019                 cls : 'roo-combo-removable-btn close'
14020             });
14021         }
14022
14023         if(this.hasFeedback && !this.allowBlank){
14024             
14025             inputblock.cls += ' has-feedback';
14026             
14027             inputblock.cn.push({
14028                 tag: 'span',
14029                 cls: 'glyphicon form-control-feedback'
14030             });
14031             
14032         }
14033         
14034         if (this.after) {
14035             
14036             inputblock.cls += (this.before) ? '' : ' input-group';
14037             
14038             inputblock.cn.push({
14039                 tag :'span',
14040                 cls : 'input-group-addon',
14041                 html : this.after
14042             });
14043         }
14044
14045         var box = {
14046             tag: 'div',
14047             cn: [
14048                 {
14049                     tag: 'input',
14050                     type : 'hidden',
14051                     cls: 'form-hidden-field'
14052                 },
14053                 inputblock
14054             ]
14055             
14056         };
14057         
14058         if(this.multiple){
14059             box = {
14060                 tag: 'div',
14061                 cn: [
14062                     {
14063                         tag: 'input',
14064                         type : 'hidden',
14065                         cls: 'form-hidden-field'
14066                     },
14067                     {
14068                         tag: 'ul',
14069                         cls: 'roo-select2-choices',
14070                         cn:[
14071                             {
14072                                 tag: 'li',
14073                                 cls: 'roo-select2-search-field',
14074                                 cn: [
14075
14076                                     inputblock
14077                                 ]
14078                             }
14079                         ]
14080                     }
14081                 ]
14082             }
14083         };
14084         
14085         var combobox = {
14086             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14087             cn: [
14088                 box
14089             ]
14090         };
14091         
14092         if(!this.multiple && this.showToggleBtn){
14093             
14094             var caret = {
14095                         tag: 'span',
14096                         cls: 'caret'
14097             };
14098             
14099             if (this.caret != false) {
14100                 caret = {
14101                      tag: 'i',
14102                      cls: 'fa fa-' + this.caret
14103                 };
14104                 
14105             }
14106             
14107             combobox.cn.push({
14108                 tag :'span',
14109                 cls : 'input-group-addon btn dropdown-toggle',
14110                 cn : [
14111                     caret,
14112                     {
14113                         tag: 'span',
14114                         cls: 'combobox-clear',
14115                         cn  : [
14116                             {
14117                                 tag : 'i',
14118                                 cls: 'icon-remove'
14119                             }
14120                         ]
14121                     }
14122                 ]
14123
14124             })
14125         }
14126         
14127         if(this.multiple){
14128             combobox.cls += ' roo-select2-container-multi';
14129         }
14130         
14131         var align = this.labelAlign || this.parentLabelAlign();
14132         
14133         cfg.cn = combobox;
14134         
14135         if(this.fieldLabel.length && this.labelWidth){
14136             
14137             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14138             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14139             
14140             cfg.cn = [
14141                 {
14142                    tag : 'i',
14143                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14144                    tooltip : 'This field is required'
14145                 },
14146                 {
14147                     tag: 'label',
14148                     cls : 'control-label ' + lw,
14149                     html : this.fieldLabel
14150
14151                 },
14152                 {
14153                     cls : cw, 
14154                     cn: [
14155                         combobox
14156                     ]
14157                 }
14158             ];
14159             
14160             if(this.indicatorpos == 'right'){
14161                 cfg.cn = [
14162                     {
14163                         tag: 'label',
14164                         cls : 'control-label ' + lw,
14165                         html : this.fieldLabel
14166
14167                     },
14168                     {
14169                        tag : 'i',
14170                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14171                        tooltip : 'This field is required'
14172                     },
14173                     {
14174                         cls : cw, 
14175                         cn: [
14176                             combobox
14177                         ]
14178                     }
14179                 ];
14180             }
14181         }
14182         
14183         var settings = this;
14184         
14185         ['xs','sm','md','lg'].map(function(size){
14186             if (settings[size]) {
14187                 cfg.cls += ' col-' + size + '-' + settings[size];
14188             }
14189         });
14190         
14191         return cfg;
14192     },
14193     
14194     initTouchView : function()
14195     {
14196         this.renderTouchView();
14197         
14198         this.touchViewEl.on('scroll', function(){
14199             this.el.dom.scrollTop = 0;
14200         }, this);
14201         
14202         this.originalValue = this.getValue();
14203         
14204         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14205         
14206         this.inputEl().on("click", this.showTouchView, this);
14207         this.triggerEl.on("click", this.showTouchView, this);
14208         
14209         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14210         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14211         
14212         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14213         
14214         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14215         this.store.on('load', this.onTouchViewLoad, this);
14216         this.store.on('loadexception', this.onTouchViewLoadException, this);
14217         
14218         if(this.hiddenName){
14219             
14220             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14221             
14222             this.hiddenField.dom.value =
14223                 this.hiddenValue !== undefined ? this.hiddenValue :
14224                 this.value !== undefined ? this.value : '';
14225         
14226             this.el.dom.removeAttribute('name');
14227             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14228         }
14229         
14230         if(this.multiple){
14231             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14232             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14233         }
14234         
14235         if(this.removable && !this.multiple){
14236             var close = this.closeTriggerEl();
14237             if(close){
14238                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14239                 close.on('click', this.removeBtnClick, this, close);
14240             }
14241         }
14242         /*
14243          * fix the bug in Safari iOS8
14244          */
14245         this.inputEl().on("focus", function(e){
14246             document.activeElement.blur();
14247         }, this);
14248         
14249         return;
14250         
14251         
14252     },
14253     
14254     renderTouchView : function()
14255     {
14256         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14257         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14258         
14259         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14260         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14261         
14262         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14263         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14264         this.touchViewBodyEl.setStyle('overflow', 'auto');
14265         
14266         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14267         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14268         
14269         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14270         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14271         
14272     },
14273     
14274     showTouchView : function()
14275     {
14276         if(this.disabled){
14277             return;
14278         }
14279         
14280         this.touchViewHeaderEl.hide();
14281
14282         if(this.modalTitle.length){
14283             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14284             this.touchViewHeaderEl.show();
14285         }
14286
14287         this.touchViewEl.show();
14288
14289         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14290         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14291                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14292
14293         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14294
14295         if(this.modalTitle.length){
14296             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14297         }
14298         
14299         this.touchViewBodyEl.setHeight(bodyHeight);
14300
14301         if(this.animate){
14302             var _this = this;
14303             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14304         }else{
14305             this.touchViewEl.addClass('in');
14306         }
14307
14308         this.doTouchViewQuery();
14309         
14310     },
14311     
14312     hideTouchView : function()
14313     {
14314         this.touchViewEl.removeClass('in');
14315
14316         if(this.animate){
14317             var _this = this;
14318             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14319         }else{
14320             this.touchViewEl.setStyle('display', 'none');
14321         }
14322         
14323     },
14324     
14325     setTouchViewValue : function()
14326     {
14327         if(this.multiple){
14328             this.clearItem();
14329         
14330             var _this = this;
14331
14332             Roo.each(this.tickItems, function(o){
14333                 this.addItem(o);
14334             }, this);
14335         }
14336         
14337         this.hideTouchView();
14338     },
14339     
14340     doTouchViewQuery : function()
14341     {
14342         var qe = {
14343             query: '',
14344             forceAll: true,
14345             combo: this,
14346             cancel:false
14347         };
14348         
14349         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14350             return false;
14351         }
14352         
14353         if(!this.alwaysQuery || this.mode == 'local'){
14354             this.onTouchViewLoad();
14355             return;
14356         }
14357         
14358         this.store.load();
14359     },
14360     
14361     onTouchViewBeforeLoad : function(combo,opts)
14362     {
14363         return;
14364     },
14365
14366     // private
14367     onTouchViewLoad : function()
14368     {
14369         if(this.store.getCount() < 1){
14370             this.onTouchViewEmptyResults();
14371             return;
14372         }
14373         
14374         this.clearTouchView();
14375         
14376         var rawValue = this.getRawValue();
14377         
14378         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14379         
14380         this.tickItems = [];
14381         
14382         this.store.data.each(function(d, rowIndex){
14383             var row = this.touchViewListGroup.createChild(template);
14384             
14385             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14386                 row.addClass(d.data.cls);
14387             }
14388             
14389             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14390                 var cfg = {
14391                     data : d.data,
14392                     html : d.data[this.displayField]
14393                 };
14394                 
14395                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14396                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14397                 }
14398             }
14399             row.removeClass('selected');
14400             if(!this.multiple && this.valueField &&
14401                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14402             {
14403                 // radio buttons..
14404                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14405                 row.addClass('selected');
14406             }
14407             
14408             if(this.multiple && this.valueField &&
14409                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14410             {
14411                 
14412                 // checkboxes...
14413                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14414                 this.tickItems.push(d.data);
14415             }
14416             
14417             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14418             
14419         }, this);
14420         
14421         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14422         
14423         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14424
14425         if(this.modalTitle.length){
14426             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14427         }
14428
14429         var listHeight = this.touchViewListGroup.getHeight();
14430         
14431         var _this = this;
14432         
14433         if(firstChecked && listHeight > bodyHeight){
14434             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14435         }
14436         
14437     },
14438     
14439     onTouchViewLoadException : function()
14440     {
14441         this.hideTouchView();
14442     },
14443     
14444     onTouchViewEmptyResults : function()
14445     {
14446         this.clearTouchView();
14447         
14448         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14449         
14450         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14451         
14452     },
14453     
14454     clearTouchView : function()
14455     {
14456         this.touchViewListGroup.dom.innerHTML = '';
14457     },
14458     
14459     onTouchViewClick : function(e, el, o)
14460     {
14461         e.preventDefault();
14462         
14463         var row = o.row;
14464         var rowIndex = o.rowIndex;
14465         
14466         var r = this.store.getAt(rowIndex);
14467         
14468         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14469             
14470             if(!this.multiple){
14471                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14472                     c.dom.removeAttribute('checked');
14473                 }, this);
14474
14475                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14476
14477                 this.setFromData(r.data);
14478
14479                 var close = this.closeTriggerEl();
14480
14481                 if(close){
14482                     close.show();
14483                 }
14484
14485                 this.hideTouchView();
14486
14487                 this.fireEvent('select', this, r, rowIndex);
14488
14489                 return;
14490             }
14491
14492             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14493                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14494                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14495                 return;
14496             }
14497
14498             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14499             this.addItem(r.data);
14500             this.tickItems.push(r.data);
14501         }
14502     }
14503     
14504
14505     /** 
14506     * @cfg {Boolean} grow 
14507     * @hide 
14508     */
14509     /** 
14510     * @cfg {Number} growMin 
14511     * @hide 
14512     */
14513     /** 
14514     * @cfg {Number} growMax 
14515     * @hide 
14516     */
14517     /**
14518      * @hide
14519      * @method autoSize
14520      */
14521 });
14522
14523 Roo.apply(Roo.bootstrap.ComboBox,  {
14524     
14525     header : {
14526         tag: 'div',
14527         cls: 'modal-header',
14528         cn: [
14529             {
14530                 tag: 'h4',
14531                 cls: 'modal-title'
14532             }
14533         ]
14534     },
14535     
14536     body : {
14537         tag: 'div',
14538         cls: 'modal-body',
14539         cn: [
14540             {
14541                 tag: 'ul',
14542                 cls: 'list-group'
14543             }
14544         ]
14545     },
14546     
14547     listItemRadio : {
14548         tag: 'li',
14549         cls: 'list-group-item',
14550         cn: [
14551             {
14552                 tag: 'span',
14553                 cls: 'roo-combobox-list-group-item-value'
14554             },
14555             {
14556                 tag: 'div',
14557                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14558                 cn: [
14559                     {
14560                         tag: 'input',
14561                         type: 'radio'
14562                     },
14563                     {
14564                         tag: 'label'
14565                     }
14566                 ]
14567             }
14568         ]
14569     },
14570     
14571     listItemCheckbox : {
14572         tag: 'li',
14573         cls: 'list-group-item',
14574         cn: [
14575             {
14576                 tag: 'span',
14577                 cls: 'roo-combobox-list-group-item-value'
14578             },
14579             {
14580                 tag: 'div',
14581                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14582                 cn: [
14583                     {
14584                         tag: 'input',
14585                         type: 'checkbox'
14586                     },
14587                     {
14588                         tag: 'label'
14589                     }
14590                 ]
14591             }
14592         ]
14593     },
14594     
14595     emptyResult : {
14596         tag: 'div',
14597         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14598     },
14599     
14600     footer : {
14601         tag: 'div',
14602         cls: 'modal-footer',
14603         cn: [
14604             {
14605                 tag: 'div',
14606                 cls: 'row',
14607                 cn: [
14608                     {
14609                         tag: 'div',
14610                         cls: 'col-xs-6 text-left',
14611                         cn: {
14612                             tag: 'button',
14613                             cls: 'btn btn-danger roo-touch-view-cancel',
14614                             html: 'Cancel'
14615                         }
14616                     },
14617                     {
14618                         tag: 'div',
14619                         cls: 'col-xs-6 text-right',
14620                         cn: {
14621                             tag: 'button',
14622                             cls: 'btn btn-success roo-touch-view-ok',
14623                             html: 'OK'
14624                         }
14625                     }
14626                 ]
14627             }
14628         ]
14629         
14630     }
14631 });
14632
14633 Roo.apply(Roo.bootstrap.ComboBox,  {
14634     
14635     touchViewTemplate : {
14636         tag: 'div',
14637         cls: 'modal fade roo-combobox-touch-view',
14638         cn: [
14639             {
14640                 tag: 'div',
14641                 cls: 'modal-dialog',
14642                 style : 'position:fixed', // we have to fix position....
14643                 cn: [
14644                     {
14645                         tag: 'div',
14646                         cls: 'modal-content',
14647                         cn: [
14648                             Roo.bootstrap.ComboBox.header,
14649                             Roo.bootstrap.ComboBox.body,
14650                             Roo.bootstrap.ComboBox.footer
14651                         ]
14652                     }
14653                 ]
14654             }
14655         ]
14656     }
14657 });/*
14658  * Based on:
14659  * Ext JS Library 1.1.1
14660  * Copyright(c) 2006-2007, Ext JS, LLC.
14661  *
14662  * Originally Released Under LGPL - original licence link has changed is not relivant.
14663  *
14664  * Fork - LGPL
14665  * <script type="text/javascript">
14666  */
14667
14668 /**
14669  * @class Roo.View
14670  * @extends Roo.util.Observable
14671  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14672  * This class also supports single and multi selection modes. <br>
14673  * Create a data model bound view:
14674  <pre><code>
14675  var store = new Roo.data.Store(...);
14676
14677  var view = new Roo.View({
14678     el : "my-element",
14679     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14680  
14681     singleSelect: true,
14682     selectedClass: "ydataview-selected",
14683     store: store
14684  });
14685
14686  // listen for node click?
14687  view.on("click", function(vw, index, node, e){
14688  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14689  });
14690
14691  // load XML data
14692  dataModel.load("foobar.xml");
14693  </code></pre>
14694  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14695  * <br><br>
14696  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14697  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14698  * 
14699  * Note: old style constructor is still suported (container, template, config)
14700  * 
14701  * @constructor
14702  * Create a new View
14703  * @param {Object} config The config object
14704  * 
14705  */
14706 Roo.View = function(config, depreciated_tpl, depreciated_config){
14707     
14708     this.parent = false;
14709     
14710     if (typeof(depreciated_tpl) == 'undefined') {
14711         // new way.. - universal constructor.
14712         Roo.apply(this, config);
14713         this.el  = Roo.get(this.el);
14714     } else {
14715         // old format..
14716         this.el  = Roo.get(config);
14717         this.tpl = depreciated_tpl;
14718         Roo.apply(this, depreciated_config);
14719     }
14720     this.wrapEl  = this.el.wrap().wrap();
14721     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14722     
14723     
14724     if(typeof(this.tpl) == "string"){
14725         this.tpl = new Roo.Template(this.tpl);
14726     } else {
14727         // support xtype ctors..
14728         this.tpl = new Roo.factory(this.tpl, Roo);
14729     }
14730     
14731     
14732     this.tpl.compile();
14733     
14734     /** @private */
14735     this.addEvents({
14736         /**
14737          * @event beforeclick
14738          * Fires before a click is processed. Returns false to cancel the default action.
14739          * @param {Roo.View} this
14740          * @param {Number} index The index of the target node
14741          * @param {HTMLElement} node The target node
14742          * @param {Roo.EventObject} e The raw event object
14743          */
14744             "beforeclick" : true,
14745         /**
14746          * @event click
14747          * Fires when a template node is clicked.
14748          * @param {Roo.View} this
14749          * @param {Number} index The index of the target node
14750          * @param {HTMLElement} node The target node
14751          * @param {Roo.EventObject} e The raw event object
14752          */
14753             "click" : true,
14754         /**
14755          * @event dblclick
14756          * Fires when a template node is double clicked.
14757          * @param {Roo.View} this
14758          * @param {Number} index The index of the target node
14759          * @param {HTMLElement} node The target node
14760          * @param {Roo.EventObject} e The raw event object
14761          */
14762             "dblclick" : true,
14763         /**
14764          * @event contextmenu
14765          * Fires when a template node is right clicked.
14766          * @param {Roo.View} this
14767          * @param {Number} index The index of the target node
14768          * @param {HTMLElement} node The target node
14769          * @param {Roo.EventObject} e The raw event object
14770          */
14771             "contextmenu" : true,
14772         /**
14773          * @event selectionchange
14774          * Fires when the selected nodes change.
14775          * @param {Roo.View} this
14776          * @param {Array} selections Array of the selected nodes
14777          */
14778             "selectionchange" : true,
14779     
14780         /**
14781          * @event beforeselect
14782          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14783          * @param {Roo.View} this
14784          * @param {HTMLElement} node The node to be selected
14785          * @param {Array} selections Array of currently selected nodes
14786          */
14787             "beforeselect" : true,
14788         /**
14789          * @event preparedata
14790          * Fires on every row to render, to allow you to change the data.
14791          * @param {Roo.View} this
14792          * @param {Object} data to be rendered (change this)
14793          */
14794           "preparedata" : true
14795           
14796           
14797         });
14798
14799
14800
14801     this.el.on({
14802         "click": this.onClick,
14803         "dblclick": this.onDblClick,
14804         "contextmenu": this.onContextMenu,
14805         scope:this
14806     });
14807
14808     this.selections = [];
14809     this.nodes = [];
14810     this.cmp = new Roo.CompositeElementLite([]);
14811     if(this.store){
14812         this.store = Roo.factory(this.store, Roo.data);
14813         this.setStore(this.store, true);
14814     }
14815     
14816     if ( this.footer && this.footer.xtype) {
14817            
14818          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14819         
14820         this.footer.dataSource = this.store;
14821         this.footer.container = fctr;
14822         this.footer = Roo.factory(this.footer, Roo);
14823         fctr.insertFirst(this.el);
14824         
14825         // this is a bit insane - as the paging toolbar seems to detach the el..
14826 //        dom.parentNode.parentNode.parentNode
14827          // they get detached?
14828     }
14829     
14830     
14831     Roo.View.superclass.constructor.call(this);
14832     
14833     
14834 };
14835
14836 Roo.extend(Roo.View, Roo.util.Observable, {
14837     
14838      /**
14839      * @cfg {Roo.data.Store} store Data store to load data from.
14840      */
14841     store : false,
14842     
14843     /**
14844      * @cfg {String|Roo.Element} el The container element.
14845      */
14846     el : '',
14847     
14848     /**
14849      * @cfg {String|Roo.Template} tpl The template used by this View 
14850      */
14851     tpl : false,
14852     /**
14853      * @cfg {String} dataName the named area of the template to use as the data area
14854      *                          Works with domtemplates roo-name="name"
14855      */
14856     dataName: false,
14857     /**
14858      * @cfg {String} selectedClass The css class to add to selected nodes
14859      */
14860     selectedClass : "x-view-selected",
14861      /**
14862      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14863      */
14864     emptyText : "",
14865     
14866     /**
14867      * @cfg {String} text to display on mask (default Loading)
14868      */
14869     mask : false,
14870     /**
14871      * @cfg {Boolean} multiSelect Allow multiple selection
14872      */
14873     multiSelect : false,
14874     /**
14875      * @cfg {Boolean} singleSelect Allow single selection
14876      */
14877     singleSelect:  false,
14878     
14879     /**
14880      * @cfg {Boolean} toggleSelect - selecting 
14881      */
14882     toggleSelect : false,
14883     
14884     /**
14885      * @cfg {Boolean} tickable - selecting 
14886      */
14887     tickable : false,
14888     
14889     /**
14890      * Returns the element this view is bound to.
14891      * @return {Roo.Element}
14892      */
14893     getEl : function(){
14894         return this.wrapEl;
14895     },
14896     
14897     
14898
14899     /**
14900      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14901      */
14902     refresh : function(){
14903         //Roo.log('refresh');
14904         var t = this.tpl;
14905         
14906         // if we are using something like 'domtemplate', then
14907         // the what gets used is:
14908         // t.applySubtemplate(NAME, data, wrapping data..)
14909         // the outer template then get' applied with
14910         //     the store 'extra data'
14911         // and the body get's added to the
14912         //      roo-name="data" node?
14913         //      <span class='roo-tpl-{name}'></span> ?????
14914         
14915         
14916         
14917         this.clearSelections();
14918         this.el.update("");
14919         var html = [];
14920         var records = this.store.getRange();
14921         if(records.length < 1) {
14922             
14923             // is this valid??  = should it render a template??
14924             
14925             this.el.update(this.emptyText);
14926             return;
14927         }
14928         var el = this.el;
14929         if (this.dataName) {
14930             this.el.update(t.apply(this.store.meta)); //????
14931             el = this.el.child('.roo-tpl-' + this.dataName);
14932         }
14933         
14934         for(var i = 0, len = records.length; i < len; i++){
14935             var data = this.prepareData(records[i].data, i, records[i]);
14936             this.fireEvent("preparedata", this, data, i, records[i]);
14937             
14938             var d = Roo.apply({}, data);
14939             
14940             if(this.tickable){
14941                 Roo.apply(d, {'roo-id' : Roo.id()});
14942                 
14943                 var _this = this;
14944             
14945                 Roo.each(this.parent.item, function(item){
14946                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14947                         return;
14948                     }
14949                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14950                 });
14951             }
14952             
14953             html[html.length] = Roo.util.Format.trim(
14954                 this.dataName ?
14955                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14956                     t.apply(d)
14957             );
14958         }
14959         
14960         
14961         
14962         el.update(html.join(""));
14963         this.nodes = el.dom.childNodes;
14964         this.updateIndexes(0);
14965     },
14966     
14967
14968     /**
14969      * Function to override to reformat the data that is sent to
14970      * the template for each node.
14971      * DEPRICATED - use the preparedata event handler.
14972      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14973      * a JSON object for an UpdateManager bound view).
14974      */
14975     prepareData : function(data, index, record)
14976     {
14977         this.fireEvent("preparedata", this, data, index, record);
14978         return data;
14979     },
14980
14981     onUpdate : function(ds, record){
14982         // Roo.log('on update');   
14983         this.clearSelections();
14984         var index = this.store.indexOf(record);
14985         var n = this.nodes[index];
14986         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14987         n.parentNode.removeChild(n);
14988         this.updateIndexes(index, index);
14989     },
14990
14991     
14992     
14993 // --------- FIXME     
14994     onAdd : function(ds, records, index)
14995     {
14996         //Roo.log(['on Add', ds, records, index] );        
14997         this.clearSelections();
14998         if(this.nodes.length == 0){
14999             this.refresh();
15000             return;
15001         }
15002         var n = this.nodes[index];
15003         for(var i = 0, len = records.length; i < len; i++){
15004             var d = this.prepareData(records[i].data, i, records[i]);
15005             if(n){
15006                 this.tpl.insertBefore(n, d);
15007             }else{
15008                 
15009                 this.tpl.append(this.el, d);
15010             }
15011         }
15012         this.updateIndexes(index);
15013     },
15014
15015     onRemove : function(ds, record, index){
15016        // Roo.log('onRemove');
15017         this.clearSelections();
15018         var el = this.dataName  ?
15019             this.el.child('.roo-tpl-' + this.dataName) :
15020             this.el; 
15021         
15022         el.dom.removeChild(this.nodes[index]);
15023         this.updateIndexes(index);
15024     },
15025
15026     /**
15027      * Refresh an individual node.
15028      * @param {Number} index
15029      */
15030     refreshNode : function(index){
15031         this.onUpdate(this.store, this.store.getAt(index));
15032     },
15033
15034     updateIndexes : function(startIndex, endIndex){
15035         var ns = this.nodes;
15036         startIndex = startIndex || 0;
15037         endIndex = endIndex || ns.length - 1;
15038         for(var i = startIndex; i <= endIndex; i++){
15039             ns[i].nodeIndex = i;
15040         }
15041     },
15042
15043     /**
15044      * Changes the data store this view uses and refresh the view.
15045      * @param {Store} store
15046      */
15047     setStore : function(store, initial){
15048         if(!initial && this.store){
15049             this.store.un("datachanged", this.refresh);
15050             this.store.un("add", this.onAdd);
15051             this.store.un("remove", this.onRemove);
15052             this.store.un("update", this.onUpdate);
15053             this.store.un("clear", this.refresh);
15054             this.store.un("beforeload", this.onBeforeLoad);
15055             this.store.un("load", this.onLoad);
15056             this.store.un("loadexception", this.onLoad);
15057         }
15058         if(store){
15059           
15060             store.on("datachanged", this.refresh, this);
15061             store.on("add", this.onAdd, this);
15062             store.on("remove", this.onRemove, this);
15063             store.on("update", this.onUpdate, this);
15064             store.on("clear", this.refresh, this);
15065             store.on("beforeload", this.onBeforeLoad, this);
15066             store.on("load", this.onLoad, this);
15067             store.on("loadexception", this.onLoad, this);
15068         }
15069         
15070         if(store){
15071             this.refresh();
15072         }
15073     },
15074     /**
15075      * onbeforeLoad - masks the loading area.
15076      *
15077      */
15078     onBeforeLoad : function(store,opts)
15079     {
15080          //Roo.log('onBeforeLoad');   
15081         if (!opts.add) {
15082             this.el.update("");
15083         }
15084         this.el.mask(this.mask ? this.mask : "Loading" ); 
15085     },
15086     onLoad : function ()
15087     {
15088         this.el.unmask();
15089     },
15090     
15091
15092     /**
15093      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15094      * @param {HTMLElement} node
15095      * @return {HTMLElement} The template node
15096      */
15097     findItemFromChild : function(node){
15098         var el = this.dataName  ?
15099             this.el.child('.roo-tpl-' + this.dataName,true) :
15100             this.el.dom; 
15101         
15102         if(!node || node.parentNode == el){
15103                     return node;
15104             }
15105             var p = node.parentNode;
15106             while(p && p != el){
15107             if(p.parentNode == el){
15108                 return p;
15109             }
15110             p = p.parentNode;
15111         }
15112             return null;
15113     },
15114
15115     /** @ignore */
15116     onClick : function(e){
15117         var item = this.findItemFromChild(e.getTarget());
15118         if(item){
15119             var index = this.indexOf(item);
15120             if(this.onItemClick(item, index, e) !== false){
15121                 this.fireEvent("click", this, index, item, e);
15122             }
15123         }else{
15124             this.clearSelections();
15125         }
15126     },
15127
15128     /** @ignore */
15129     onContextMenu : function(e){
15130         var item = this.findItemFromChild(e.getTarget());
15131         if(item){
15132             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15133         }
15134     },
15135
15136     /** @ignore */
15137     onDblClick : function(e){
15138         var item = this.findItemFromChild(e.getTarget());
15139         if(item){
15140             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15141         }
15142     },
15143
15144     onItemClick : function(item, index, e)
15145     {
15146         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15147             return false;
15148         }
15149         if (this.toggleSelect) {
15150             var m = this.isSelected(item) ? 'unselect' : 'select';
15151             //Roo.log(m);
15152             var _t = this;
15153             _t[m](item, true, false);
15154             return true;
15155         }
15156         if(this.multiSelect || this.singleSelect){
15157             if(this.multiSelect && e.shiftKey && this.lastSelection){
15158                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15159             }else{
15160                 this.select(item, this.multiSelect && e.ctrlKey);
15161                 this.lastSelection = item;
15162             }
15163             
15164             if(!this.tickable){
15165                 e.preventDefault();
15166             }
15167             
15168         }
15169         return true;
15170     },
15171
15172     /**
15173      * Get the number of selected nodes.
15174      * @return {Number}
15175      */
15176     getSelectionCount : function(){
15177         return this.selections.length;
15178     },
15179
15180     /**
15181      * Get the currently selected nodes.
15182      * @return {Array} An array of HTMLElements
15183      */
15184     getSelectedNodes : function(){
15185         return this.selections;
15186     },
15187
15188     /**
15189      * Get the indexes of the selected nodes.
15190      * @return {Array}
15191      */
15192     getSelectedIndexes : function(){
15193         var indexes = [], s = this.selections;
15194         for(var i = 0, len = s.length; i < len; i++){
15195             indexes.push(s[i].nodeIndex);
15196         }
15197         return indexes;
15198     },
15199
15200     /**
15201      * Clear all selections
15202      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15203      */
15204     clearSelections : function(suppressEvent){
15205         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15206             this.cmp.elements = this.selections;
15207             this.cmp.removeClass(this.selectedClass);
15208             this.selections = [];
15209             if(!suppressEvent){
15210                 this.fireEvent("selectionchange", this, this.selections);
15211             }
15212         }
15213     },
15214
15215     /**
15216      * Returns true if the passed node is selected
15217      * @param {HTMLElement/Number} node The node or node index
15218      * @return {Boolean}
15219      */
15220     isSelected : function(node){
15221         var s = this.selections;
15222         if(s.length < 1){
15223             return false;
15224         }
15225         node = this.getNode(node);
15226         return s.indexOf(node) !== -1;
15227     },
15228
15229     /**
15230      * Selects nodes.
15231      * @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
15232      * @param {Boolean} keepExisting (optional) true to keep existing selections
15233      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15234      */
15235     select : function(nodeInfo, keepExisting, suppressEvent){
15236         if(nodeInfo instanceof Array){
15237             if(!keepExisting){
15238                 this.clearSelections(true);
15239             }
15240             for(var i = 0, len = nodeInfo.length; i < len; i++){
15241                 this.select(nodeInfo[i], true, true);
15242             }
15243             return;
15244         } 
15245         var node = this.getNode(nodeInfo);
15246         if(!node || this.isSelected(node)){
15247             return; // already selected.
15248         }
15249         if(!keepExisting){
15250             this.clearSelections(true);
15251         }
15252         
15253         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15254             Roo.fly(node).addClass(this.selectedClass);
15255             this.selections.push(node);
15256             if(!suppressEvent){
15257                 this.fireEvent("selectionchange", this, this.selections);
15258             }
15259         }
15260         
15261         
15262     },
15263       /**
15264      * Unselects nodes.
15265      * @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
15266      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15267      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15268      */
15269     unselect : function(nodeInfo, keepExisting, suppressEvent)
15270     {
15271         if(nodeInfo instanceof Array){
15272             Roo.each(this.selections, function(s) {
15273                 this.unselect(s, nodeInfo);
15274             }, this);
15275             return;
15276         }
15277         var node = this.getNode(nodeInfo);
15278         if(!node || !this.isSelected(node)){
15279             //Roo.log("not selected");
15280             return; // not selected.
15281         }
15282         // fireevent???
15283         var ns = [];
15284         Roo.each(this.selections, function(s) {
15285             if (s == node ) {
15286                 Roo.fly(node).removeClass(this.selectedClass);
15287
15288                 return;
15289             }
15290             ns.push(s);
15291         },this);
15292         
15293         this.selections= ns;
15294         this.fireEvent("selectionchange", this, this.selections);
15295     },
15296
15297     /**
15298      * Gets a template node.
15299      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15300      * @return {HTMLElement} The node or null if it wasn't found
15301      */
15302     getNode : function(nodeInfo){
15303         if(typeof nodeInfo == "string"){
15304             return document.getElementById(nodeInfo);
15305         }else if(typeof nodeInfo == "number"){
15306             return this.nodes[nodeInfo];
15307         }
15308         return nodeInfo;
15309     },
15310
15311     /**
15312      * Gets a range template nodes.
15313      * @param {Number} startIndex
15314      * @param {Number} endIndex
15315      * @return {Array} An array of nodes
15316      */
15317     getNodes : function(start, end){
15318         var ns = this.nodes;
15319         start = start || 0;
15320         end = typeof end == "undefined" ? ns.length - 1 : end;
15321         var nodes = [];
15322         if(start <= end){
15323             for(var i = start; i <= end; i++){
15324                 nodes.push(ns[i]);
15325             }
15326         } else{
15327             for(var i = start; i >= end; i--){
15328                 nodes.push(ns[i]);
15329             }
15330         }
15331         return nodes;
15332     },
15333
15334     /**
15335      * Finds the index of the passed node
15336      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15337      * @return {Number} The index of the node or -1
15338      */
15339     indexOf : function(node){
15340         node = this.getNode(node);
15341         if(typeof node.nodeIndex == "number"){
15342             return node.nodeIndex;
15343         }
15344         var ns = this.nodes;
15345         for(var i = 0, len = ns.length; i < len; i++){
15346             if(ns[i] == node){
15347                 return i;
15348             }
15349         }
15350         return -1;
15351     }
15352 });
15353 /*
15354  * - LGPL
15355  *
15356  * based on jquery fullcalendar
15357  * 
15358  */
15359
15360 Roo.bootstrap = Roo.bootstrap || {};
15361 /**
15362  * @class Roo.bootstrap.Calendar
15363  * @extends Roo.bootstrap.Component
15364  * Bootstrap Calendar class
15365  * @cfg {Boolean} loadMask (true|false) default false
15366  * @cfg {Object} header generate the user specific header of the calendar, default false
15367
15368  * @constructor
15369  * Create a new Container
15370  * @param {Object} config The config object
15371  */
15372
15373
15374
15375 Roo.bootstrap.Calendar = function(config){
15376     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15377      this.addEvents({
15378         /**
15379              * @event select
15380              * Fires when a date is selected
15381              * @param {DatePicker} this
15382              * @param {Date} date The selected date
15383              */
15384         'select': true,
15385         /**
15386              * @event monthchange
15387              * Fires when the displayed month changes 
15388              * @param {DatePicker} this
15389              * @param {Date} date The selected month
15390              */
15391         'monthchange': true,
15392         /**
15393              * @event evententer
15394              * Fires when mouse over an event
15395              * @param {Calendar} this
15396              * @param {event} Event
15397              */
15398         'evententer': true,
15399         /**
15400              * @event eventleave
15401              * Fires when the mouse leaves an
15402              * @param {Calendar} this
15403              * @param {event}
15404              */
15405         'eventleave': true,
15406         /**
15407              * @event eventclick
15408              * Fires when the mouse click an
15409              * @param {Calendar} this
15410              * @param {event}
15411              */
15412         'eventclick': true
15413         
15414     });
15415
15416 };
15417
15418 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15419     
15420      /**
15421      * @cfg {Number} startDay
15422      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15423      */
15424     startDay : 0,
15425     
15426     loadMask : false,
15427     
15428     header : false,
15429       
15430     getAutoCreate : function(){
15431         
15432         
15433         var fc_button = function(name, corner, style, content ) {
15434             return Roo.apply({},{
15435                 tag : 'span',
15436                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15437                          (corner.length ?
15438                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15439                             ''
15440                         ),
15441                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15442                 unselectable: 'on'
15443             });
15444         };
15445         
15446         var header = {};
15447         
15448         if(!this.header){
15449             header = {
15450                 tag : 'table',
15451                 cls : 'fc-header',
15452                 style : 'width:100%',
15453                 cn : [
15454                     {
15455                         tag: 'tr',
15456                         cn : [
15457                             {
15458                                 tag : 'td',
15459                                 cls : 'fc-header-left',
15460                                 cn : [
15461                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15462                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15463                                     { tag: 'span', cls: 'fc-header-space' },
15464                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15465
15466
15467                                 ]
15468                             },
15469
15470                             {
15471                                 tag : 'td',
15472                                 cls : 'fc-header-center',
15473                                 cn : [
15474                                     {
15475                                         tag: 'span',
15476                                         cls: 'fc-header-title',
15477                                         cn : {
15478                                             tag: 'H2',
15479                                             html : 'month / year'
15480                                         }
15481                                     }
15482
15483                                 ]
15484                             },
15485                             {
15486                                 tag : 'td',
15487                                 cls : 'fc-header-right',
15488                                 cn : [
15489                               /*      fc_button('month', 'left', '', 'month' ),
15490                                     fc_button('week', '', '', 'week' ),
15491                                     fc_button('day', 'right', '', 'day' )
15492                                 */    
15493
15494                                 ]
15495                             }
15496
15497                         ]
15498                     }
15499                 ]
15500             };
15501         }
15502         
15503         header = this.header;
15504         
15505        
15506         var cal_heads = function() {
15507             var ret = [];
15508             // fixme - handle this.
15509             
15510             for (var i =0; i < Date.dayNames.length; i++) {
15511                 var d = Date.dayNames[i];
15512                 ret.push({
15513                     tag: 'th',
15514                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15515                     html : d.substring(0,3)
15516                 });
15517                 
15518             }
15519             ret[0].cls += ' fc-first';
15520             ret[6].cls += ' fc-last';
15521             return ret;
15522         };
15523         var cal_cell = function(n) {
15524             return  {
15525                 tag: 'td',
15526                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15527                 cn : [
15528                     {
15529                         cn : [
15530                             {
15531                                 cls: 'fc-day-number',
15532                                 html: 'D'
15533                             },
15534                             {
15535                                 cls: 'fc-day-content',
15536                              
15537                                 cn : [
15538                                      {
15539                                         style: 'position: relative;' // height: 17px;
15540                                     }
15541                                 ]
15542                             }
15543                             
15544                             
15545                         ]
15546                     }
15547                 ]
15548                 
15549             }
15550         };
15551         var cal_rows = function() {
15552             
15553             var ret = [];
15554             for (var r = 0; r < 6; r++) {
15555                 var row= {
15556                     tag : 'tr',
15557                     cls : 'fc-week',
15558                     cn : []
15559                 };
15560                 
15561                 for (var i =0; i < Date.dayNames.length; i++) {
15562                     var d = Date.dayNames[i];
15563                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15564
15565                 }
15566                 row.cn[0].cls+=' fc-first';
15567                 row.cn[0].cn[0].style = 'min-height:90px';
15568                 row.cn[6].cls+=' fc-last';
15569                 ret.push(row);
15570                 
15571             }
15572             ret[0].cls += ' fc-first';
15573             ret[4].cls += ' fc-prev-last';
15574             ret[5].cls += ' fc-last';
15575             return ret;
15576             
15577         };
15578         
15579         var cal_table = {
15580             tag: 'table',
15581             cls: 'fc-border-separate',
15582             style : 'width:100%',
15583             cellspacing  : 0,
15584             cn : [
15585                 { 
15586                     tag: 'thead',
15587                     cn : [
15588                         { 
15589                             tag: 'tr',
15590                             cls : 'fc-first fc-last',
15591                             cn : cal_heads()
15592                         }
15593                     ]
15594                 },
15595                 { 
15596                     tag: 'tbody',
15597                     cn : cal_rows()
15598                 }
15599                   
15600             ]
15601         };
15602          
15603          var cfg = {
15604             cls : 'fc fc-ltr',
15605             cn : [
15606                 header,
15607                 {
15608                     cls : 'fc-content',
15609                     style : "position: relative;",
15610                     cn : [
15611                         {
15612                             cls : 'fc-view fc-view-month fc-grid',
15613                             style : 'position: relative',
15614                             unselectable : 'on',
15615                             cn : [
15616                                 {
15617                                     cls : 'fc-event-container',
15618                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15619                                 },
15620                                 cal_table
15621                             ]
15622                         }
15623                     ]
15624     
15625                 }
15626            ] 
15627             
15628         };
15629         
15630          
15631         
15632         return cfg;
15633     },
15634     
15635     
15636     initEvents : function()
15637     {
15638         if(!this.store){
15639             throw "can not find store for calendar";
15640         }
15641         
15642         var mark = {
15643             tag: "div",
15644             cls:"x-dlg-mask",
15645             style: "text-align:center",
15646             cn: [
15647                 {
15648                     tag: "div",
15649                     style: "background-color:white;width:50%;margin:250 auto",
15650                     cn: [
15651                         {
15652                             tag: "img",
15653                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15654                         },
15655                         {
15656                             tag: "span",
15657                             html: "Loading"
15658                         }
15659                         
15660                     ]
15661                 }
15662             ]
15663         };
15664         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15665         
15666         var size = this.el.select('.fc-content', true).first().getSize();
15667         this.maskEl.setSize(size.width, size.height);
15668         this.maskEl.enableDisplayMode("block");
15669         if(!this.loadMask){
15670             this.maskEl.hide();
15671         }
15672         
15673         this.store = Roo.factory(this.store, Roo.data);
15674         this.store.on('load', this.onLoad, this);
15675         this.store.on('beforeload', this.onBeforeLoad, this);
15676         
15677         this.resize();
15678         
15679         this.cells = this.el.select('.fc-day',true);
15680         //Roo.log(this.cells);
15681         this.textNodes = this.el.query('.fc-day-number');
15682         this.cells.addClassOnOver('fc-state-hover');
15683         
15684         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15685         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15686         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15687         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15688         
15689         this.on('monthchange', this.onMonthChange, this);
15690         
15691         this.update(new Date().clearTime());
15692     },
15693     
15694     resize : function() {
15695         var sz  = this.el.getSize();
15696         
15697         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15698         this.el.select('.fc-day-content div',true).setHeight(34);
15699     },
15700     
15701     
15702     // private
15703     showPrevMonth : function(e){
15704         this.update(this.activeDate.add("mo", -1));
15705     },
15706     showToday : function(e){
15707         this.update(new Date().clearTime());
15708     },
15709     // private
15710     showNextMonth : function(e){
15711         this.update(this.activeDate.add("mo", 1));
15712     },
15713
15714     // private
15715     showPrevYear : function(){
15716         this.update(this.activeDate.add("y", -1));
15717     },
15718
15719     // private
15720     showNextYear : function(){
15721         this.update(this.activeDate.add("y", 1));
15722     },
15723
15724     
15725    // private
15726     update : function(date)
15727     {
15728         var vd = this.activeDate;
15729         this.activeDate = date;
15730 //        if(vd && this.el){
15731 //            var t = date.getTime();
15732 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15733 //                Roo.log('using add remove');
15734 //                
15735 //                this.fireEvent('monthchange', this, date);
15736 //                
15737 //                this.cells.removeClass("fc-state-highlight");
15738 //                this.cells.each(function(c){
15739 //                   if(c.dateValue == t){
15740 //                       c.addClass("fc-state-highlight");
15741 //                       setTimeout(function(){
15742 //                            try{c.dom.firstChild.focus();}catch(e){}
15743 //                       }, 50);
15744 //                       return false;
15745 //                   }
15746 //                   return true;
15747 //                });
15748 //                return;
15749 //            }
15750 //        }
15751         
15752         var days = date.getDaysInMonth();
15753         
15754         var firstOfMonth = date.getFirstDateOfMonth();
15755         var startingPos = firstOfMonth.getDay()-this.startDay;
15756         
15757         if(startingPos < this.startDay){
15758             startingPos += 7;
15759         }
15760         
15761         var pm = date.add(Date.MONTH, -1);
15762         var prevStart = pm.getDaysInMonth()-startingPos;
15763 //        
15764         this.cells = this.el.select('.fc-day',true);
15765         this.textNodes = this.el.query('.fc-day-number');
15766         this.cells.addClassOnOver('fc-state-hover');
15767         
15768         var cells = this.cells.elements;
15769         var textEls = this.textNodes;
15770         
15771         Roo.each(cells, function(cell){
15772             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15773         });
15774         
15775         days += startingPos;
15776
15777         // convert everything to numbers so it's fast
15778         var day = 86400000;
15779         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15780         //Roo.log(d);
15781         //Roo.log(pm);
15782         //Roo.log(prevStart);
15783         
15784         var today = new Date().clearTime().getTime();
15785         var sel = date.clearTime().getTime();
15786         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15787         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15788         var ddMatch = this.disabledDatesRE;
15789         var ddText = this.disabledDatesText;
15790         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15791         var ddaysText = this.disabledDaysText;
15792         var format = this.format;
15793         
15794         var setCellClass = function(cal, cell){
15795             cell.row = 0;
15796             cell.events = [];
15797             cell.more = [];
15798             //Roo.log('set Cell Class');
15799             cell.title = "";
15800             var t = d.getTime();
15801             
15802             //Roo.log(d);
15803             
15804             cell.dateValue = t;
15805             if(t == today){
15806                 cell.className += " fc-today";
15807                 cell.className += " fc-state-highlight";
15808                 cell.title = cal.todayText;
15809             }
15810             if(t == sel){
15811                 // disable highlight in other month..
15812                 //cell.className += " fc-state-highlight";
15813                 
15814             }
15815             // disabling
15816             if(t < min) {
15817                 cell.className = " fc-state-disabled";
15818                 cell.title = cal.minText;
15819                 return;
15820             }
15821             if(t > max) {
15822                 cell.className = " fc-state-disabled";
15823                 cell.title = cal.maxText;
15824                 return;
15825             }
15826             if(ddays){
15827                 if(ddays.indexOf(d.getDay()) != -1){
15828                     cell.title = ddaysText;
15829                     cell.className = " fc-state-disabled";
15830                 }
15831             }
15832             if(ddMatch && format){
15833                 var fvalue = d.dateFormat(format);
15834                 if(ddMatch.test(fvalue)){
15835                     cell.title = ddText.replace("%0", fvalue);
15836                     cell.className = " fc-state-disabled";
15837                 }
15838             }
15839             
15840             if (!cell.initialClassName) {
15841                 cell.initialClassName = cell.dom.className;
15842             }
15843             
15844             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15845         };
15846
15847         var i = 0;
15848         
15849         for(; i < startingPos; i++) {
15850             textEls[i].innerHTML = (++prevStart);
15851             d.setDate(d.getDate()+1);
15852             
15853             cells[i].className = "fc-past fc-other-month";
15854             setCellClass(this, cells[i]);
15855         }
15856         
15857         var intDay = 0;
15858         
15859         for(; i < days; i++){
15860             intDay = i - startingPos + 1;
15861             textEls[i].innerHTML = (intDay);
15862             d.setDate(d.getDate()+1);
15863             
15864             cells[i].className = ''; // "x-date-active";
15865             setCellClass(this, cells[i]);
15866         }
15867         var extraDays = 0;
15868         
15869         for(; i < 42; i++) {
15870             textEls[i].innerHTML = (++extraDays);
15871             d.setDate(d.getDate()+1);
15872             
15873             cells[i].className = "fc-future fc-other-month";
15874             setCellClass(this, cells[i]);
15875         }
15876         
15877         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15878         
15879         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15880         
15881         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15882         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15883         
15884         if(totalRows != 6){
15885             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15886             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15887         }
15888         
15889         this.fireEvent('monthchange', this, date);
15890         
15891         
15892         /*
15893         if(!this.internalRender){
15894             var main = this.el.dom.firstChild;
15895             var w = main.offsetWidth;
15896             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15897             Roo.fly(main).setWidth(w);
15898             this.internalRender = true;
15899             // opera does not respect the auto grow header center column
15900             // then, after it gets a width opera refuses to recalculate
15901             // without a second pass
15902             if(Roo.isOpera && !this.secondPass){
15903                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15904                 this.secondPass = true;
15905                 this.update.defer(10, this, [date]);
15906             }
15907         }
15908         */
15909         
15910     },
15911     
15912     findCell : function(dt) {
15913         dt = dt.clearTime().getTime();
15914         var ret = false;
15915         this.cells.each(function(c){
15916             //Roo.log("check " +c.dateValue + '?=' + dt);
15917             if(c.dateValue == dt){
15918                 ret = c;
15919                 return false;
15920             }
15921             return true;
15922         });
15923         
15924         return ret;
15925     },
15926     
15927     findCells : function(ev) {
15928         var s = ev.start.clone().clearTime().getTime();
15929        // Roo.log(s);
15930         var e= ev.end.clone().clearTime().getTime();
15931        // Roo.log(e);
15932         var ret = [];
15933         this.cells.each(function(c){
15934              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15935             
15936             if(c.dateValue > e){
15937                 return ;
15938             }
15939             if(c.dateValue < s){
15940                 return ;
15941             }
15942             ret.push(c);
15943         });
15944         
15945         return ret;    
15946     },
15947     
15948 //    findBestRow: function(cells)
15949 //    {
15950 //        var ret = 0;
15951 //        
15952 //        for (var i =0 ; i < cells.length;i++) {
15953 //            ret  = Math.max(cells[i].rows || 0,ret);
15954 //        }
15955 //        return ret;
15956 //        
15957 //    },
15958     
15959     
15960     addItem : function(ev)
15961     {
15962         // look for vertical location slot in
15963         var cells = this.findCells(ev);
15964         
15965 //        ev.row = this.findBestRow(cells);
15966         
15967         // work out the location.
15968         
15969         var crow = false;
15970         var rows = [];
15971         for(var i =0; i < cells.length; i++) {
15972             
15973             cells[i].row = cells[0].row;
15974             
15975             if(i == 0){
15976                 cells[i].row = cells[i].row + 1;
15977             }
15978             
15979             if (!crow) {
15980                 crow = {
15981                     start : cells[i],
15982                     end :  cells[i]
15983                 };
15984                 continue;
15985             }
15986             if (crow.start.getY() == cells[i].getY()) {
15987                 // on same row.
15988                 crow.end = cells[i];
15989                 continue;
15990             }
15991             // different row.
15992             rows.push(crow);
15993             crow = {
15994                 start: cells[i],
15995                 end : cells[i]
15996             };
15997             
15998         }
15999         
16000         rows.push(crow);
16001         ev.els = [];
16002         ev.rows = rows;
16003         ev.cells = cells;
16004         
16005         cells[0].events.push(ev);
16006         
16007         this.calevents.push(ev);
16008     },
16009     
16010     clearEvents: function() {
16011         
16012         if(!this.calevents){
16013             return;
16014         }
16015         
16016         Roo.each(this.cells.elements, function(c){
16017             c.row = 0;
16018             c.events = [];
16019             c.more = [];
16020         });
16021         
16022         Roo.each(this.calevents, function(e) {
16023             Roo.each(e.els, function(el) {
16024                 el.un('mouseenter' ,this.onEventEnter, this);
16025                 el.un('mouseleave' ,this.onEventLeave, this);
16026                 el.remove();
16027             },this);
16028         },this);
16029         
16030         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16031             e.remove();
16032         });
16033         
16034     },
16035     
16036     renderEvents: function()
16037     {   
16038         var _this = this;
16039         
16040         this.cells.each(function(c) {
16041             
16042             if(c.row < 5){
16043                 return;
16044             }
16045             
16046             var ev = c.events;
16047             
16048             var r = 4;
16049             if(c.row != c.events.length){
16050                 r = 4 - (4 - (c.row - c.events.length));
16051             }
16052             
16053             c.events = ev.slice(0, r);
16054             c.more = ev.slice(r);
16055             
16056             if(c.more.length && c.more.length == 1){
16057                 c.events.push(c.more.pop());
16058             }
16059             
16060             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16061             
16062         });
16063             
16064         this.cells.each(function(c) {
16065             
16066             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16067             
16068             
16069             for (var e = 0; e < c.events.length; e++){
16070                 var ev = c.events[e];
16071                 var rows = ev.rows;
16072                 
16073                 for(var i = 0; i < rows.length; i++) {
16074                 
16075                     // how many rows should it span..
16076
16077                     var  cfg = {
16078                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16079                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16080
16081                         unselectable : "on",
16082                         cn : [
16083                             {
16084                                 cls: 'fc-event-inner',
16085                                 cn : [
16086     //                                {
16087     //                                  tag:'span',
16088     //                                  cls: 'fc-event-time',
16089     //                                  html : cells.length > 1 ? '' : ev.time
16090     //                                },
16091                                     {
16092                                       tag:'span',
16093                                       cls: 'fc-event-title',
16094                                       html : String.format('{0}', ev.title)
16095                                     }
16096
16097
16098                                 ]
16099                             },
16100                             {
16101                                 cls: 'ui-resizable-handle ui-resizable-e',
16102                                 html : '&nbsp;&nbsp;&nbsp'
16103                             }
16104
16105                         ]
16106                     };
16107
16108                     if (i == 0) {
16109                         cfg.cls += ' fc-event-start';
16110                     }
16111                     if ((i+1) == rows.length) {
16112                         cfg.cls += ' fc-event-end';
16113                     }
16114
16115                     var ctr = _this.el.select('.fc-event-container',true).first();
16116                     var cg = ctr.createChild(cfg);
16117
16118                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16119                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16120
16121                     var r = (c.more.length) ? 1 : 0;
16122                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16123                     cg.setWidth(ebox.right - sbox.x -2);
16124
16125                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16126                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16127                     cg.on('click', _this.onEventClick, _this, ev);
16128
16129                     ev.els.push(cg);
16130                     
16131                 }
16132                 
16133             }
16134             
16135             
16136             if(c.more.length){
16137                 var  cfg = {
16138                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16139                     style : 'position: absolute',
16140                     unselectable : "on",
16141                     cn : [
16142                         {
16143                             cls: 'fc-event-inner',
16144                             cn : [
16145                                 {
16146                                   tag:'span',
16147                                   cls: 'fc-event-title',
16148                                   html : 'More'
16149                                 }
16150
16151
16152                             ]
16153                         },
16154                         {
16155                             cls: 'ui-resizable-handle ui-resizable-e',
16156                             html : '&nbsp;&nbsp;&nbsp'
16157                         }
16158
16159                     ]
16160                 };
16161
16162                 var ctr = _this.el.select('.fc-event-container',true).first();
16163                 var cg = ctr.createChild(cfg);
16164
16165                 var sbox = c.select('.fc-day-content',true).first().getBox();
16166                 var ebox = c.select('.fc-day-content',true).first().getBox();
16167                 //Roo.log(cg);
16168                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16169                 cg.setWidth(ebox.right - sbox.x -2);
16170
16171                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16172                 
16173             }
16174             
16175         });
16176         
16177         
16178         
16179     },
16180     
16181     onEventEnter: function (e, el,event,d) {
16182         this.fireEvent('evententer', this, el, event);
16183     },
16184     
16185     onEventLeave: function (e, el,event,d) {
16186         this.fireEvent('eventleave', this, el, event);
16187     },
16188     
16189     onEventClick: function (e, el,event,d) {
16190         this.fireEvent('eventclick', this, el, event);
16191     },
16192     
16193     onMonthChange: function () {
16194         this.store.load();
16195     },
16196     
16197     onMoreEventClick: function(e, el, more)
16198     {
16199         var _this = this;
16200         
16201         this.calpopover.placement = 'right';
16202         this.calpopover.setTitle('More');
16203         
16204         this.calpopover.setContent('');
16205         
16206         var ctr = this.calpopover.el.select('.popover-content', true).first();
16207         
16208         Roo.each(more, function(m){
16209             var cfg = {
16210                 cls : 'fc-event-hori fc-event-draggable',
16211                 html : m.title
16212             };
16213             var cg = ctr.createChild(cfg);
16214             
16215             cg.on('click', _this.onEventClick, _this, m);
16216         });
16217         
16218         this.calpopover.show(el);
16219         
16220         
16221     },
16222     
16223     onLoad: function () 
16224     {   
16225         this.calevents = [];
16226         var cal = this;
16227         
16228         if(this.store.getCount() > 0){
16229             this.store.data.each(function(d){
16230                cal.addItem({
16231                     id : d.data.id,
16232                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16233                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16234                     time : d.data.start_time,
16235                     title : d.data.title,
16236                     description : d.data.description,
16237                     venue : d.data.venue
16238                 });
16239             });
16240         }
16241         
16242         this.renderEvents();
16243         
16244         if(this.calevents.length && this.loadMask){
16245             this.maskEl.hide();
16246         }
16247     },
16248     
16249     onBeforeLoad: function()
16250     {
16251         this.clearEvents();
16252         if(this.loadMask){
16253             this.maskEl.show();
16254         }
16255     }
16256 });
16257
16258  
16259  /*
16260  * - LGPL
16261  *
16262  * element
16263  * 
16264  */
16265
16266 /**
16267  * @class Roo.bootstrap.Popover
16268  * @extends Roo.bootstrap.Component
16269  * Bootstrap Popover class
16270  * @cfg {String} html contents of the popover   (or false to use children..)
16271  * @cfg {String} title of popover (or false to hide)
16272  * @cfg {String} placement how it is placed
16273  * @cfg {String} trigger click || hover (or false to trigger manually)
16274  * @cfg {String} over what (parent or false to trigger manually.)
16275  * @cfg {Number} delay - delay before showing
16276  
16277  * @constructor
16278  * Create a new Popover
16279  * @param {Object} config The config object
16280  */
16281
16282 Roo.bootstrap.Popover = function(config){
16283     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16284     
16285     this.addEvents({
16286         // raw events
16287          /**
16288          * @event show
16289          * After the popover show
16290          * 
16291          * @param {Roo.bootstrap.Popover} this
16292          */
16293         "show" : true,
16294         /**
16295          * @event hide
16296          * After the popover hide
16297          * 
16298          * @param {Roo.bootstrap.Popover} this
16299          */
16300         "hide" : true
16301     });
16302 };
16303
16304 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16305     
16306     title: 'Fill in a title',
16307     html: false,
16308     
16309     placement : 'right',
16310     trigger : 'hover', // hover
16311     
16312     delay : 0,
16313     
16314     over: 'parent',
16315     
16316     can_build_overlaid : false,
16317     
16318     getChildContainer : function()
16319     {
16320         return this.el.select('.popover-content',true).first();
16321     },
16322     
16323     getAutoCreate : function(){
16324          
16325         var cfg = {
16326            cls : 'popover roo-dynamic',
16327            style: 'display:block',
16328            cn : [
16329                 {
16330                     cls : 'arrow'
16331                 },
16332                 {
16333                     cls : 'popover-inner',
16334                     cn : [
16335                         {
16336                             tag: 'h3',
16337                             cls: 'popover-title',
16338                             html : this.title
16339                         },
16340                         {
16341                             cls : 'popover-content',
16342                             html : this.html
16343                         }
16344                     ]
16345                     
16346                 }
16347            ]
16348         };
16349         
16350         return cfg;
16351     },
16352     setTitle: function(str)
16353     {
16354         this.title = str;
16355         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16356     },
16357     setContent: function(str)
16358     {
16359         this.html = str;
16360         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16361     },
16362     // as it get's added to the bottom of the page.
16363     onRender : function(ct, position)
16364     {
16365         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16366         if(!this.el){
16367             var cfg = Roo.apply({},  this.getAutoCreate());
16368             cfg.id = Roo.id();
16369             
16370             if (this.cls) {
16371                 cfg.cls += ' ' + this.cls;
16372             }
16373             if (this.style) {
16374                 cfg.style = this.style;
16375             }
16376             //Roo.log("adding to ");
16377             this.el = Roo.get(document.body).createChild(cfg, position);
16378 //            Roo.log(this.el);
16379         }
16380         this.initEvents();
16381     },
16382     
16383     initEvents : function()
16384     {
16385         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16386         this.el.enableDisplayMode('block');
16387         this.el.hide();
16388         if (this.over === false) {
16389             return; 
16390         }
16391         if (this.triggers === false) {
16392             return;
16393         }
16394         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16395         var triggers = this.trigger ? this.trigger.split(' ') : [];
16396         Roo.each(triggers, function(trigger) {
16397         
16398             if (trigger == 'click') {
16399                 on_el.on('click', this.toggle, this);
16400             } else if (trigger != 'manual') {
16401                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16402                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16403       
16404                 on_el.on(eventIn  ,this.enter, this);
16405                 on_el.on(eventOut, this.leave, this);
16406             }
16407         }, this);
16408         
16409     },
16410     
16411     
16412     // private
16413     timeout : null,
16414     hoverState : null,
16415     
16416     toggle : function () {
16417         this.hoverState == 'in' ? this.leave() : this.enter();
16418     },
16419     
16420     enter : function () {
16421         
16422         clearTimeout(this.timeout);
16423     
16424         this.hoverState = 'in';
16425     
16426         if (!this.delay || !this.delay.show) {
16427             this.show();
16428             return;
16429         }
16430         var _t = this;
16431         this.timeout = setTimeout(function () {
16432             if (_t.hoverState == 'in') {
16433                 _t.show();
16434             }
16435         }, this.delay.show)
16436     },
16437     
16438     leave : function() {
16439         clearTimeout(this.timeout);
16440     
16441         this.hoverState = 'out';
16442     
16443         if (!this.delay || !this.delay.hide) {
16444             this.hide();
16445             return;
16446         }
16447         var _t = this;
16448         this.timeout = setTimeout(function () {
16449             if (_t.hoverState == 'out') {
16450                 _t.hide();
16451             }
16452         }, this.delay.hide)
16453     },
16454     
16455     show : function (on_el)
16456     {
16457         if (!on_el) {
16458             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16459         }
16460         
16461         // set content.
16462         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16463         if (this.html !== false) {
16464             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16465         }
16466         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16467         if (!this.title.length) {
16468             this.el.select('.popover-title',true).hide();
16469         }
16470         
16471         var placement = typeof this.placement == 'function' ?
16472             this.placement.call(this, this.el, on_el) :
16473             this.placement;
16474             
16475         var autoToken = /\s?auto?\s?/i;
16476         var autoPlace = autoToken.test(placement);
16477         if (autoPlace) {
16478             placement = placement.replace(autoToken, '') || 'top';
16479         }
16480         
16481         //this.el.detach()
16482         //this.el.setXY([0,0]);
16483         this.el.show();
16484         this.el.dom.style.display='block';
16485         this.el.addClass(placement);
16486         
16487         //this.el.appendTo(on_el);
16488         
16489         var p = this.getPosition();
16490         var box = this.el.getBox();
16491         
16492         if (autoPlace) {
16493             // fixme..
16494         }
16495         var align = Roo.bootstrap.Popover.alignment[placement];
16496         this.el.alignTo(on_el, align[0],align[1]);
16497         //var arrow = this.el.select('.arrow',true).first();
16498         //arrow.set(align[2], 
16499         
16500         this.el.addClass('in');
16501         
16502         
16503         if (this.el.hasClass('fade')) {
16504             // fade it?
16505         }
16506         
16507         this.hoverState = 'in';
16508         
16509         this.fireEvent('show', this);
16510         
16511     },
16512     hide : function()
16513     {
16514         this.el.setXY([0,0]);
16515         this.el.removeClass('in');
16516         this.el.hide();
16517         this.hoverState = null;
16518         
16519         this.fireEvent('hide', this);
16520     }
16521     
16522 });
16523
16524 Roo.bootstrap.Popover.alignment = {
16525     'left' : ['r-l', [-10,0], 'right'],
16526     'right' : ['l-r', [10,0], 'left'],
16527     'bottom' : ['t-b', [0,10], 'top'],
16528     'top' : [ 'b-t', [0,-10], 'bottom']
16529 };
16530
16531  /*
16532  * - LGPL
16533  *
16534  * Progress
16535  * 
16536  */
16537
16538 /**
16539  * @class Roo.bootstrap.Progress
16540  * @extends Roo.bootstrap.Component
16541  * Bootstrap Progress class
16542  * @cfg {Boolean} striped striped of the progress bar
16543  * @cfg {Boolean} active animated of the progress bar
16544  * 
16545  * 
16546  * @constructor
16547  * Create a new Progress
16548  * @param {Object} config The config object
16549  */
16550
16551 Roo.bootstrap.Progress = function(config){
16552     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16553 };
16554
16555 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16556     
16557     striped : false,
16558     active: false,
16559     
16560     getAutoCreate : function(){
16561         var cfg = {
16562             tag: 'div',
16563             cls: 'progress'
16564         };
16565         
16566         
16567         if(this.striped){
16568             cfg.cls += ' progress-striped';
16569         }
16570       
16571         if(this.active){
16572             cfg.cls += ' active';
16573         }
16574         
16575         
16576         return cfg;
16577     }
16578    
16579 });
16580
16581  
16582
16583  /*
16584  * - LGPL
16585  *
16586  * ProgressBar
16587  * 
16588  */
16589
16590 /**
16591  * @class Roo.bootstrap.ProgressBar
16592  * @extends Roo.bootstrap.Component
16593  * Bootstrap ProgressBar class
16594  * @cfg {Number} aria_valuenow aria-value now
16595  * @cfg {Number} aria_valuemin aria-value min
16596  * @cfg {Number} aria_valuemax aria-value max
16597  * @cfg {String} label label for the progress bar
16598  * @cfg {String} panel (success | info | warning | danger )
16599  * @cfg {String} role role of the progress bar
16600  * @cfg {String} sr_only text
16601  * 
16602  * 
16603  * @constructor
16604  * Create a new ProgressBar
16605  * @param {Object} config The config object
16606  */
16607
16608 Roo.bootstrap.ProgressBar = function(config){
16609     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16610 };
16611
16612 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16613     
16614     aria_valuenow : 0,
16615     aria_valuemin : 0,
16616     aria_valuemax : 100,
16617     label : false,
16618     panel : false,
16619     role : false,
16620     sr_only: false,
16621     
16622     getAutoCreate : function()
16623     {
16624         
16625         var cfg = {
16626             tag: 'div',
16627             cls: 'progress-bar',
16628             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16629         };
16630         
16631         if(this.sr_only){
16632             cfg.cn = {
16633                 tag: 'span',
16634                 cls: 'sr-only',
16635                 html: this.sr_only
16636             }
16637         }
16638         
16639         if(this.role){
16640             cfg.role = this.role;
16641         }
16642         
16643         if(this.aria_valuenow){
16644             cfg['aria-valuenow'] = this.aria_valuenow;
16645         }
16646         
16647         if(this.aria_valuemin){
16648             cfg['aria-valuemin'] = this.aria_valuemin;
16649         }
16650         
16651         if(this.aria_valuemax){
16652             cfg['aria-valuemax'] = this.aria_valuemax;
16653         }
16654         
16655         if(this.label && !this.sr_only){
16656             cfg.html = this.label;
16657         }
16658         
16659         if(this.panel){
16660             cfg.cls += ' progress-bar-' + this.panel;
16661         }
16662         
16663         return cfg;
16664     },
16665     
16666     update : function(aria_valuenow)
16667     {
16668         this.aria_valuenow = aria_valuenow;
16669         
16670         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16671     }
16672    
16673 });
16674
16675  
16676
16677  /*
16678  * - LGPL
16679  *
16680  * column
16681  * 
16682  */
16683
16684 /**
16685  * @class Roo.bootstrap.TabGroup
16686  * @extends Roo.bootstrap.Column
16687  * Bootstrap Column class
16688  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16689  * @cfg {Boolean} carousel true to make the group behave like a carousel
16690  * @cfg {Boolean} bullets show bullets for the panels
16691  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16692  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16693  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16694  * @cfg {Boolean} showarrow (true|false) show arrow default true
16695  * 
16696  * @constructor
16697  * Create a new TabGroup
16698  * @param {Object} config The config object
16699  */
16700
16701 Roo.bootstrap.TabGroup = function(config){
16702     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16703     if (!this.navId) {
16704         this.navId = Roo.id();
16705     }
16706     this.tabs = [];
16707     Roo.bootstrap.TabGroup.register(this);
16708     
16709 };
16710
16711 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16712     
16713     carousel : false,
16714     transition : false,
16715     bullets : 0,
16716     timer : 0,
16717     autoslide : false,
16718     slideFn : false,
16719     slideOnTouch : false,
16720     showarrow : true,
16721     
16722     getAutoCreate : function()
16723     {
16724         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16725         
16726         cfg.cls += ' tab-content';
16727         
16728         if (this.carousel) {
16729             cfg.cls += ' carousel slide';
16730             
16731             cfg.cn = [{
16732                cls : 'carousel-inner',
16733                cn : []
16734             }];
16735         
16736             if(this.bullets  && !Roo.isTouch){
16737                 
16738                 var bullets = {
16739                     cls : 'carousel-bullets',
16740                     cn : []
16741                 };
16742                
16743                 if(this.bullets_cls){
16744                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16745                 }
16746                 
16747                 bullets.cn.push({
16748                     cls : 'clear'
16749                 });
16750                 
16751                 cfg.cn[0].cn.push(bullets);
16752             }
16753             
16754             if(this.showarrow){
16755                 cfg.cn[0].cn.push({
16756                     tag : 'div',
16757                     class : 'carousel-arrow',
16758                     cn : [
16759                         {
16760                             tag : 'div',
16761                             class : 'carousel-prev',
16762                             cn : [
16763                                 {
16764                                     tag : 'i',
16765                                     class : 'fa fa-chevron-left'
16766                                 }
16767                             ]
16768                         },
16769                         {
16770                             tag : 'div',
16771                             class : 'carousel-next',
16772                             cn : [
16773                                 {
16774                                     tag : 'i',
16775                                     class : 'fa fa-chevron-right'
16776                                 }
16777                             ]
16778                         }
16779                     ]
16780                 });
16781             }
16782             
16783         }
16784         
16785         return cfg;
16786     },
16787     
16788     initEvents:  function()
16789     {
16790         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16791             this.el.on("touchstart", this.onTouchStart, this);
16792         }
16793         
16794         if(this.autoslide){
16795             var _this = this;
16796             
16797             this.slideFn = window.setInterval(function() {
16798                 _this.showPanelNext();
16799             }, this.timer);
16800         }
16801         
16802         if(this.showarrow){
16803             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16804             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16805         }
16806         
16807         
16808     },
16809     
16810     onTouchStart : function(e, el, o)
16811     {
16812         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16813             return;
16814         }
16815         
16816         this.showPanelNext();
16817     },
16818     
16819     getChildContainer : function()
16820     {
16821         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16822     },
16823     
16824     /**
16825     * register a Navigation item
16826     * @param {Roo.bootstrap.NavItem} the navitem to add
16827     */
16828     register : function(item)
16829     {
16830         this.tabs.push( item);
16831         item.navId = this.navId; // not really needed..
16832         this.addBullet();
16833     
16834     },
16835     
16836     getActivePanel : function()
16837     {
16838         var r = false;
16839         Roo.each(this.tabs, function(t) {
16840             if (t.active) {
16841                 r = t;
16842                 return false;
16843             }
16844             return null;
16845         });
16846         return r;
16847         
16848     },
16849     getPanelByName : function(n)
16850     {
16851         var r = false;
16852         Roo.each(this.tabs, function(t) {
16853             if (t.tabId == n) {
16854                 r = t;
16855                 return false;
16856             }
16857             return null;
16858         });
16859         return r;
16860     },
16861     indexOfPanel : function(p)
16862     {
16863         var r = false;
16864         Roo.each(this.tabs, function(t,i) {
16865             if (t.tabId == p.tabId) {
16866                 r = i;
16867                 return false;
16868             }
16869             return null;
16870         });
16871         return r;
16872     },
16873     /**
16874      * show a specific panel
16875      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16876      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16877      */
16878     showPanel : function (pan)
16879     {
16880         if(this.transition || typeof(pan) == 'undefined'){
16881             Roo.log("waiting for the transitionend");
16882             return;
16883         }
16884         
16885         if (typeof(pan) == 'number') {
16886             pan = this.tabs[pan];
16887         }
16888         
16889         if (typeof(pan) == 'string') {
16890             pan = this.getPanelByName(pan);
16891         }
16892         
16893         var cur = this.getActivePanel();
16894         
16895         if(!pan || !cur){
16896             Roo.log('pan or acitve pan is undefined');
16897             return false;
16898         }
16899         
16900         if (pan.tabId == this.getActivePanel().tabId) {
16901             return true;
16902         }
16903         
16904         if (false === cur.fireEvent('beforedeactivate')) {
16905             return false;
16906         }
16907         
16908         if(this.bullets > 0 && !Roo.isTouch){
16909             this.setActiveBullet(this.indexOfPanel(pan));
16910         }
16911         
16912         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16913             
16914             this.transition = true;
16915             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16916             var lr = dir == 'next' ? 'left' : 'right';
16917             pan.el.addClass(dir); // or prev
16918             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16919             cur.el.addClass(lr); // or right
16920             pan.el.addClass(lr);
16921             
16922             var _this = this;
16923             cur.el.on('transitionend', function() {
16924                 Roo.log("trans end?");
16925                 
16926                 pan.el.removeClass([lr,dir]);
16927                 pan.setActive(true);
16928                 
16929                 cur.el.removeClass([lr]);
16930                 cur.setActive(false);
16931                 
16932                 _this.transition = false;
16933                 
16934             }, this, { single:  true } );
16935             
16936             return true;
16937         }
16938         
16939         cur.setActive(false);
16940         pan.setActive(true);
16941         
16942         return true;
16943         
16944     },
16945     showPanelNext : function()
16946     {
16947         var i = this.indexOfPanel(this.getActivePanel());
16948         
16949         if (i >= this.tabs.length - 1 && !this.autoslide) {
16950             return;
16951         }
16952         
16953         if (i >= this.tabs.length - 1 && this.autoslide) {
16954             i = -1;
16955         }
16956         
16957         this.showPanel(this.tabs[i+1]);
16958     },
16959     
16960     showPanelPrev : function()
16961     {
16962         var i = this.indexOfPanel(this.getActivePanel());
16963         
16964         if (i  < 1 && !this.autoslide) {
16965             return;
16966         }
16967         
16968         if (i < 1 && this.autoslide) {
16969             i = this.tabs.length;
16970         }
16971         
16972         this.showPanel(this.tabs[i-1]);
16973     },
16974     
16975     
16976     addBullet: function()
16977     {
16978         if(!this.bullets || Roo.isTouch){
16979             return;
16980         }
16981         var ctr = this.el.select('.carousel-bullets',true).first();
16982         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16983         var bullet = ctr.createChild({
16984             cls : 'bullet bullet-' + i
16985         },ctr.dom.lastChild);
16986         
16987         
16988         var _this = this;
16989         
16990         bullet.on('click', (function(e, el, o, ii, t){
16991
16992             e.preventDefault();
16993
16994             this.showPanel(ii);
16995
16996             if(this.autoslide && this.slideFn){
16997                 clearInterval(this.slideFn);
16998                 this.slideFn = window.setInterval(function() {
16999                     _this.showPanelNext();
17000                 }, this.timer);
17001             }
17002
17003         }).createDelegate(this, [i, bullet], true));
17004                 
17005         
17006     },
17007      
17008     setActiveBullet : function(i)
17009     {
17010         if(Roo.isTouch){
17011             return;
17012         }
17013         
17014         Roo.each(this.el.select('.bullet', true).elements, function(el){
17015             el.removeClass('selected');
17016         });
17017
17018         var bullet = this.el.select('.bullet-' + i, true).first();
17019         
17020         if(!bullet){
17021             return;
17022         }
17023         
17024         bullet.addClass('selected');
17025     }
17026     
17027     
17028   
17029 });
17030
17031  
17032
17033  
17034  
17035 Roo.apply(Roo.bootstrap.TabGroup, {
17036     
17037     groups: {},
17038      /**
17039     * register a Navigation Group
17040     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17041     */
17042     register : function(navgrp)
17043     {
17044         this.groups[navgrp.navId] = navgrp;
17045         
17046     },
17047     /**
17048     * fetch a Navigation Group based on the navigation ID
17049     * if one does not exist , it will get created.
17050     * @param {string} the navgroup to add
17051     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17052     */
17053     get: function(navId) {
17054         if (typeof(this.groups[navId]) == 'undefined') {
17055             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17056         }
17057         return this.groups[navId] ;
17058     }
17059     
17060     
17061     
17062 });
17063
17064  /*
17065  * - LGPL
17066  *
17067  * TabPanel
17068  * 
17069  */
17070
17071 /**
17072  * @class Roo.bootstrap.TabPanel
17073  * @extends Roo.bootstrap.Component
17074  * Bootstrap TabPanel class
17075  * @cfg {Boolean} active panel active
17076  * @cfg {String} html panel content
17077  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17078  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17079  * @cfg {String} href click to link..
17080  * 
17081  * 
17082  * @constructor
17083  * Create a new TabPanel
17084  * @param {Object} config The config object
17085  */
17086
17087 Roo.bootstrap.TabPanel = function(config){
17088     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17089     this.addEvents({
17090         /**
17091              * @event changed
17092              * Fires when the active status changes
17093              * @param {Roo.bootstrap.TabPanel} this
17094              * @param {Boolean} state the new state
17095             
17096          */
17097         'changed': true,
17098         /**
17099              * @event beforedeactivate
17100              * Fires before a tab is de-activated - can be used to do validation on a form.
17101              * @param {Roo.bootstrap.TabPanel} this
17102              * @return {Boolean} false if there is an error
17103             
17104          */
17105         'beforedeactivate': true
17106      });
17107     
17108     this.tabId = this.tabId || Roo.id();
17109   
17110 };
17111
17112 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17113     
17114     active: false,
17115     html: false,
17116     tabId: false,
17117     navId : false,
17118     href : '',
17119     
17120     getAutoCreate : function(){
17121         var cfg = {
17122             tag: 'div',
17123             // item is needed for carousel - not sure if it has any effect otherwise
17124             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17125             html: this.html || ''
17126         };
17127         
17128         if(this.active){
17129             cfg.cls += ' active';
17130         }
17131         
17132         if(this.tabId){
17133             cfg.tabId = this.tabId;
17134         }
17135         
17136         
17137         return cfg;
17138     },
17139     
17140     initEvents:  function()
17141     {
17142         var p = this.parent();
17143         this.navId = this.navId || p.navId;
17144         
17145         if (typeof(this.navId) != 'undefined') {
17146             // not really needed.. but just in case.. parent should be a NavGroup.
17147             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17148             
17149             tg.register(this);
17150             
17151             var i = tg.tabs.length - 1;
17152             
17153             if(this.active && tg.bullets > 0 && i < tg.bullets){
17154                 tg.setActiveBullet(i);
17155             }
17156         }
17157         
17158         if(this.href.length){
17159             this.el.on('click', this.onClick, this);
17160         }
17161         
17162     },
17163     
17164     onRender : function(ct, position)
17165     {
17166        // Roo.log("Call onRender: " + this.xtype);
17167         
17168         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17169         
17170         
17171         
17172         
17173         
17174     },
17175     
17176     setActive: function(state)
17177     {
17178         Roo.log("panel - set active " + this.tabId + "=" + state);
17179         
17180         this.active = state;
17181         if (!state) {
17182             this.el.removeClass('active');
17183             
17184         } else  if (!this.el.hasClass('active')) {
17185             this.el.addClass('active');
17186         }
17187         
17188         this.fireEvent('changed', this, state);
17189     },
17190     
17191     onClick: function(e)
17192     {
17193         e.preventDefault();
17194         
17195         window.location.href = this.href;
17196     }
17197     
17198     
17199 });
17200  
17201
17202  
17203
17204  /*
17205  * - LGPL
17206  *
17207  * DateField
17208  * 
17209  */
17210
17211 /**
17212  * @class Roo.bootstrap.DateField
17213  * @extends Roo.bootstrap.Input
17214  * Bootstrap DateField class
17215  * @cfg {Number} weekStart default 0
17216  * @cfg {String} viewMode default empty, (months|years)
17217  * @cfg {String} minViewMode default empty, (months|years)
17218  * @cfg {Number} startDate default -Infinity
17219  * @cfg {Number} endDate default Infinity
17220  * @cfg {Boolean} todayHighlight default false
17221  * @cfg {Boolean} todayBtn default false
17222  * @cfg {Boolean} calendarWeeks default false
17223  * @cfg {Object} daysOfWeekDisabled default empty
17224  * @cfg {Boolean} singleMode default false (true | false)
17225  * 
17226  * @cfg {Boolean} keyboardNavigation default true
17227  * @cfg {String} language default en
17228  * 
17229  * @constructor
17230  * Create a new DateField
17231  * @param {Object} config The config object
17232  */
17233
17234 Roo.bootstrap.DateField = function(config){
17235     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17236      this.addEvents({
17237             /**
17238              * @event show
17239              * Fires when this field show.
17240              * @param {Roo.bootstrap.DateField} this
17241              * @param {Mixed} date The date value
17242              */
17243             show : true,
17244             /**
17245              * @event show
17246              * Fires when this field hide.
17247              * @param {Roo.bootstrap.DateField} this
17248              * @param {Mixed} date The date value
17249              */
17250             hide : true,
17251             /**
17252              * @event select
17253              * Fires when select a date.
17254              * @param {Roo.bootstrap.DateField} this
17255              * @param {Mixed} date The date value
17256              */
17257             select : true,
17258             /**
17259              * @event beforeselect
17260              * Fires when before select a date.
17261              * @param {Roo.bootstrap.DateField} this
17262              * @param {Mixed} date The date value
17263              */
17264             beforeselect : true
17265         });
17266 };
17267
17268 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17269     
17270     /**
17271      * @cfg {String} format
17272      * The default date format string which can be overriden for localization support.  The format must be
17273      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17274      */
17275     format : "m/d/y",
17276     /**
17277      * @cfg {String} altFormats
17278      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17279      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17280      */
17281     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17282     
17283     weekStart : 0,
17284     
17285     viewMode : '',
17286     
17287     minViewMode : '',
17288     
17289     todayHighlight : false,
17290     
17291     todayBtn: false,
17292     
17293     language: 'en',
17294     
17295     keyboardNavigation: true,
17296     
17297     calendarWeeks: false,
17298     
17299     startDate: -Infinity,
17300     
17301     endDate: Infinity,
17302     
17303     daysOfWeekDisabled: [],
17304     
17305     _events: [],
17306     
17307     singleMode : false,
17308     
17309     UTCDate: function()
17310     {
17311         return new Date(Date.UTC.apply(Date, arguments));
17312     },
17313     
17314     UTCToday: function()
17315     {
17316         var today = new Date();
17317         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17318     },
17319     
17320     getDate: function() {
17321             var d = this.getUTCDate();
17322             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17323     },
17324     
17325     getUTCDate: function() {
17326             return this.date;
17327     },
17328     
17329     setDate: function(d) {
17330             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17331     },
17332     
17333     setUTCDate: function(d) {
17334             this.date = d;
17335             this.setValue(this.formatDate(this.date));
17336     },
17337         
17338     onRender: function(ct, position)
17339     {
17340         
17341         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17342         
17343         this.language = this.language || 'en';
17344         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17345         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17346         
17347         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17348         this.format = this.format || 'm/d/y';
17349         this.isInline = false;
17350         this.isInput = true;
17351         this.component = this.el.select('.add-on', true).first() || false;
17352         this.component = (this.component && this.component.length === 0) ? false : this.component;
17353         this.hasInput = this.component && this.inputEl().length;
17354         
17355         if (typeof(this.minViewMode === 'string')) {
17356             switch (this.minViewMode) {
17357                 case 'months':
17358                     this.minViewMode = 1;
17359                     break;
17360                 case 'years':
17361                     this.minViewMode = 2;
17362                     break;
17363                 default:
17364                     this.minViewMode = 0;
17365                     break;
17366             }
17367         }
17368         
17369         if (typeof(this.viewMode === 'string')) {
17370             switch (this.viewMode) {
17371                 case 'months':
17372                     this.viewMode = 1;
17373                     break;
17374                 case 'years':
17375                     this.viewMode = 2;
17376                     break;
17377                 default:
17378                     this.viewMode = 0;
17379                     break;
17380             }
17381         }
17382                 
17383         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17384         
17385 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17386         
17387         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17388         
17389         this.picker().on('mousedown', this.onMousedown, this);
17390         this.picker().on('click', this.onClick, this);
17391         
17392         this.picker().addClass('datepicker-dropdown');
17393         
17394         this.startViewMode = this.viewMode;
17395         
17396         if(this.singleMode){
17397             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17398                 v.setVisibilityMode(Roo.Element.DISPLAY);
17399                 v.hide();
17400             });
17401             
17402             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17403                 v.setStyle('width', '189px');
17404             });
17405         }
17406         
17407         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17408             if(!this.calendarWeeks){
17409                 v.remove();
17410                 return;
17411             }
17412             
17413             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17414             v.attr('colspan', function(i, val){
17415                 return parseInt(val) + 1;
17416             });
17417         });
17418                         
17419         
17420         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17421         
17422         this.setStartDate(this.startDate);
17423         this.setEndDate(this.endDate);
17424         
17425         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17426         
17427         this.fillDow();
17428         this.fillMonths();
17429         this.update();
17430         this.showMode();
17431         
17432         if(this.isInline) {
17433             this.show();
17434         }
17435     },
17436     
17437     picker : function()
17438     {
17439         return this.pickerEl;
17440 //        return this.el.select('.datepicker', true).first();
17441     },
17442     
17443     fillDow: function()
17444     {
17445         var dowCnt = this.weekStart;
17446         
17447         var dow = {
17448             tag: 'tr',
17449             cn: [
17450                 
17451             ]
17452         };
17453         
17454         if(this.calendarWeeks){
17455             dow.cn.push({
17456                 tag: 'th',
17457                 cls: 'cw',
17458                 html: '&nbsp;'
17459             })
17460         }
17461         
17462         while (dowCnt < this.weekStart + 7) {
17463             dow.cn.push({
17464                 tag: 'th',
17465                 cls: 'dow',
17466                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17467             });
17468         }
17469         
17470         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17471     },
17472     
17473     fillMonths: function()
17474     {    
17475         var i = 0;
17476         var months = this.picker().select('>.datepicker-months td', true).first();
17477         
17478         months.dom.innerHTML = '';
17479         
17480         while (i < 12) {
17481             var month = {
17482                 tag: 'span',
17483                 cls: 'month',
17484                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17485             };
17486             
17487             months.createChild(month);
17488         }
17489         
17490     },
17491     
17492     update: function()
17493     {
17494         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;
17495         
17496         if (this.date < this.startDate) {
17497             this.viewDate = new Date(this.startDate);
17498         } else if (this.date > this.endDate) {
17499             this.viewDate = new Date(this.endDate);
17500         } else {
17501             this.viewDate = new Date(this.date);
17502         }
17503         
17504         this.fill();
17505     },
17506     
17507     fill: function() 
17508     {
17509         var d = new Date(this.viewDate),
17510                 year = d.getUTCFullYear(),
17511                 month = d.getUTCMonth(),
17512                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17513                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17514                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17515                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17516                 currentDate = this.date && this.date.valueOf(),
17517                 today = this.UTCToday();
17518         
17519         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17520         
17521 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17522         
17523 //        this.picker.select('>tfoot th.today').
17524 //                                              .text(dates[this.language].today)
17525 //                                              .toggle(this.todayBtn !== false);
17526     
17527         this.updateNavArrows();
17528         this.fillMonths();
17529                                                 
17530         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17531         
17532         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17533          
17534         prevMonth.setUTCDate(day);
17535         
17536         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17537         
17538         var nextMonth = new Date(prevMonth);
17539         
17540         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17541         
17542         nextMonth = nextMonth.valueOf();
17543         
17544         var fillMonths = false;
17545         
17546         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17547         
17548         while(prevMonth.valueOf() < nextMonth) {
17549             var clsName = '';
17550             
17551             if (prevMonth.getUTCDay() === this.weekStart) {
17552                 if(fillMonths){
17553                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17554                 }
17555                     
17556                 fillMonths = {
17557                     tag: 'tr',
17558                     cn: []
17559                 };
17560                 
17561                 if(this.calendarWeeks){
17562                     // ISO 8601: First week contains first thursday.
17563                     // ISO also states week starts on Monday, but we can be more abstract here.
17564                     var
17565                     // Start of current week: based on weekstart/current date
17566                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17567                     // Thursday of this week
17568                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17569                     // First Thursday of year, year from thursday
17570                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17571                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17572                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17573                     
17574                     fillMonths.cn.push({
17575                         tag: 'td',
17576                         cls: 'cw',
17577                         html: calWeek
17578                     });
17579                 }
17580             }
17581             
17582             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17583                 clsName += ' old';
17584             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17585                 clsName += ' new';
17586             }
17587             if (this.todayHighlight &&
17588                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17589                 prevMonth.getUTCMonth() == today.getMonth() &&
17590                 prevMonth.getUTCDate() == today.getDate()) {
17591                 clsName += ' today';
17592             }
17593             
17594             if (currentDate && prevMonth.valueOf() === currentDate) {
17595                 clsName += ' active';
17596             }
17597             
17598             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17599                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17600                     clsName += ' disabled';
17601             }
17602             
17603             fillMonths.cn.push({
17604                 tag: 'td',
17605                 cls: 'day ' + clsName,
17606                 html: prevMonth.getDate()
17607             });
17608             
17609             prevMonth.setDate(prevMonth.getDate()+1);
17610         }
17611           
17612         var currentYear = this.date && this.date.getUTCFullYear();
17613         var currentMonth = this.date && this.date.getUTCMonth();
17614         
17615         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17616         
17617         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17618             v.removeClass('active');
17619             
17620             if(currentYear === year && k === currentMonth){
17621                 v.addClass('active');
17622             }
17623             
17624             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17625                 v.addClass('disabled');
17626             }
17627             
17628         });
17629         
17630         
17631         year = parseInt(year/10, 10) * 10;
17632         
17633         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17634         
17635         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17636         
17637         year -= 1;
17638         for (var i = -1; i < 11; i++) {
17639             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17640                 tag: 'span',
17641                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17642                 html: year
17643             });
17644             
17645             year += 1;
17646         }
17647     },
17648     
17649     showMode: function(dir) 
17650     {
17651         if (dir) {
17652             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17653         }
17654         
17655         Roo.each(this.picker().select('>div',true).elements, function(v){
17656             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17657             v.hide();
17658         });
17659         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17660     },
17661     
17662     place: function()
17663     {
17664         if(this.isInline) {
17665             return;
17666         }
17667         
17668         this.picker().removeClass(['bottom', 'top']);
17669         
17670         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17671             /*
17672              * place to the top of element!
17673              *
17674              */
17675             
17676             this.picker().addClass('top');
17677             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17678             
17679             return;
17680         }
17681         
17682         this.picker().addClass('bottom');
17683         
17684         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17685     },
17686     
17687     parseDate : function(value)
17688     {
17689         if(!value || value instanceof Date){
17690             return value;
17691         }
17692         var v = Date.parseDate(value, this.format);
17693         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17694             v = Date.parseDate(value, 'Y-m-d');
17695         }
17696         if(!v && this.altFormats){
17697             if(!this.altFormatsArray){
17698                 this.altFormatsArray = this.altFormats.split("|");
17699             }
17700             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17701                 v = Date.parseDate(value, this.altFormatsArray[i]);
17702             }
17703         }
17704         return v;
17705     },
17706     
17707     formatDate : function(date, fmt)
17708     {   
17709         return (!date || !(date instanceof Date)) ?
17710         date : date.dateFormat(fmt || this.format);
17711     },
17712     
17713     onFocus : function()
17714     {
17715         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17716         this.show();
17717     },
17718     
17719     onBlur : function()
17720     {
17721         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17722         
17723         var d = this.inputEl().getValue();
17724         
17725         this.setValue(d);
17726                 
17727         this.hide();
17728     },
17729     
17730     show : function()
17731     {
17732         this.picker().show();
17733         this.update();
17734         this.place();
17735         
17736         this.fireEvent('show', this, this.date);
17737     },
17738     
17739     hide : function()
17740     {
17741         if(this.isInline) {
17742             return;
17743         }
17744         this.picker().hide();
17745         this.viewMode = this.startViewMode;
17746         this.showMode();
17747         
17748         this.fireEvent('hide', this, this.date);
17749         
17750     },
17751     
17752     onMousedown: function(e)
17753     {
17754         e.stopPropagation();
17755         e.preventDefault();
17756     },
17757     
17758     keyup: function(e)
17759     {
17760         Roo.bootstrap.DateField.superclass.keyup.call(this);
17761         this.update();
17762     },
17763
17764     setValue: function(v)
17765     {
17766         if(this.fireEvent('beforeselect', this, v) !== false){
17767             var d = new Date(this.parseDate(v) ).clearTime();
17768         
17769             if(isNaN(d.getTime())){
17770                 this.date = this.viewDate = '';
17771                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17772                 return;
17773             }
17774
17775             v = this.formatDate(d);
17776
17777             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17778
17779             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17780
17781             this.update();
17782
17783             this.fireEvent('select', this, this.date);
17784         }
17785     },
17786     
17787     getValue: function()
17788     {
17789         return this.formatDate(this.date);
17790     },
17791     
17792     fireKey: function(e)
17793     {
17794         if (!this.picker().isVisible()){
17795             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17796                 this.show();
17797             }
17798             return;
17799         }
17800         
17801         var dateChanged = false,
17802         dir, day, month,
17803         newDate, newViewDate;
17804         
17805         switch(e.keyCode){
17806             case 27: // escape
17807                 this.hide();
17808                 e.preventDefault();
17809                 break;
17810             case 37: // left
17811             case 39: // right
17812                 if (!this.keyboardNavigation) {
17813                     break;
17814                 }
17815                 dir = e.keyCode == 37 ? -1 : 1;
17816                 
17817                 if (e.ctrlKey){
17818                     newDate = this.moveYear(this.date, dir);
17819                     newViewDate = this.moveYear(this.viewDate, dir);
17820                 } else if (e.shiftKey){
17821                     newDate = this.moveMonth(this.date, dir);
17822                     newViewDate = this.moveMonth(this.viewDate, dir);
17823                 } else {
17824                     newDate = new Date(this.date);
17825                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17826                     newViewDate = new Date(this.viewDate);
17827                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17828                 }
17829                 if (this.dateWithinRange(newDate)){
17830                     this.date = newDate;
17831                     this.viewDate = newViewDate;
17832                     this.setValue(this.formatDate(this.date));
17833 //                    this.update();
17834                     e.preventDefault();
17835                     dateChanged = true;
17836                 }
17837                 break;
17838             case 38: // up
17839             case 40: // down
17840                 if (!this.keyboardNavigation) {
17841                     break;
17842                 }
17843                 dir = e.keyCode == 38 ? -1 : 1;
17844                 if (e.ctrlKey){
17845                     newDate = this.moveYear(this.date, dir);
17846                     newViewDate = this.moveYear(this.viewDate, dir);
17847                 } else if (e.shiftKey){
17848                     newDate = this.moveMonth(this.date, dir);
17849                     newViewDate = this.moveMonth(this.viewDate, dir);
17850                 } else {
17851                     newDate = new Date(this.date);
17852                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17853                     newViewDate = new Date(this.viewDate);
17854                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17855                 }
17856                 if (this.dateWithinRange(newDate)){
17857                     this.date = newDate;
17858                     this.viewDate = newViewDate;
17859                     this.setValue(this.formatDate(this.date));
17860 //                    this.update();
17861                     e.preventDefault();
17862                     dateChanged = true;
17863                 }
17864                 break;
17865             case 13: // enter
17866                 this.setValue(this.formatDate(this.date));
17867                 this.hide();
17868                 e.preventDefault();
17869                 break;
17870             case 9: // tab
17871                 this.setValue(this.formatDate(this.date));
17872                 this.hide();
17873                 break;
17874             case 16: // shift
17875             case 17: // ctrl
17876             case 18: // alt
17877                 break;
17878             default :
17879                 this.hide();
17880                 
17881         }
17882     },
17883     
17884     
17885     onClick: function(e) 
17886     {
17887         e.stopPropagation();
17888         e.preventDefault();
17889         
17890         var target = e.getTarget();
17891         
17892         if(target.nodeName.toLowerCase() === 'i'){
17893             target = Roo.get(target).dom.parentNode;
17894         }
17895         
17896         var nodeName = target.nodeName;
17897         var className = target.className;
17898         var html = target.innerHTML;
17899         //Roo.log(nodeName);
17900         
17901         switch(nodeName.toLowerCase()) {
17902             case 'th':
17903                 switch(className) {
17904                     case 'switch':
17905                         this.showMode(1);
17906                         break;
17907                     case 'prev':
17908                     case 'next':
17909                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17910                         switch(this.viewMode){
17911                                 case 0:
17912                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17913                                         break;
17914                                 case 1:
17915                                 case 2:
17916                                         this.viewDate = this.moveYear(this.viewDate, dir);
17917                                         break;
17918                         }
17919                         this.fill();
17920                         break;
17921                     case 'today':
17922                         var date = new Date();
17923                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17924 //                        this.fill()
17925                         this.setValue(this.formatDate(this.date));
17926                         
17927                         this.hide();
17928                         break;
17929                 }
17930                 break;
17931             case 'span':
17932                 if (className.indexOf('disabled') < 0) {
17933                     this.viewDate.setUTCDate(1);
17934                     if (className.indexOf('month') > -1) {
17935                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17936                     } else {
17937                         var year = parseInt(html, 10) || 0;
17938                         this.viewDate.setUTCFullYear(year);
17939                         
17940                     }
17941                     
17942                     if(this.singleMode){
17943                         this.setValue(this.formatDate(this.viewDate));
17944                         this.hide();
17945                         return;
17946                     }
17947                     
17948                     this.showMode(-1);
17949                     this.fill();
17950                 }
17951                 break;
17952                 
17953             case 'td':
17954                 //Roo.log(className);
17955                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17956                     var day = parseInt(html, 10) || 1;
17957                     var year = this.viewDate.getUTCFullYear(),
17958                         month = this.viewDate.getUTCMonth();
17959
17960                     if (className.indexOf('old') > -1) {
17961                         if(month === 0 ){
17962                             month = 11;
17963                             year -= 1;
17964                         }else{
17965                             month -= 1;
17966                         }
17967                     } else if (className.indexOf('new') > -1) {
17968                         if (month == 11) {
17969                             month = 0;
17970                             year += 1;
17971                         } else {
17972                             month += 1;
17973                         }
17974                     }
17975                     //Roo.log([year,month,day]);
17976                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17977                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17978 //                    this.fill();
17979                     //Roo.log(this.formatDate(this.date));
17980                     this.setValue(this.formatDate(this.date));
17981                     this.hide();
17982                 }
17983                 break;
17984         }
17985     },
17986     
17987     setStartDate: function(startDate)
17988     {
17989         this.startDate = startDate || -Infinity;
17990         if (this.startDate !== -Infinity) {
17991             this.startDate = this.parseDate(this.startDate);
17992         }
17993         this.update();
17994         this.updateNavArrows();
17995     },
17996
17997     setEndDate: function(endDate)
17998     {
17999         this.endDate = endDate || Infinity;
18000         if (this.endDate !== Infinity) {
18001             this.endDate = this.parseDate(this.endDate);
18002         }
18003         this.update();
18004         this.updateNavArrows();
18005     },
18006     
18007     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18008     {
18009         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18010         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18011             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18012         }
18013         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18014             return parseInt(d, 10);
18015         });
18016         this.update();
18017         this.updateNavArrows();
18018     },
18019     
18020     updateNavArrows: function() 
18021     {
18022         if(this.singleMode){
18023             return;
18024         }
18025         
18026         var d = new Date(this.viewDate),
18027         year = d.getUTCFullYear(),
18028         month = d.getUTCMonth();
18029         
18030         Roo.each(this.picker().select('.prev', true).elements, function(v){
18031             v.show();
18032             switch (this.viewMode) {
18033                 case 0:
18034
18035                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18036                         v.hide();
18037                     }
18038                     break;
18039                 case 1:
18040                 case 2:
18041                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18042                         v.hide();
18043                     }
18044                     break;
18045             }
18046         });
18047         
18048         Roo.each(this.picker().select('.next', true).elements, function(v){
18049             v.show();
18050             switch (this.viewMode) {
18051                 case 0:
18052
18053                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18054                         v.hide();
18055                     }
18056                     break;
18057                 case 1:
18058                 case 2:
18059                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18060                         v.hide();
18061                     }
18062                     break;
18063             }
18064         })
18065     },
18066     
18067     moveMonth: function(date, dir)
18068     {
18069         if (!dir) {
18070             return date;
18071         }
18072         var new_date = new Date(date.valueOf()),
18073         day = new_date.getUTCDate(),
18074         month = new_date.getUTCMonth(),
18075         mag = Math.abs(dir),
18076         new_month, test;
18077         dir = dir > 0 ? 1 : -1;
18078         if (mag == 1){
18079             test = dir == -1
18080             // If going back one month, make sure month is not current month
18081             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18082             ? function(){
18083                 return new_date.getUTCMonth() == month;
18084             }
18085             // If going forward one month, make sure month is as expected
18086             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18087             : function(){
18088                 return new_date.getUTCMonth() != new_month;
18089             };
18090             new_month = month + dir;
18091             new_date.setUTCMonth(new_month);
18092             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18093             if (new_month < 0 || new_month > 11) {
18094                 new_month = (new_month + 12) % 12;
18095             }
18096         } else {
18097             // For magnitudes >1, move one month at a time...
18098             for (var i=0; i<mag; i++) {
18099                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18100                 new_date = this.moveMonth(new_date, dir);
18101             }
18102             // ...then reset the day, keeping it in the new month
18103             new_month = new_date.getUTCMonth();
18104             new_date.setUTCDate(day);
18105             test = function(){
18106                 return new_month != new_date.getUTCMonth();
18107             };
18108         }
18109         // Common date-resetting loop -- if date is beyond end of month, make it
18110         // end of month
18111         while (test()){
18112             new_date.setUTCDate(--day);
18113             new_date.setUTCMonth(new_month);
18114         }
18115         return new_date;
18116     },
18117
18118     moveYear: function(date, dir)
18119     {
18120         return this.moveMonth(date, dir*12);
18121     },
18122
18123     dateWithinRange: function(date)
18124     {
18125         return date >= this.startDate && date <= this.endDate;
18126     },
18127
18128     
18129     remove: function() 
18130     {
18131         this.picker().remove();
18132     },
18133     
18134     validateValue : function(value)
18135     {
18136         if(value.length < 1)  {
18137             if(this.allowBlank){
18138                 return true;
18139             }
18140             return false;
18141         }
18142         
18143         if(value.length < this.minLength){
18144             return false;
18145         }
18146         if(value.length > this.maxLength){
18147             return false;
18148         }
18149         if(this.vtype){
18150             var vt = Roo.form.VTypes;
18151             if(!vt[this.vtype](value, this)){
18152                 return false;
18153             }
18154         }
18155         if(typeof this.validator == "function"){
18156             var msg = this.validator(value);
18157             if(msg !== true){
18158                 return false;
18159             }
18160         }
18161         
18162         if(this.regex && !this.regex.test(value)){
18163             return false;
18164         }
18165         
18166         if(typeof(this.parseDate(value)) == 'undefined'){
18167             return false;
18168         }
18169         
18170         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18171             return false;
18172         }      
18173         
18174         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18175             return false;
18176         } 
18177         
18178         
18179         return true;
18180     }
18181    
18182 });
18183
18184 Roo.apply(Roo.bootstrap.DateField,  {
18185     
18186     head : {
18187         tag: 'thead',
18188         cn: [
18189         {
18190             tag: 'tr',
18191             cn: [
18192             {
18193                 tag: 'th',
18194                 cls: 'prev',
18195                 html: '<i class="fa fa-arrow-left"/>'
18196             },
18197             {
18198                 tag: 'th',
18199                 cls: 'switch',
18200                 colspan: '5'
18201             },
18202             {
18203                 tag: 'th',
18204                 cls: 'next',
18205                 html: '<i class="fa fa-arrow-right"/>'
18206             }
18207
18208             ]
18209         }
18210         ]
18211     },
18212     
18213     content : {
18214         tag: 'tbody',
18215         cn: [
18216         {
18217             tag: 'tr',
18218             cn: [
18219             {
18220                 tag: 'td',
18221                 colspan: '7'
18222             }
18223             ]
18224         }
18225         ]
18226     },
18227     
18228     footer : {
18229         tag: 'tfoot',
18230         cn: [
18231         {
18232             tag: 'tr',
18233             cn: [
18234             {
18235                 tag: 'th',
18236                 colspan: '7',
18237                 cls: 'today'
18238             }
18239                     
18240             ]
18241         }
18242         ]
18243     },
18244     
18245     dates:{
18246         en: {
18247             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18248             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18249             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18250             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18251             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18252             today: "Today"
18253         }
18254     },
18255     
18256     modes: [
18257     {
18258         clsName: 'days',
18259         navFnc: 'Month',
18260         navStep: 1
18261     },
18262     {
18263         clsName: 'months',
18264         navFnc: 'FullYear',
18265         navStep: 1
18266     },
18267     {
18268         clsName: 'years',
18269         navFnc: 'FullYear',
18270         navStep: 10
18271     }]
18272 });
18273
18274 Roo.apply(Roo.bootstrap.DateField,  {
18275   
18276     template : {
18277         tag: 'div',
18278         cls: 'datepicker dropdown-menu roo-dynamic',
18279         cn: [
18280         {
18281             tag: 'div',
18282             cls: 'datepicker-days',
18283             cn: [
18284             {
18285                 tag: 'table',
18286                 cls: 'table-condensed',
18287                 cn:[
18288                 Roo.bootstrap.DateField.head,
18289                 {
18290                     tag: 'tbody'
18291                 },
18292                 Roo.bootstrap.DateField.footer
18293                 ]
18294             }
18295             ]
18296         },
18297         {
18298             tag: 'div',
18299             cls: 'datepicker-months',
18300             cn: [
18301             {
18302                 tag: 'table',
18303                 cls: 'table-condensed',
18304                 cn:[
18305                 Roo.bootstrap.DateField.head,
18306                 Roo.bootstrap.DateField.content,
18307                 Roo.bootstrap.DateField.footer
18308                 ]
18309             }
18310             ]
18311         },
18312         {
18313             tag: 'div',
18314             cls: 'datepicker-years',
18315             cn: [
18316             {
18317                 tag: 'table',
18318                 cls: 'table-condensed',
18319                 cn:[
18320                 Roo.bootstrap.DateField.head,
18321                 Roo.bootstrap.DateField.content,
18322                 Roo.bootstrap.DateField.footer
18323                 ]
18324             }
18325             ]
18326         }
18327         ]
18328     }
18329 });
18330
18331  
18332
18333  /*
18334  * - LGPL
18335  *
18336  * TimeField
18337  * 
18338  */
18339
18340 /**
18341  * @class Roo.bootstrap.TimeField
18342  * @extends Roo.bootstrap.Input
18343  * Bootstrap DateField class
18344  * 
18345  * 
18346  * @constructor
18347  * Create a new TimeField
18348  * @param {Object} config The config object
18349  */
18350
18351 Roo.bootstrap.TimeField = function(config){
18352     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18353     this.addEvents({
18354             /**
18355              * @event show
18356              * Fires when this field show.
18357              * @param {Roo.bootstrap.DateField} thisthis
18358              * @param {Mixed} date The date value
18359              */
18360             show : true,
18361             /**
18362              * @event show
18363              * Fires when this field hide.
18364              * @param {Roo.bootstrap.DateField} this
18365              * @param {Mixed} date The date value
18366              */
18367             hide : true,
18368             /**
18369              * @event select
18370              * Fires when select a date.
18371              * @param {Roo.bootstrap.DateField} this
18372              * @param {Mixed} date The date value
18373              */
18374             select : true
18375         });
18376 };
18377
18378 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18379     
18380     /**
18381      * @cfg {String} format
18382      * The default time format string which can be overriden for localization support.  The format must be
18383      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18384      */
18385     format : "H:i",
18386        
18387     onRender: function(ct, position)
18388     {
18389         
18390         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18391                 
18392         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18393         
18394         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18395         
18396         this.pop = this.picker().select('>.datepicker-time',true).first();
18397         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18398         
18399         this.picker().on('mousedown', this.onMousedown, this);
18400         this.picker().on('click', this.onClick, this);
18401         
18402         this.picker().addClass('datepicker-dropdown');
18403     
18404         this.fillTime();
18405         this.update();
18406             
18407         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18408         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18409         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18410         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18411         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18412         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18413
18414     },
18415     
18416     fireKey: function(e){
18417         if (!this.picker().isVisible()){
18418             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18419                 this.show();
18420             }
18421             return;
18422         }
18423
18424         e.preventDefault();
18425         
18426         switch(e.keyCode){
18427             case 27: // escape
18428                 this.hide();
18429                 break;
18430             case 37: // left
18431             case 39: // right
18432                 this.onTogglePeriod();
18433                 break;
18434             case 38: // up
18435                 this.onIncrementMinutes();
18436                 break;
18437             case 40: // down
18438                 this.onDecrementMinutes();
18439                 break;
18440             case 13: // enter
18441             case 9: // tab
18442                 this.setTime();
18443                 break;
18444         }
18445     },
18446     
18447     onClick: function(e) {
18448         e.stopPropagation();
18449         e.preventDefault();
18450     },
18451     
18452     picker : function()
18453     {
18454         return this.el.select('.datepicker', true).first();
18455     },
18456     
18457     fillTime: function()
18458     {    
18459         var time = this.pop.select('tbody', true).first();
18460         
18461         time.dom.innerHTML = '';
18462         
18463         time.createChild({
18464             tag: 'tr',
18465             cn: [
18466                 {
18467                     tag: 'td',
18468                     cn: [
18469                         {
18470                             tag: 'a',
18471                             href: '#',
18472                             cls: 'btn',
18473                             cn: [
18474                                 {
18475                                     tag: 'span',
18476                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18477                                 }
18478                             ]
18479                         } 
18480                     ]
18481                 },
18482                 {
18483                     tag: 'td',
18484                     cls: 'separator'
18485                 },
18486                 {
18487                     tag: 'td',
18488                     cn: [
18489                         {
18490                             tag: 'a',
18491                             href: '#',
18492                             cls: 'btn',
18493                             cn: [
18494                                 {
18495                                     tag: 'span',
18496                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18497                                 }
18498                             ]
18499                         }
18500                     ]
18501                 },
18502                 {
18503                     tag: 'td',
18504                     cls: 'separator'
18505                 }
18506             ]
18507         });
18508         
18509         time.createChild({
18510             tag: 'tr',
18511             cn: [
18512                 {
18513                     tag: 'td',
18514                     cn: [
18515                         {
18516                             tag: 'span',
18517                             cls: 'timepicker-hour',
18518                             html: '00'
18519                         }  
18520                     ]
18521                 },
18522                 {
18523                     tag: 'td',
18524                     cls: 'separator',
18525                     html: ':'
18526                 },
18527                 {
18528                     tag: 'td',
18529                     cn: [
18530                         {
18531                             tag: 'span',
18532                             cls: 'timepicker-minute',
18533                             html: '00'
18534                         }  
18535                     ]
18536                 },
18537                 {
18538                     tag: 'td',
18539                     cls: 'separator'
18540                 },
18541                 {
18542                     tag: 'td',
18543                     cn: [
18544                         {
18545                             tag: 'button',
18546                             type: 'button',
18547                             cls: 'btn btn-primary period',
18548                             html: 'AM'
18549                             
18550                         }
18551                     ]
18552                 }
18553             ]
18554         });
18555         
18556         time.createChild({
18557             tag: 'tr',
18558             cn: [
18559                 {
18560                     tag: 'td',
18561                     cn: [
18562                         {
18563                             tag: 'a',
18564                             href: '#',
18565                             cls: 'btn',
18566                             cn: [
18567                                 {
18568                                     tag: 'span',
18569                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18570                                 }
18571                             ]
18572                         }
18573                     ]
18574                 },
18575                 {
18576                     tag: 'td',
18577                     cls: 'separator'
18578                 },
18579                 {
18580                     tag: 'td',
18581                     cn: [
18582                         {
18583                             tag: 'a',
18584                             href: '#',
18585                             cls: 'btn',
18586                             cn: [
18587                                 {
18588                                     tag: 'span',
18589                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18590                                 }
18591                             ]
18592                         }
18593                     ]
18594                 },
18595                 {
18596                     tag: 'td',
18597                     cls: 'separator'
18598                 }
18599             ]
18600         });
18601         
18602     },
18603     
18604     update: function()
18605     {
18606         
18607         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18608         
18609         this.fill();
18610     },
18611     
18612     fill: function() 
18613     {
18614         var hours = this.time.getHours();
18615         var minutes = this.time.getMinutes();
18616         var period = 'AM';
18617         
18618         if(hours > 11){
18619             period = 'PM';
18620         }
18621         
18622         if(hours == 0){
18623             hours = 12;
18624         }
18625         
18626         
18627         if(hours > 12){
18628             hours = hours - 12;
18629         }
18630         
18631         if(hours < 10){
18632             hours = '0' + hours;
18633         }
18634         
18635         if(minutes < 10){
18636             minutes = '0' + minutes;
18637         }
18638         
18639         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18640         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18641         this.pop.select('button', true).first().dom.innerHTML = period;
18642         
18643     },
18644     
18645     place: function()
18646     {   
18647         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18648         
18649         var cls = ['bottom'];
18650         
18651         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18652             cls.pop();
18653             cls.push('top');
18654         }
18655         
18656         cls.push('right');
18657         
18658         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18659             cls.pop();
18660             cls.push('left');
18661         }
18662         
18663         this.picker().addClass(cls.join('-'));
18664         
18665         var _this = this;
18666         
18667         Roo.each(cls, function(c){
18668             if(c == 'bottom'){
18669                 _this.picker().setTop(_this.inputEl().getHeight());
18670                 return;
18671             }
18672             if(c == 'top'){
18673                 _this.picker().setTop(0 - _this.picker().getHeight());
18674                 return;
18675             }
18676             
18677             if(c == 'left'){
18678                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18679                 return;
18680             }
18681             if(c == 'right'){
18682                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18683                 return;
18684             }
18685         });
18686         
18687     },
18688   
18689     onFocus : function()
18690     {
18691         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18692         this.show();
18693     },
18694     
18695     onBlur : function()
18696     {
18697         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18698         this.hide();
18699     },
18700     
18701     show : function()
18702     {
18703         this.picker().show();
18704         this.pop.show();
18705         this.update();
18706         this.place();
18707         
18708         this.fireEvent('show', this, this.date);
18709     },
18710     
18711     hide : function()
18712     {
18713         this.picker().hide();
18714         this.pop.hide();
18715         
18716         this.fireEvent('hide', this, this.date);
18717     },
18718     
18719     setTime : function()
18720     {
18721         this.hide();
18722         this.setValue(this.time.format(this.format));
18723         
18724         this.fireEvent('select', this, this.date);
18725         
18726         
18727     },
18728     
18729     onMousedown: function(e){
18730         e.stopPropagation();
18731         e.preventDefault();
18732     },
18733     
18734     onIncrementHours: function()
18735     {
18736         Roo.log('onIncrementHours');
18737         this.time = this.time.add(Date.HOUR, 1);
18738         this.update();
18739         
18740     },
18741     
18742     onDecrementHours: function()
18743     {
18744         Roo.log('onDecrementHours');
18745         this.time = this.time.add(Date.HOUR, -1);
18746         this.update();
18747     },
18748     
18749     onIncrementMinutes: function()
18750     {
18751         Roo.log('onIncrementMinutes');
18752         this.time = this.time.add(Date.MINUTE, 1);
18753         this.update();
18754     },
18755     
18756     onDecrementMinutes: function()
18757     {
18758         Roo.log('onDecrementMinutes');
18759         this.time = this.time.add(Date.MINUTE, -1);
18760         this.update();
18761     },
18762     
18763     onTogglePeriod: function()
18764     {
18765         Roo.log('onTogglePeriod');
18766         this.time = this.time.add(Date.HOUR, 12);
18767         this.update();
18768     }
18769     
18770    
18771 });
18772
18773 Roo.apply(Roo.bootstrap.TimeField,  {
18774     
18775     content : {
18776         tag: 'tbody',
18777         cn: [
18778             {
18779                 tag: 'tr',
18780                 cn: [
18781                 {
18782                     tag: 'td',
18783                     colspan: '7'
18784                 }
18785                 ]
18786             }
18787         ]
18788     },
18789     
18790     footer : {
18791         tag: 'tfoot',
18792         cn: [
18793             {
18794                 tag: 'tr',
18795                 cn: [
18796                 {
18797                     tag: 'th',
18798                     colspan: '7',
18799                     cls: '',
18800                     cn: [
18801                         {
18802                             tag: 'button',
18803                             cls: 'btn btn-info ok',
18804                             html: 'OK'
18805                         }
18806                     ]
18807                 }
18808
18809                 ]
18810             }
18811         ]
18812     }
18813 });
18814
18815 Roo.apply(Roo.bootstrap.TimeField,  {
18816   
18817     template : {
18818         tag: 'div',
18819         cls: 'datepicker dropdown-menu',
18820         cn: [
18821             {
18822                 tag: 'div',
18823                 cls: 'datepicker-time',
18824                 cn: [
18825                 {
18826                     tag: 'table',
18827                     cls: 'table-condensed',
18828                     cn:[
18829                     Roo.bootstrap.TimeField.content,
18830                     Roo.bootstrap.TimeField.footer
18831                     ]
18832                 }
18833                 ]
18834             }
18835         ]
18836     }
18837 });
18838
18839  
18840
18841  /*
18842  * - LGPL
18843  *
18844  * MonthField
18845  * 
18846  */
18847
18848 /**
18849  * @class Roo.bootstrap.MonthField
18850  * @extends Roo.bootstrap.Input
18851  * Bootstrap MonthField class
18852  * 
18853  * @cfg {String} language default en
18854  * 
18855  * @constructor
18856  * Create a new MonthField
18857  * @param {Object} config The config object
18858  */
18859
18860 Roo.bootstrap.MonthField = function(config){
18861     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18862     
18863     this.addEvents({
18864         /**
18865          * @event show
18866          * Fires when this field show.
18867          * @param {Roo.bootstrap.MonthField} this
18868          * @param {Mixed} date The date value
18869          */
18870         show : true,
18871         /**
18872          * @event show
18873          * Fires when this field hide.
18874          * @param {Roo.bootstrap.MonthField} this
18875          * @param {Mixed} date The date value
18876          */
18877         hide : true,
18878         /**
18879          * @event select
18880          * Fires when select a date.
18881          * @param {Roo.bootstrap.MonthField} this
18882          * @param {String} oldvalue The old value
18883          * @param {String} newvalue The new value
18884          */
18885         select : true
18886     });
18887 };
18888
18889 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18890     
18891     onRender: function(ct, position)
18892     {
18893         
18894         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18895         
18896         this.language = this.language || 'en';
18897         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18898         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18899         
18900         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18901         this.isInline = false;
18902         this.isInput = true;
18903         this.component = this.el.select('.add-on', true).first() || false;
18904         this.component = (this.component && this.component.length === 0) ? false : this.component;
18905         this.hasInput = this.component && this.inputEL().length;
18906         
18907         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18908         
18909         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18910         
18911         this.picker().on('mousedown', this.onMousedown, this);
18912         this.picker().on('click', this.onClick, this);
18913         
18914         this.picker().addClass('datepicker-dropdown');
18915         
18916         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18917             v.setStyle('width', '189px');
18918         });
18919         
18920         this.fillMonths();
18921         
18922         this.update();
18923         
18924         if(this.isInline) {
18925             this.show();
18926         }
18927         
18928     },
18929     
18930     setValue: function(v, suppressEvent)
18931     {   
18932         var o = this.getValue();
18933         
18934         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18935         
18936         this.update();
18937
18938         if(suppressEvent !== true){
18939             this.fireEvent('select', this, o, v);
18940         }
18941         
18942     },
18943     
18944     getValue: function()
18945     {
18946         return this.value;
18947     },
18948     
18949     onClick: function(e) 
18950     {
18951         e.stopPropagation();
18952         e.preventDefault();
18953         
18954         var target = e.getTarget();
18955         
18956         if(target.nodeName.toLowerCase() === 'i'){
18957             target = Roo.get(target).dom.parentNode;
18958         }
18959         
18960         var nodeName = target.nodeName;
18961         var className = target.className;
18962         var html = target.innerHTML;
18963         
18964         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18965             return;
18966         }
18967         
18968         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18969         
18970         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18971         
18972         this.hide();
18973                         
18974     },
18975     
18976     picker : function()
18977     {
18978         return this.pickerEl;
18979     },
18980     
18981     fillMonths: function()
18982     {    
18983         var i = 0;
18984         var months = this.picker().select('>.datepicker-months td', true).first();
18985         
18986         months.dom.innerHTML = '';
18987         
18988         while (i < 12) {
18989             var month = {
18990                 tag: 'span',
18991                 cls: 'month',
18992                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18993             };
18994             
18995             months.createChild(month);
18996         }
18997         
18998     },
18999     
19000     update: function()
19001     {
19002         var _this = this;
19003         
19004         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19005             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19006         }
19007         
19008         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19009             e.removeClass('active');
19010             
19011             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19012                 e.addClass('active');
19013             }
19014         })
19015     },
19016     
19017     place: function()
19018     {
19019         if(this.isInline) {
19020             return;
19021         }
19022         
19023         this.picker().removeClass(['bottom', 'top']);
19024         
19025         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19026             /*
19027              * place to the top of element!
19028              *
19029              */
19030             
19031             this.picker().addClass('top');
19032             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19033             
19034             return;
19035         }
19036         
19037         this.picker().addClass('bottom');
19038         
19039         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19040     },
19041     
19042     onFocus : function()
19043     {
19044         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19045         this.show();
19046     },
19047     
19048     onBlur : function()
19049     {
19050         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19051         
19052         var d = this.inputEl().getValue();
19053         
19054         this.setValue(d);
19055                 
19056         this.hide();
19057     },
19058     
19059     show : function()
19060     {
19061         this.picker().show();
19062         this.picker().select('>.datepicker-months', true).first().show();
19063         this.update();
19064         this.place();
19065         
19066         this.fireEvent('show', this, this.date);
19067     },
19068     
19069     hide : function()
19070     {
19071         if(this.isInline) {
19072             return;
19073         }
19074         this.picker().hide();
19075         this.fireEvent('hide', this, this.date);
19076         
19077     },
19078     
19079     onMousedown: function(e)
19080     {
19081         e.stopPropagation();
19082         e.preventDefault();
19083     },
19084     
19085     keyup: function(e)
19086     {
19087         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19088         this.update();
19089     },
19090
19091     fireKey: function(e)
19092     {
19093         if (!this.picker().isVisible()){
19094             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19095                 this.show();
19096             }
19097             return;
19098         }
19099         
19100         var dir;
19101         
19102         switch(e.keyCode){
19103             case 27: // escape
19104                 this.hide();
19105                 e.preventDefault();
19106                 break;
19107             case 37: // left
19108             case 39: // right
19109                 dir = e.keyCode == 37 ? -1 : 1;
19110                 
19111                 this.vIndex = this.vIndex + dir;
19112                 
19113                 if(this.vIndex < 0){
19114                     this.vIndex = 0;
19115                 }
19116                 
19117                 if(this.vIndex > 11){
19118                     this.vIndex = 11;
19119                 }
19120                 
19121                 if(isNaN(this.vIndex)){
19122                     this.vIndex = 0;
19123                 }
19124                 
19125                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19126                 
19127                 break;
19128             case 38: // up
19129             case 40: // down
19130                 
19131                 dir = e.keyCode == 38 ? -1 : 1;
19132                 
19133                 this.vIndex = this.vIndex + dir * 4;
19134                 
19135                 if(this.vIndex < 0){
19136                     this.vIndex = 0;
19137                 }
19138                 
19139                 if(this.vIndex > 11){
19140                     this.vIndex = 11;
19141                 }
19142                 
19143                 if(isNaN(this.vIndex)){
19144                     this.vIndex = 0;
19145                 }
19146                 
19147                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19148                 break;
19149                 
19150             case 13: // enter
19151                 
19152                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19153                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19154                 }
19155                 
19156                 this.hide();
19157                 e.preventDefault();
19158                 break;
19159             case 9: // tab
19160                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19161                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19162                 }
19163                 this.hide();
19164                 break;
19165             case 16: // shift
19166             case 17: // ctrl
19167             case 18: // alt
19168                 break;
19169             default :
19170                 this.hide();
19171                 
19172         }
19173     },
19174     
19175     remove: function() 
19176     {
19177         this.picker().remove();
19178     }
19179    
19180 });
19181
19182 Roo.apply(Roo.bootstrap.MonthField,  {
19183     
19184     content : {
19185         tag: 'tbody',
19186         cn: [
19187         {
19188             tag: 'tr',
19189             cn: [
19190             {
19191                 tag: 'td',
19192                 colspan: '7'
19193             }
19194             ]
19195         }
19196         ]
19197     },
19198     
19199     dates:{
19200         en: {
19201             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19202             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19203         }
19204     }
19205 });
19206
19207 Roo.apply(Roo.bootstrap.MonthField,  {
19208   
19209     template : {
19210         tag: 'div',
19211         cls: 'datepicker dropdown-menu roo-dynamic',
19212         cn: [
19213             {
19214                 tag: 'div',
19215                 cls: 'datepicker-months',
19216                 cn: [
19217                 {
19218                     tag: 'table',
19219                     cls: 'table-condensed',
19220                     cn:[
19221                         Roo.bootstrap.DateField.content
19222                     ]
19223                 }
19224                 ]
19225             }
19226         ]
19227     }
19228 });
19229
19230  
19231
19232  
19233  /*
19234  * - LGPL
19235  *
19236  * CheckBox
19237  * 
19238  */
19239
19240 /**
19241  * @class Roo.bootstrap.CheckBox
19242  * @extends Roo.bootstrap.Input
19243  * Bootstrap CheckBox class
19244  * 
19245  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19246  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19247  * @cfg {String} boxLabel The text that appears beside the checkbox
19248  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19249  * @cfg {Boolean} checked initnal the element
19250  * @cfg {Boolean} inline inline the element (default false)
19251  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19252  * 
19253  * @constructor
19254  * Create a new CheckBox
19255  * @param {Object} config The config object
19256  */
19257
19258 Roo.bootstrap.CheckBox = function(config){
19259     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19260    
19261     this.addEvents({
19262         /**
19263         * @event check
19264         * Fires when the element is checked or unchecked.
19265         * @param {Roo.bootstrap.CheckBox} this This input
19266         * @param {Boolean} checked The new checked value
19267         */
19268        check : true
19269     });
19270     
19271 };
19272
19273 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19274   
19275     inputType: 'checkbox',
19276     inputValue: 1,
19277     valueOff: 0,
19278     boxLabel: false,
19279     checked: false,
19280     weight : false,
19281     inline: false,
19282     
19283     getAutoCreate : function()
19284     {
19285         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19286         
19287         var id = Roo.id();
19288         
19289         var cfg = {};
19290         
19291         cfg.cls = 'form-group ' + this.inputType; //input-group
19292         
19293         if(this.inline){
19294             cfg.cls += ' ' + this.inputType + '-inline';
19295         }
19296         
19297         var input =  {
19298             tag: 'input',
19299             id : id,
19300             type : this.inputType,
19301             value : this.inputValue,
19302             cls : 'roo-' + this.inputType, //'form-box',
19303             placeholder : this.placeholder || ''
19304             
19305         };
19306         
19307          
19308         var hidden =  {
19309             tag: 'input',
19310             type : 'hidden',
19311             cls : 'roo-hidden-value',
19312             value : this.checked ? this.valueOff : this.inputValue
19313         };
19314             
19315         if (this.weight) { // Validity check?
19316             cfg.cls += " " + this.inputType + "-" + this.weight;
19317         }
19318         
19319         if (this.disabled) {
19320             input.disabled=true;
19321         }
19322         
19323         if(this.checked){
19324             input.checked = this.checked;
19325             
19326         }
19327         
19328         
19329         if (this.name) {
19330             hidden.name = this.name;
19331             input.name = '_hidden_' + this.name;
19332         }
19333         
19334         if (this.size) {
19335             input.cls += ' input-' + this.size;
19336         }
19337         
19338         var settings=this;
19339         
19340         ['xs','sm','md','lg'].map(function(size){
19341             if (settings[size]) {
19342                 cfg.cls += ' col-' + size + '-' + settings[size];
19343             }
19344         });
19345         
19346         var inputblock = {
19347             tag: 'span',
19348             cn : [
19349                     input,
19350                     hidden
19351                 ]
19352         };
19353          
19354         if (this.before || this.after) {
19355             
19356             inputblock = {
19357                 cls : 'input-group',
19358                 cn :  [] 
19359             };
19360             
19361             if (this.before) {
19362                 inputblock.cn.push({
19363                     tag :'span',
19364                     cls : 'input-group-addon',
19365                     html : this.before
19366                 });
19367             }
19368             
19369             inputblock.cn.push(input);
19370             inputblock.cn.push(hidden);
19371             
19372             if (this.after) {
19373                 inputblock.cn.push({
19374                     tag :'span',
19375                     cls : 'input-group-addon',
19376                     html : this.after
19377                 });
19378             }
19379             
19380         }
19381         
19382         if (align ==='left' && this.fieldLabel.length) {
19383 //                Roo.log("left and has label");
19384                 cfg.cn = [
19385                     
19386                     {
19387                         tag: 'label',
19388                         'for' :  id,
19389                         cls : 'control-label col-md-' + this.labelWidth,
19390                         html : this.fieldLabel
19391                         
19392                     },
19393                     {
19394                         cls : "col-md-" + (12 - this.labelWidth), 
19395                         cn: [
19396                             inputblock
19397                         ]
19398                     }
19399                     
19400                 ];
19401         } else if ( this.fieldLabel.length) {
19402 //                Roo.log(" label");
19403                 cfg.cn = [
19404                    
19405                     {
19406                         tag: this.boxLabel ? 'span' : 'label',
19407                         'for': id,
19408                         cls: 'control-label box-input-label',
19409                         //cls : 'input-group-addon',
19410                         html : this.fieldLabel
19411                         
19412                     },
19413                     
19414                     inputblock
19415                     
19416                 ];
19417
19418         } else {
19419             
19420 //                Roo.log(" no label && no align");
19421                 cfg.cn = [  inputblock ] ;
19422                 
19423                 
19424         }
19425         
19426         if(this.boxLabel){
19427              var boxLabelCfg = {
19428                 tag: 'label',
19429                 //'for': id, // box label is handled by onclick - so no for...
19430                 cls: 'box-label',
19431                 html: this.boxLabel
19432             };
19433             
19434             if(this.tooltip){
19435                 boxLabelCfg.tooltip = this.tooltip;
19436             }
19437              
19438             cfg.cn.push(boxLabelCfg);
19439         }
19440         
19441         
19442        
19443         return cfg;
19444         
19445     },
19446     
19447     /**
19448      * return the real input element.
19449      */
19450     inputEl: function ()
19451     {
19452         return this.el.select('input.roo-' + this.inputType,true).first();
19453     },
19454     hiddenEl: function ()
19455     {
19456         return this.el.select('input.hidden-value',true).first();
19457     },
19458     
19459     labelEl: function()
19460     {
19461         return this.el.select('label.control-label',true).first();
19462     },
19463     /* depricated... */
19464     
19465     label: function()
19466     {
19467         return this.labelEl();
19468     },
19469     
19470     boxLabelEl: function()
19471     {
19472         return this.el.select('label.box-label',true).first();
19473     },
19474     
19475     initEvents : function()
19476     {
19477 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19478         
19479         this.inputEl().on('click', this.onClick,  this);
19480         
19481         if (this.boxLabel) { 
19482             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19483         }
19484         
19485         this.startValue = this.getValue();
19486         
19487         if(this.groupId){
19488             Roo.bootstrap.CheckBox.register(this);
19489         }
19490     },
19491     
19492     onClick : function()
19493     {   
19494         this.setChecked(!this.checked);
19495     },
19496     
19497     setChecked : function(state,suppressEvent)
19498     {
19499         this.startValue = this.getValue();
19500         
19501         if(this.inputType == 'radio'){
19502             
19503             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19504                 e.dom.checked = false;
19505             });
19506             
19507             this.inputEl().dom.checked = true;
19508             
19509             this.inputEl().dom.value = this.inputValue;
19510             
19511             if(suppressEvent !== true){
19512                 this.fireEvent('check', this, true);
19513             }
19514             
19515             this.validate();
19516             
19517             return;
19518         }
19519         
19520         this.checked = state;
19521         
19522         this.inputEl().dom.checked = state;
19523         
19524         
19525         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19526         
19527         if(suppressEvent !== true){
19528             this.fireEvent('check', this, state);
19529         }
19530         
19531         this.validate();
19532     },
19533     
19534     getValue : function()
19535     {
19536         if(this.inputType == 'radio'){
19537             return this.getGroupValue();
19538         }
19539         
19540         return this.hiddenEl() ? this.hiddenEl().dom.value : this.value;
19541         
19542     },
19543     
19544     getGroupValue : function()
19545     {
19546         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19547             return '';
19548         }
19549         
19550         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19551     },
19552     
19553     setValue : function(v,suppressEvent)
19554     {
19555         if(this.inputType == 'radio'){
19556             this.setGroupValue(v, suppressEvent);
19557             return;
19558         }
19559         
19560         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19561         
19562         this.validate();
19563     },
19564     
19565     setGroupValue : function(v, suppressEvent)
19566     {
19567         this.startValue = this.getValue();
19568         
19569         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19570             e.dom.checked = false;
19571             
19572             if(e.dom.value == v){
19573                 e.dom.checked = true;
19574             }
19575         });
19576         
19577         if(suppressEvent !== true){
19578             this.fireEvent('check', this, true);
19579         }
19580
19581         this.validate();
19582         
19583         return;
19584     },
19585     
19586     validate : function()
19587     {
19588         if(
19589                 this.disabled || 
19590                 (this.inputType == 'radio' && this.validateRadio()) ||
19591                 (this.inputType == 'checkbox' && this.validateCheckbox())
19592         ){
19593             this.markValid();
19594             return true;
19595         }
19596         
19597         this.markInvalid();
19598         return false;
19599     },
19600     
19601     validateRadio : function()
19602     {
19603         if(this.allowBlank){
19604             return true;
19605         }
19606         
19607         var valid = false;
19608         
19609         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19610             if(!e.dom.checked){
19611                 return;
19612             }
19613             
19614             valid = true;
19615             
19616             return false;
19617         });
19618         
19619         return valid;
19620     },
19621     
19622     validateCheckbox : function()
19623     {
19624         if(!this.groupId){
19625             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19626         }
19627         
19628         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19629         
19630         if(!group){
19631             return false;
19632         }
19633         
19634         var r = false;
19635         
19636         for(var i in group){
19637             if(r){
19638                 break;
19639             }
19640             
19641             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19642         }
19643         
19644         return r;
19645     },
19646     
19647     /**
19648      * Mark this field as valid
19649      */
19650     markValid : function()
19651     {
19652         if(this.allowBlank){
19653             return;
19654         }
19655         
19656         var _this = this;
19657         
19658         this.fireEvent('valid', this);
19659         
19660         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19661         
19662         if(this.groupId){
19663             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19664         }
19665         
19666         if(label){
19667             label.markValid();
19668         }
19669         
19670         if(this.inputType == 'radio'){
19671             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19672                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19673                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19674             });
19675             
19676             return;
19677         }
19678         
19679         if(!this.groupId){
19680             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19681             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19682             return;
19683         }
19684         
19685         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19686             
19687         if(!group){
19688             return;
19689         }
19690         
19691         for(var i in group){
19692             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19693             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19694         }
19695     },
19696     
19697      /**
19698      * Mark this field as invalid
19699      * @param {String} msg The validation message
19700      */
19701     markInvalid : function(msg)
19702     {
19703         if(this.allowBlank){
19704             return;
19705         }
19706         
19707         var _this = this;
19708         
19709         this.fireEvent('invalid', this, msg);
19710         
19711         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19712         
19713         if(this.groupId){
19714             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19715         }
19716         
19717         if(label){
19718             label.markInvalid();
19719         }
19720             
19721         if(this.inputType == 'radio'){
19722             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19723                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19724                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19725             });
19726             
19727             return;
19728         }
19729         
19730         if(!this.groupId){
19731             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19732             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19733             return;
19734         }
19735         
19736         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19737         
19738         if(!group){
19739             return;
19740         }
19741         
19742         for(var i in group){
19743             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19744             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19745         }
19746         
19747     },
19748     
19749     disable : function()
19750     {
19751         if(this.inputType != 'radio'){
19752             Roo.bootstrap.CheckBox.superclass.disable.call(this);
19753             return;
19754         }
19755         
19756         var _this = this;
19757         
19758         if(this.rendered){
19759             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19760                 _this.getActionEl().addClass(this.disabledClass);
19761                 e.dom.disabled = true;
19762             });
19763         }
19764         
19765         this.disabled = true;
19766         this.fireEvent("disable", this);
19767         return this;
19768     },
19769
19770     enable : function()
19771     {
19772         if(this.inputType != 'radio'){
19773             Roo.bootstrap.CheckBox.superclass.enable.call(this);
19774             return;
19775         }
19776         
19777         var _this = this;
19778         
19779         if(this.rendered){
19780             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19781                 _this.getActionEl().removeClass(this.disabledClass);
19782                 e.dom.disabled = false;
19783             });
19784         }
19785         
19786         this.disabled = false;
19787         this.fireEvent("enable", this);
19788         return this;
19789     }
19790     
19791
19792 });
19793
19794 Roo.apply(Roo.bootstrap.CheckBox, {
19795     
19796     groups: {},
19797     
19798      /**
19799     * register a CheckBox Group
19800     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19801     */
19802     register : function(checkbox)
19803     {
19804         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19805             this.groups[checkbox.groupId] = {};
19806         }
19807         
19808         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19809             return;
19810         }
19811         
19812         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19813         
19814     },
19815     /**
19816     * fetch a CheckBox Group based on the group ID
19817     * @param {string} the group ID
19818     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19819     */
19820     get: function(groupId) {
19821         if (typeof(this.groups[groupId]) == 'undefined') {
19822             return false;
19823         }
19824         
19825         return this.groups[groupId] ;
19826     }
19827     
19828     
19829 });
19830 /*
19831  * - LGPL
19832  *
19833  * Radio
19834  *
19835  *
19836  * not inline
19837  *<div class="radio">
19838   <label>
19839     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19840     Option one is this and that&mdash;be sure to include why it's great
19841   </label>
19842 </div>
19843  *
19844  *
19845  *inline
19846  *<span>
19847  *<label class="radio-inline">fieldLabel</label>
19848  *<label class="radio-inline">
19849   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19850 </label>
19851 <span>
19852  * 
19853  * 
19854  */
19855
19856 /**
19857  * @class Roo.bootstrap.Radio
19858  * @extends Roo.bootstrap.CheckBox
19859  * Bootstrap Radio class
19860
19861  * @constructor
19862  * Create a new Radio
19863  * @param {Object} config The config object
19864  */
19865
19866 Roo.bootstrap.Radio = function(config){
19867     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19868    
19869 };
19870
19871 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19872     
19873     inputType: 'radio',
19874     inputValue: '',
19875     valueOff: '',
19876     
19877     getAutoCreate : function()
19878     {
19879         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19880         align = align || 'left'; // default...
19881         
19882         
19883         
19884         var id = Roo.id();
19885         
19886         var cfg = {
19887                 tag : this.inline ? 'span' : 'div',
19888                 cls : '',
19889                 cn : []
19890         };
19891         
19892         var inline = this.inline ? ' radio-inline' : '';
19893         
19894         var lbl = {
19895                 tag: 'label' ,
19896                 // does not need for, as we wrap the input with it..
19897                 'for' : id,
19898                 cls : 'control-label box-label' + inline,
19899                 cn : []
19900         };
19901         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19902         
19903         var fieldLabel = {
19904             tag: 'label' ,
19905             //cls : 'control-label' + inline,
19906             html : this.fieldLabel,
19907             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19908         };
19909         
19910  
19911         
19912         
19913         var input =  {
19914             tag: 'input',
19915             id : id,
19916             type : this.inputType,
19917             //value : (!this.checked) ? this.valueOff : this.inputValue,
19918             value : this.inputValue,
19919             cls : 'roo-radio',
19920             placeholder : this.placeholder || '' // ?? needed????
19921             
19922         };
19923         if (this.weight) { // Validity check?
19924             input.cls += " radio-" + this.weight;
19925         }
19926         if (this.disabled) {
19927             input.disabled=true;
19928         }
19929         
19930         if(this.checked){
19931             input.checked = this.checked;
19932         }
19933         
19934         if (this.name) {
19935             input.name = this.name;
19936         }
19937         
19938         if (this.size) {
19939             input.cls += ' input-' + this.size;
19940         }
19941         
19942         //?? can span's inline have a width??
19943         
19944         var settings=this;
19945         ['xs','sm','md','lg'].map(function(size){
19946             if (settings[size]) {
19947                 cfg.cls += ' col-' + size + '-' + settings[size];
19948             }
19949         });
19950         
19951         var inputblock = input;
19952         
19953         if (this.before || this.after) {
19954             
19955             inputblock = {
19956                 cls : 'input-group',
19957                 tag : 'span',
19958                 cn :  [] 
19959             };
19960             if (this.before) {
19961                 inputblock.cn.push({
19962                     tag :'span',
19963                     cls : 'input-group-addon',
19964                     html : this.before
19965                 });
19966             }
19967             inputblock.cn.push(input);
19968             if (this.after) {
19969                 inputblock.cn.push({
19970                     tag :'span',
19971                     cls : 'input-group-addon',
19972                     html : this.after
19973                 });
19974             }
19975             
19976         };
19977         
19978         
19979         if (this.fieldLabel && this.fieldLabel.length) {
19980             cfg.cn.push(fieldLabel);
19981         }
19982        
19983         // normal bootstrap puts the input inside the label.
19984         // however with our styled version - it has to go after the input.
19985        
19986         //lbl.cn.push(inputblock);
19987         
19988         var lblwrap =  {
19989             tag: 'span',
19990             cls: 'radio' + inline,
19991             cn: [
19992                 inputblock,
19993                 lbl
19994             ]
19995         };
19996         
19997         cfg.cn.push( lblwrap);
19998         
19999         if(this.boxLabel){
20000             lbl.cn.push({
20001                 tag: 'span',
20002                 html: this.boxLabel
20003             })
20004         }
20005          
20006         
20007         return cfg;
20008         
20009     },
20010     
20011     initEvents : function()
20012     {
20013 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20014         
20015         this.inputEl().on('click', this.onClick,  this);
20016         if (this.boxLabel) {
20017             //Roo.log('find label');
20018             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
20019         }
20020         
20021     },
20022     
20023     inputEl: function ()
20024     {
20025         return this.el.select('input.roo-radio',true).first();
20026     },
20027     onClick : function()
20028     {   
20029         Roo.log("click");
20030         this.setChecked(true);
20031     },
20032     
20033     setChecked : function(state,suppressEvent)
20034     {
20035         if(state){
20036             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20037                 v.dom.checked = false;
20038             });
20039         }
20040         Roo.log(this.inputEl().dom);
20041         this.checked = state;
20042         this.inputEl().dom.checked = state;
20043         
20044         if(suppressEvent !== true){
20045             this.fireEvent('check', this, state);
20046         }
20047         
20048         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20049         
20050     },
20051     
20052     getGroupValue : function()
20053     {
20054         var value = '';
20055         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20056             if(v.dom.checked == true){
20057                 value = v.dom.value;
20058             }
20059         });
20060         
20061         return value;
20062     },
20063     
20064     /**
20065      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20066      * @return {Mixed} value The field value
20067      */
20068     getValue : function(){
20069         return this.getGroupValue();
20070     }
20071     
20072 });
20073
20074  
20075 //<script type="text/javascript">
20076
20077 /*
20078  * Based  Ext JS Library 1.1.1
20079  * Copyright(c) 2006-2007, Ext JS, LLC.
20080  * LGPL
20081  *
20082  */
20083  
20084 /**
20085  * @class Roo.HtmlEditorCore
20086  * @extends Roo.Component
20087  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20088  *
20089  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20090  */
20091
20092 Roo.HtmlEditorCore = function(config){
20093     
20094     
20095     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20096     
20097     
20098     this.addEvents({
20099         /**
20100          * @event initialize
20101          * Fires when the editor is fully initialized (including the iframe)
20102          * @param {Roo.HtmlEditorCore} this
20103          */
20104         initialize: true,
20105         /**
20106          * @event activate
20107          * Fires when the editor is first receives the focus. Any insertion must wait
20108          * until after this event.
20109          * @param {Roo.HtmlEditorCore} this
20110          */
20111         activate: true,
20112          /**
20113          * @event beforesync
20114          * Fires before the textarea is updated with content from the editor iframe. Return false
20115          * to cancel the sync.
20116          * @param {Roo.HtmlEditorCore} this
20117          * @param {String} html
20118          */
20119         beforesync: true,
20120          /**
20121          * @event beforepush
20122          * Fires before the iframe editor is updated with content from the textarea. Return false
20123          * to cancel the push.
20124          * @param {Roo.HtmlEditorCore} this
20125          * @param {String} html
20126          */
20127         beforepush: true,
20128          /**
20129          * @event sync
20130          * Fires when the textarea is updated with content from the editor iframe.
20131          * @param {Roo.HtmlEditorCore} this
20132          * @param {String} html
20133          */
20134         sync: true,
20135          /**
20136          * @event push
20137          * Fires when the iframe editor is updated with content from the textarea.
20138          * @param {Roo.HtmlEditorCore} this
20139          * @param {String} html
20140          */
20141         push: true,
20142         
20143         /**
20144          * @event editorevent
20145          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20146          * @param {Roo.HtmlEditorCore} this
20147          */
20148         editorevent: true
20149         
20150     });
20151     
20152     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20153     
20154     // defaults : white / black...
20155     this.applyBlacklists();
20156     
20157     
20158     
20159 };
20160
20161
20162 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20163
20164
20165      /**
20166      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20167      */
20168     
20169     owner : false,
20170     
20171      /**
20172      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20173      *                        Roo.resizable.
20174      */
20175     resizable : false,
20176      /**
20177      * @cfg {Number} height (in pixels)
20178      */   
20179     height: 300,
20180    /**
20181      * @cfg {Number} width (in pixels)
20182      */   
20183     width: 500,
20184     
20185     /**
20186      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20187      * 
20188      */
20189     stylesheets: false,
20190     
20191     // id of frame..
20192     frameId: false,
20193     
20194     // private properties
20195     validationEvent : false,
20196     deferHeight: true,
20197     initialized : false,
20198     activated : false,
20199     sourceEditMode : false,
20200     onFocus : Roo.emptyFn,
20201     iframePad:3,
20202     hideMode:'offsets',
20203     
20204     clearUp: true,
20205     
20206     // blacklist + whitelisted elements..
20207     black: false,
20208     white: false,
20209      
20210     
20211
20212     /**
20213      * Protected method that will not generally be called directly. It
20214      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20215      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20216      */
20217     getDocMarkup : function(){
20218         // body styles..
20219         var st = '';
20220         
20221         // inherit styels from page...?? 
20222         if (this.stylesheets === false) {
20223             
20224             Roo.get(document.head).select('style').each(function(node) {
20225                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20226             });
20227             
20228             Roo.get(document.head).select('link').each(function(node) { 
20229                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20230             });
20231             
20232         } else if (!this.stylesheets.length) {
20233                 // simple..
20234                 st = '<style type="text/css">' +
20235                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20236                    '</style>';
20237         } else { 
20238             
20239         }
20240         
20241         st +=  '<style type="text/css">' +
20242             'IMG { cursor: pointer } ' +
20243         '</style>';
20244
20245         
20246         return '<html><head>' + st  +
20247             //<style type="text/css">' +
20248             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20249             //'</style>' +
20250             ' </head><body class="roo-htmleditor-body"></body></html>';
20251     },
20252
20253     // private
20254     onRender : function(ct, position)
20255     {
20256         var _t = this;
20257         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20258         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20259         
20260         
20261         this.el.dom.style.border = '0 none';
20262         this.el.dom.setAttribute('tabIndex', -1);
20263         this.el.addClass('x-hidden hide');
20264         
20265         
20266         
20267         if(Roo.isIE){ // fix IE 1px bogus margin
20268             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20269         }
20270        
20271         
20272         this.frameId = Roo.id();
20273         
20274          
20275         
20276         var iframe = this.owner.wrap.createChild({
20277             tag: 'iframe',
20278             cls: 'form-control', // bootstrap..
20279             id: this.frameId,
20280             name: this.frameId,
20281             frameBorder : 'no',
20282             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20283         }, this.el
20284         );
20285         
20286         
20287         this.iframe = iframe.dom;
20288
20289          this.assignDocWin();
20290         
20291         this.doc.designMode = 'on';
20292        
20293         this.doc.open();
20294         this.doc.write(this.getDocMarkup());
20295         this.doc.close();
20296
20297         
20298         var task = { // must defer to wait for browser to be ready
20299             run : function(){
20300                 //console.log("run task?" + this.doc.readyState);
20301                 this.assignDocWin();
20302                 if(this.doc.body || this.doc.readyState == 'complete'){
20303                     try {
20304                         this.doc.designMode="on";
20305                     } catch (e) {
20306                         return;
20307                     }
20308                     Roo.TaskMgr.stop(task);
20309                     this.initEditor.defer(10, this);
20310                 }
20311             },
20312             interval : 10,
20313             duration: 10000,
20314             scope: this
20315         };
20316         Roo.TaskMgr.start(task);
20317
20318     },
20319
20320     // private
20321     onResize : function(w, h)
20322     {
20323          Roo.log('resize: ' +w + ',' + h );
20324         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20325         if(!this.iframe){
20326             return;
20327         }
20328         if(typeof w == 'number'){
20329             
20330             this.iframe.style.width = w + 'px';
20331         }
20332         if(typeof h == 'number'){
20333             
20334             this.iframe.style.height = h + 'px';
20335             if(this.doc){
20336                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20337             }
20338         }
20339         
20340     },
20341
20342     /**
20343      * Toggles the editor between standard and source edit mode.
20344      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20345      */
20346     toggleSourceEdit : function(sourceEditMode){
20347         
20348         this.sourceEditMode = sourceEditMode === true;
20349         
20350         if(this.sourceEditMode){
20351  
20352             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20353             
20354         }else{
20355             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20356             //this.iframe.className = '';
20357             this.deferFocus();
20358         }
20359         //this.setSize(this.owner.wrap.getSize());
20360         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20361     },
20362
20363     
20364   
20365
20366     /**
20367      * Protected method that will not generally be called directly. If you need/want
20368      * custom HTML cleanup, this is the method you should override.
20369      * @param {String} html The HTML to be cleaned
20370      * return {String} The cleaned HTML
20371      */
20372     cleanHtml : function(html){
20373         html = String(html);
20374         if(html.length > 5){
20375             if(Roo.isSafari){ // strip safari nonsense
20376                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20377             }
20378         }
20379         if(html == '&nbsp;'){
20380             html = '';
20381         }
20382         return html;
20383     },
20384
20385     /**
20386      * HTML Editor -> Textarea
20387      * Protected method that will not generally be called directly. Syncs the contents
20388      * of the editor iframe with the textarea.
20389      */
20390     syncValue : function(){
20391         if(this.initialized){
20392             var bd = (this.doc.body || this.doc.documentElement);
20393             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20394             var html = bd.innerHTML;
20395             if(Roo.isSafari){
20396                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20397                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20398                 if(m && m[1]){
20399                     html = '<div style="'+m[0]+'">' + html + '</div>';
20400                 }
20401             }
20402             html = this.cleanHtml(html);
20403             // fix up the special chars.. normaly like back quotes in word...
20404             // however we do not want to do this with chinese..
20405             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20406                 var cc = b.charCodeAt();
20407                 if (
20408                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20409                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20410                     (cc >= 0xf900 && cc < 0xfb00 )
20411                 ) {
20412                         return b;
20413                 }
20414                 return "&#"+cc+";" 
20415             });
20416             if(this.owner.fireEvent('beforesync', this, html) !== false){
20417                 this.el.dom.value = html;
20418                 this.owner.fireEvent('sync', this, html);
20419             }
20420         }
20421     },
20422
20423     /**
20424      * Protected method that will not generally be called directly. Pushes the value of the textarea
20425      * into the iframe editor.
20426      */
20427     pushValue : function(){
20428         if(this.initialized){
20429             var v = this.el.dom.value.trim();
20430             
20431 //            if(v.length < 1){
20432 //                v = '&#160;';
20433 //            }
20434             
20435             if(this.owner.fireEvent('beforepush', this, v) !== false){
20436                 var d = (this.doc.body || this.doc.documentElement);
20437                 d.innerHTML = v;
20438                 this.cleanUpPaste();
20439                 this.el.dom.value = d.innerHTML;
20440                 this.owner.fireEvent('push', this, v);
20441             }
20442         }
20443     },
20444
20445     // private
20446     deferFocus : function(){
20447         this.focus.defer(10, this);
20448     },
20449
20450     // doc'ed in Field
20451     focus : function(){
20452         if(this.win && !this.sourceEditMode){
20453             this.win.focus();
20454         }else{
20455             this.el.focus();
20456         }
20457     },
20458     
20459     assignDocWin: function()
20460     {
20461         var iframe = this.iframe;
20462         
20463          if(Roo.isIE){
20464             this.doc = iframe.contentWindow.document;
20465             this.win = iframe.contentWindow;
20466         } else {
20467 //            if (!Roo.get(this.frameId)) {
20468 //                return;
20469 //            }
20470 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20471 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20472             
20473             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20474                 return;
20475             }
20476             
20477             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20478             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20479         }
20480     },
20481     
20482     // private
20483     initEditor : function(){
20484         //console.log("INIT EDITOR");
20485         this.assignDocWin();
20486         
20487         
20488         
20489         this.doc.designMode="on";
20490         this.doc.open();
20491         this.doc.write(this.getDocMarkup());
20492         this.doc.close();
20493         
20494         var dbody = (this.doc.body || this.doc.documentElement);
20495         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20496         // this copies styles from the containing element into thsi one..
20497         // not sure why we need all of this..
20498         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20499         
20500         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20501         //ss['background-attachment'] = 'fixed'; // w3c
20502         dbody.bgProperties = 'fixed'; // ie
20503         //Roo.DomHelper.applyStyles(dbody, ss);
20504         Roo.EventManager.on(this.doc, {
20505             //'mousedown': this.onEditorEvent,
20506             'mouseup': this.onEditorEvent,
20507             'dblclick': this.onEditorEvent,
20508             'click': this.onEditorEvent,
20509             'keyup': this.onEditorEvent,
20510             buffer:100,
20511             scope: this
20512         });
20513         if(Roo.isGecko){
20514             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20515         }
20516         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20517             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20518         }
20519         this.initialized = true;
20520
20521         this.owner.fireEvent('initialize', this);
20522         this.pushValue();
20523     },
20524
20525     // private
20526     onDestroy : function(){
20527         
20528         
20529         
20530         if(this.rendered){
20531             
20532             //for (var i =0; i < this.toolbars.length;i++) {
20533             //    // fixme - ask toolbars for heights?
20534             //    this.toolbars[i].onDestroy();
20535            // }
20536             
20537             //this.wrap.dom.innerHTML = '';
20538             //this.wrap.remove();
20539         }
20540     },
20541
20542     // private
20543     onFirstFocus : function(){
20544         
20545         this.assignDocWin();
20546         
20547         
20548         this.activated = true;
20549          
20550     
20551         if(Roo.isGecko){ // prevent silly gecko errors
20552             this.win.focus();
20553             var s = this.win.getSelection();
20554             if(!s.focusNode || s.focusNode.nodeType != 3){
20555                 var r = s.getRangeAt(0);
20556                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20557                 r.collapse(true);
20558                 this.deferFocus();
20559             }
20560             try{
20561                 this.execCmd('useCSS', true);
20562                 this.execCmd('styleWithCSS', false);
20563             }catch(e){}
20564         }
20565         this.owner.fireEvent('activate', this);
20566     },
20567
20568     // private
20569     adjustFont: function(btn){
20570         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20571         //if(Roo.isSafari){ // safari
20572         //    adjust *= 2;
20573        // }
20574         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20575         if(Roo.isSafari){ // safari
20576             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20577             v =  (v < 10) ? 10 : v;
20578             v =  (v > 48) ? 48 : v;
20579             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20580             
20581         }
20582         
20583         
20584         v = Math.max(1, v+adjust);
20585         
20586         this.execCmd('FontSize', v  );
20587     },
20588
20589     onEditorEvent : function(e)
20590     {
20591         this.owner.fireEvent('editorevent', this, e);
20592       //  this.updateToolbar();
20593         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20594     },
20595
20596     insertTag : function(tg)
20597     {
20598         // could be a bit smarter... -> wrap the current selected tRoo..
20599         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20600             
20601             range = this.createRange(this.getSelection());
20602             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20603             wrappingNode.appendChild(range.extractContents());
20604             range.insertNode(wrappingNode);
20605
20606             return;
20607             
20608             
20609             
20610         }
20611         this.execCmd("formatblock",   tg);
20612         
20613     },
20614     
20615     insertText : function(txt)
20616     {
20617         
20618         
20619         var range = this.createRange();
20620         range.deleteContents();
20621                //alert(Sender.getAttribute('label'));
20622                
20623         range.insertNode(this.doc.createTextNode(txt));
20624     } ,
20625     
20626      
20627
20628     /**
20629      * Executes a Midas editor command on the editor document and performs necessary focus and
20630      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20631      * @param {String} cmd The Midas command
20632      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20633      */
20634     relayCmd : function(cmd, value){
20635         this.win.focus();
20636         this.execCmd(cmd, value);
20637         this.owner.fireEvent('editorevent', this);
20638         //this.updateToolbar();
20639         this.owner.deferFocus();
20640     },
20641
20642     /**
20643      * Executes a Midas editor command directly on the editor document.
20644      * For visual commands, you should use {@link #relayCmd} instead.
20645      * <b>This should only be called after the editor is initialized.</b>
20646      * @param {String} cmd The Midas command
20647      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20648      */
20649     execCmd : function(cmd, value){
20650         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20651         this.syncValue();
20652     },
20653  
20654  
20655    
20656     /**
20657      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20658      * to insert tRoo.
20659      * @param {String} text | dom node.. 
20660      */
20661     insertAtCursor : function(text)
20662     {
20663         
20664         
20665         
20666         if(!this.activated){
20667             return;
20668         }
20669         /*
20670         if(Roo.isIE){
20671             this.win.focus();
20672             var r = this.doc.selection.createRange();
20673             if(r){
20674                 r.collapse(true);
20675                 r.pasteHTML(text);
20676                 this.syncValue();
20677                 this.deferFocus();
20678             
20679             }
20680             return;
20681         }
20682         */
20683         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20684             this.win.focus();
20685             
20686             
20687             // from jquery ui (MIT licenced)
20688             var range, node;
20689             var win = this.win;
20690             
20691             if (win.getSelection && win.getSelection().getRangeAt) {
20692                 range = win.getSelection().getRangeAt(0);
20693                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20694                 range.insertNode(node);
20695             } else if (win.document.selection && win.document.selection.createRange) {
20696                 // no firefox support
20697                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20698                 win.document.selection.createRange().pasteHTML(txt);
20699             } else {
20700                 // no firefox support
20701                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20702                 this.execCmd('InsertHTML', txt);
20703             } 
20704             
20705             this.syncValue();
20706             
20707             this.deferFocus();
20708         }
20709     },
20710  // private
20711     mozKeyPress : function(e){
20712         if(e.ctrlKey){
20713             var c = e.getCharCode(), cmd;
20714           
20715             if(c > 0){
20716                 c = String.fromCharCode(c).toLowerCase();
20717                 switch(c){
20718                     case 'b':
20719                         cmd = 'bold';
20720                         break;
20721                     case 'i':
20722                         cmd = 'italic';
20723                         break;
20724                     
20725                     case 'u':
20726                         cmd = 'underline';
20727                         break;
20728                     
20729                     case 'v':
20730                         this.cleanUpPaste.defer(100, this);
20731                         return;
20732                         
20733                 }
20734                 if(cmd){
20735                     this.win.focus();
20736                     this.execCmd(cmd);
20737                     this.deferFocus();
20738                     e.preventDefault();
20739                 }
20740                 
20741             }
20742         }
20743     },
20744
20745     // private
20746     fixKeys : function(){ // load time branching for fastest keydown performance
20747         if(Roo.isIE){
20748             return function(e){
20749                 var k = e.getKey(), r;
20750                 if(k == e.TAB){
20751                     e.stopEvent();
20752                     r = this.doc.selection.createRange();
20753                     if(r){
20754                         r.collapse(true);
20755                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20756                         this.deferFocus();
20757                     }
20758                     return;
20759                 }
20760                 
20761                 if(k == e.ENTER){
20762                     r = this.doc.selection.createRange();
20763                     if(r){
20764                         var target = r.parentElement();
20765                         if(!target || target.tagName.toLowerCase() != 'li'){
20766                             e.stopEvent();
20767                             r.pasteHTML('<br />');
20768                             r.collapse(false);
20769                             r.select();
20770                         }
20771                     }
20772                 }
20773                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20774                     this.cleanUpPaste.defer(100, this);
20775                     return;
20776                 }
20777                 
20778                 
20779             };
20780         }else if(Roo.isOpera){
20781             return function(e){
20782                 var k = e.getKey();
20783                 if(k == e.TAB){
20784                     e.stopEvent();
20785                     this.win.focus();
20786                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20787                     this.deferFocus();
20788                 }
20789                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20790                     this.cleanUpPaste.defer(100, this);
20791                     return;
20792                 }
20793                 
20794             };
20795         }else if(Roo.isSafari){
20796             return function(e){
20797                 var k = e.getKey();
20798                 
20799                 if(k == e.TAB){
20800                     e.stopEvent();
20801                     this.execCmd('InsertText','\t');
20802                     this.deferFocus();
20803                     return;
20804                 }
20805                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20806                     this.cleanUpPaste.defer(100, this);
20807                     return;
20808                 }
20809                 
20810              };
20811         }
20812     }(),
20813     
20814     getAllAncestors: function()
20815     {
20816         var p = this.getSelectedNode();
20817         var a = [];
20818         if (!p) {
20819             a.push(p); // push blank onto stack..
20820             p = this.getParentElement();
20821         }
20822         
20823         
20824         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20825             a.push(p);
20826             p = p.parentNode;
20827         }
20828         a.push(this.doc.body);
20829         return a;
20830     },
20831     lastSel : false,
20832     lastSelNode : false,
20833     
20834     
20835     getSelection : function() 
20836     {
20837         this.assignDocWin();
20838         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20839     },
20840     
20841     getSelectedNode: function() 
20842     {
20843         // this may only work on Gecko!!!
20844         
20845         // should we cache this!!!!
20846         
20847         
20848         
20849          
20850         var range = this.createRange(this.getSelection()).cloneRange();
20851         
20852         if (Roo.isIE) {
20853             var parent = range.parentElement();
20854             while (true) {
20855                 var testRange = range.duplicate();
20856                 testRange.moveToElementText(parent);
20857                 if (testRange.inRange(range)) {
20858                     break;
20859                 }
20860                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20861                     break;
20862                 }
20863                 parent = parent.parentElement;
20864             }
20865             return parent;
20866         }
20867         
20868         // is ancestor a text element.
20869         var ac =  range.commonAncestorContainer;
20870         if (ac.nodeType == 3) {
20871             ac = ac.parentNode;
20872         }
20873         
20874         var ar = ac.childNodes;
20875          
20876         var nodes = [];
20877         var other_nodes = [];
20878         var has_other_nodes = false;
20879         for (var i=0;i<ar.length;i++) {
20880             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20881                 continue;
20882             }
20883             // fullly contained node.
20884             
20885             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20886                 nodes.push(ar[i]);
20887                 continue;
20888             }
20889             
20890             // probably selected..
20891             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20892                 other_nodes.push(ar[i]);
20893                 continue;
20894             }
20895             // outer..
20896             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20897                 continue;
20898             }
20899             
20900             
20901             has_other_nodes = true;
20902         }
20903         if (!nodes.length && other_nodes.length) {
20904             nodes= other_nodes;
20905         }
20906         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20907             return false;
20908         }
20909         
20910         return nodes[0];
20911     },
20912     createRange: function(sel)
20913     {
20914         // this has strange effects when using with 
20915         // top toolbar - not sure if it's a great idea.
20916         //this.editor.contentWindow.focus();
20917         if (typeof sel != "undefined") {
20918             try {
20919                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20920             } catch(e) {
20921                 return this.doc.createRange();
20922             }
20923         } else {
20924             return this.doc.createRange();
20925         }
20926     },
20927     getParentElement: function()
20928     {
20929         
20930         this.assignDocWin();
20931         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20932         
20933         var range = this.createRange(sel);
20934          
20935         try {
20936             var p = range.commonAncestorContainer;
20937             while (p.nodeType == 3) { // text node
20938                 p = p.parentNode;
20939             }
20940             return p;
20941         } catch (e) {
20942             return null;
20943         }
20944     
20945     },
20946     /***
20947      *
20948      * Range intersection.. the hard stuff...
20949      *  '-1' = before
20950      *  '0' = hits..
20951      *  '1' = after.
20952      *         [ -- selected range --- ]
20953      *   [fail]                        [fail]
20954      *
20955      *    basically..
20956      *      if end is before start or  hits it. fail.
20957      *      if start is after end or hits it fail.
20958      *
20959      *   if either hits (but other is outside. - then it's not 
20960      *   
20961      *    
20962      **/
20963     
20964     
20965     // @see http://www.thismuchiknow.co.uk/?p=64.
20966     rangeIntersectsNode : function(range, node)
20967     {
20968         var nodeRange = node.ownerDocument.createRange();
20969         try {
20970             nodeRange.selectNode(node);
20971         } catch (e) {
20972             nodeRange.selectNodeContents(node);
20973         }
20974     
20975         var rangeStartRange = range.cloneRange();
20976         rangeStartRange.collapse(true);
20977     
20978         var rangeEndRange = range.cloneRange();
20979         rangeEndRange.collapse(false);
20980     
20981         var nodeStartRange = nodeRange.cloneRange();
20982         nodeStartRange.collapse(true);
20983     
20984         var nodeEndRange = nodeRange.cloneRange();
20985         nodeEndRange.collapse(false);
20986     
20987         return rangeStartRange.compareBoundaryPoints(
20988                  Range.START_TO_START, nodeEndRange) == -1 &&
20989                rangeEndRange.compareBoundaryPoints(
20990                  Range.START_TO_START, nodeStartRange) == 1;
20991         
20992          
20993     },
20994     rangeCompareNode : function(range, node)
20995     {
20996         var nodeRange = node.ownerDocument.createRange();
20997         try {
20998             nodeRange.selectNode(node);
20999         } catch (e) {
21000             nodeRange.selectNodeContents(node);
21001         }
21002         
21003         
21004         range.collapse(true);
21005     
21006         nodeRange.collapse(true);
21007      
21008         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21009         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21010          
21011         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21012         
21013         var nodeIsBefore   =  ss == 1;
21014         var nodeIsAfter    = ee == -1;
21015         
21016         if (nodeIsBefore && nodeIsAfter) {
21017             return 0; // outer
21018         }
21019         if (!nodeIsBefore && nodeIsAfter) {
21020             return 1; //right trailed.
21021         }
21022         
21023         if (nodeIsBefore && !nodeIsAfter) {
21024             return 2;  // left trailed.
21025         }
21026         // fully contined.
21027         return 3;
21028     },
21029
21030     // private? - in a new class?
21031     cleanUpPaste :  function()
21032     {
21033         // cleans up the whole document..
21034         Roo.log('cleanuppaste');
21035         
21036         this.cleanUpChildren(this.doc.body);
21037         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21038         if (clean != this.doc.body.innerHTML) {
21039             this.doc.body.innerHTML = clean;
21040         }
21041         
21042     },
21043     
21044     cleanWordChars : function(input) {// change the chars to hex code
21045         var he = Roo.HtmlEditorCore;
21046         
21047         var output = input;
21048         Roo.each(he.swapCodes, function(sw) { 
21049             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21050             
21051             output = output.replace(swapper, sw[1]);
21052         });
21053         
21054         return output;
21055     },
21056     
21057     
21058     cleanUpChildren : function (n)
21059     {
21060         if (!n.childNodes.length) {
21061             return;
21062         }
21063         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21064            this.cleanUpChild(n.childNodes[i]);
21065         }
21066     },
21067     
21068     
21069         
21070     
21071     cleanUpChild : function (node)
21072     {
21073         var ed = this;
21074         //console.log(node);
21075         if (node.nodeName == "#text") {
21076             // clean up silly Windows -- stuff?
21077             return; 
21078         }
21079         if (node.nodeName == "#comment") {
21080             node.parentNode.removeChild(node);
21081             // clean up silly Windows -- stuff?
21082             return; 
21083         }
21084         var lcname = node.tagName.toLowerCase();
21085         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21086         // whitelist of tags..
21087         
21088         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21089             // remove node.
21090             node.parentNode.removeChild(node);
21091             return;
21092             
21093         }
21094         
21095         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21096         
21097         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21098         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21099         
21100         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21101         //    remove_keep_children = true;
21102         //}
21103         
21104         if (remove_keep_children) {
21105             this.cleanUpChildren(node);
21106             // inserts everything just before this node...
21107             while (node.childNodes.length) {
21108                 var cn = node.childNodes[0];
21109                 node.removeChild(cn);
21110                 node.parentNode.insertBefore(cn, node);
21111             }
21112             node.parentNode.removeChild(node);
21113             return;
21114         }
21115         
21116         if (!node.attributes || !node.attributes.length) {
21117             this.cleanUpChildren(node);
21118             return;
21119         }
21120         
21121         function cleanAttr(n,v)
21122         {
21123             
21124             if (v.match(/^\./) || v.match(/^\//)) {
21125                 return;
21126             }
21127             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21128                 return;
21129             }
21130             if (v.match(/^#/)) {
21131                 return;
21132             }
21133 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21134             node.removeAttribute(n);
21135             
21136         }
21137         
21138         var cwhite = this.cwhite;
21139         var cblack = this.cblack;
21140             
21141         function cleanStyle(n,v)
21142         {
21143             if (v.match(/expression/)) { //XSS?? should we even bother..
21144                 node.removeAttribute(n);
21145                 return;
21146             }
21147             
21148             var parts = v.split(/;/);
21149             var clean = [];
21150             
21151             Roo.each(parts, function(p) {
21152                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21153                 if (!p.length) {
21154                     return true;
21155                 }
21156                 var l = p.split(':').shift().replace(/\s+/g,'');
21157                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21158                 
21159                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21160 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21161                     //node.removeAttribute(n);
21162                     return true;
21163                 }
21164                 //Roo.log()
21165                 // only allow 'c whitelisted system attributes'
21166                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21167 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21168                     //node.removeAttribute(n);
21169                     return true;
21170                 }
21171                 
21172                 
21173                  
21174                 
21175                 clean.push(p);
21176                 return true;
21177             });
21178             if (clean.length) { 
21179                 node.setAttribute(n, clean.join(';'));
21180             } else {
21181                 node.removeAttribute(n);
21182             }
21183             
21184         }
21185         
21186         
21187         for (var i = node.attributes.length-1; i > -1 ; i--) {
21188             var a = node.attributes[i];
21189             //console.log(a);
21190             
21191             if (a.name.toLowerCase().substr(0,2)=='on')  {
21192                 node.removeAttribute(a.name);
21193                 continue;
21194             }
21195             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21196                 node.removeAttribute(a.name);
21197                 continue;
21198             }
21199             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21200                 cleanAttr(a.name,a.value); // fixme..
21201                 continue;
21202             }
21203             if (a.name == 'style') {
21204                 cleanStyle(a.name,a.value);
21205                 continue;
21206             }
21207             /// clean up MS crap..
21208             // tecnically this should be a list of valid class'es..
21209             
21210             
21211             if (a.name == 'class') {
21212                 if (a.value.match(/^Mso/)) {
21213                     node.className = '';
21214                 }
21215                 
21216                 if (a.value.match(/body/)) {
21217                     node.className = '';
21218                 }
21219                 continue;
21220             }
21221             
21222             // style cleanup!?
21223             // class cleanup?
21224             
21225         }
21226         
21227         
21228         this.cleanUpChildren(node);
21229         
21230         
21231     },
21232     
21233     /**
21234      * Clean up MS wordisms...
21235      */
21236     cleanWord : function(node)
21237     {
21238         
21239         
21240         if (!node) {
21241             this.cleanWord(this.doc.body);
21242             return;
21243         }
21244         if (node.nodeName == "#text") {
21245             // clean up silly Windows -- stuff?
21246             return; 
21247         }
21248         if (node.nodeName == "#comment") {
21249             node.parentNode.removeChild(node);
21250             // clean up silly Windows -- stuff?
21251             return; 
21252         }
21253         
21254         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21255             node.parentNode.removeChild(node);
21256             return;
21257         }
21258         
21259         // remove - but keep children..
21260         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21261             while (node.childNodes.length) {
21262                 var cn = node.childNodes[0];
21263                 node.removeChild(cn);
21264                 node.parentNode.insertBefore(cn, node);
21265             }
21266             node.parentNode.removeChild(node);
21267             this.iterateChildren(node, this.cleanWord);
21268             return;
21269         }
21270         // clean styles
21271         if (node.className.length) {
21272             
21273             var cn = node.className.split(/\W+/);
21274             var cna = [];
21275             Roo.each(cn, function(cls) {
21276                 if (cls.match(/Mso[a-zA-Z]+/)) {
21277                     return;
21278                 }
21279                 cna.push(cls);
21280             });
21281             node.className = cna.length ? cna.join(' ') : '';
21282             if (!cna.length) {
21283                 node.removeAttribute("class");
21284             }
21285         }
21286         
21287         if (node.hasAttribute("lang")) {
21288             node.removeAttribute("lang");
21289         }
21290         
21291         if (node.hasAttribute("style")) {
21292             
21293             var styles = node.getAttribute("style").split(";");
21294             var nstyle = [];
21295             Roo.each(styles, function(s) {
21296                 if (!s.match(/:/)) {
21297                     return;
21298                 }
21299                 var kv = s.split(":");
21300                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21301                     return;
21302                 }
21303                 // what ever is left... we allow.
21304                 nstyle.push(s);
21305             });
21306             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21307             if (!nstyle.length) {
21308                 node.removeAttribute('style');
21309             }
21310         }
21311         this.iterateChildren(node, this.cleanWord);
21312         
21313         
21314         
21315     },
21316     /**
21317      * iterateChildren of a Node, calling fn each time, using this as the scole..
21318      * @param {DomNode} node node to iterate children of.
21319      * @param {Function} fn method of this class to call on each item.
21320      */
21321     iterateChildren : function(node, fn)
21322     {
21323         if (!node.childNodes.length) {
21324                 return;
21325         }
21326         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21327            fn.call(this, node.childNodes[i])
21328         }
21329     },
21330     
21331     
21332     /**
21333      * cleanTableWidths.
21334      *
21335      * Quite often pasting from word etc.. results in tables with column and widths.
21336      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21337      *
21338      */
21339     cleanTableWidths : function(node)
21340     {
21341          
21342          
21343         if (!node) {
21344             this.cleanTableWidths(this.doc.body);
21345             return;
21346         }
21347         
21348         // ignore list...
21349         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21350             return; 
21351         }
21352         Roo.log(node.tagName);
21353         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21354             this.iterateChildren(node, this.cleanTableWidths);
21355             return;
21356         }
21357         if (node.hasAttribute('width')) {
21358             node.removeAttribute('width');
21359         }
21360         
21361          
21362         if (node.hasAttribute("style")) {
21363             // pretty basic...
21364             
21365             var styles = node.getAttribute("style").split(";");
21366             var nstyle = [];
21367             Roo.each(styles, function(s) {
21368                 if (!s.match(/:/)) {
21369                     return;
21370                 }
21371                 var kv = s.split(":");
21372                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21373                     return;
21374                 }
21375                 // what ever is left... we allow.
21376                 nstyle.push(s);
21377             });
21378             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21379             if (!nstyle.length) {
21380                 node.removeAttribute('style');
21381             }
21382         }
21383         
21384         this.iterateChildren(node, this.cleanTableWidths);
21385         
21386         
21387     },
21388     
21389     
21390     
21391     
21392     domToHTML : function(currentElement, depth, nopadtext) {
21393         
21394         depth = depth || 0;
21395         nopadtext = nopadtext || false;
21396     
21397         if (!currentElement) {
21398             return this.domToHTML(this.doc.body);
21399         }
21400         
21401         //Roo.log(currentElement);
21402         var j;
21403         var allText = false;
21404         var nodeName = currentElement.nodeName;
21405         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21406         
21407         if  (nodeName == '#text') {
21408             
21409             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21410         }
21411         
21412         
21413         var ret = '';
21414         if (nodeName != 'BODY') {
21415              
21416             var i = 0;
21417             // Prints the node tagName, such as <A>, <IMG>, etc
21418             if (tagName) {
21419                 var attr = [];
21420                 for(i = 0; i < currentElement.attributes.length;i++) {
21421                     // quoting?
21422                     var aname = currentElement.attributes.item(i).name;
21423                     if (!currentElement.attributes.item(i).value.length) {
21424                         continue;
21425                     }
21426                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21427                 }
21428                 
21429                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21430             } 
21431             else {
21432                 
21433                 // eack
21434             }
21435         } else {
21436             tagName = false;
21437         }
21438         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21439             return ret;
21440         }
21441         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21442             nopadtext = true;
21443         }
21444         
21445         
21446         // Traverse the tree
21447         i = 0;
21448         var currentElementChild = currentElement.childNodes.item(i);
21449         var allText = true;
21450         var innerHTML  = '';
21451         lastnode = '';
21452         while (currentElementChild) {
21453             // Formatting code (indent the tree so it looks nice on the screen)
21454             var nopad = nopadtext;
21455             if (lastnode == 'SPAN') {
21456                 nopad  = true;
21457             }
21458             // text
21459             if  (currentElementChild.nodeName == '#text') {
21460                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21461                 toadd = nopadtext ? toadd : toadd.trim();
21462                 if (!nopad && toadd.length > 80) {
21463                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21464                 }
21465                 innerHTML  += toadd;
21466                 
21467                 i++;
21468                 currentElementChild = currentElement.childNodes.item(i);
21469                 lastNode = '';
21470                 continue;
21471             }
21472             allText = false;
21473             
21474             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21475                 
21476             // Recursively traverse the tree structure of the child node
21477             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21478             lastnode = currentElementChild.nodeName;
21479             i++;
21480             currentElementChild=currentElement.childNodes.item(i);
21481         }
21482         
21483         ret += innerHTML;
21484         
21485         if (!allText) {
21486                 // The remaining code is mostly for formatting the tree
21487             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21488         }
21489         
21490         
21491         if (tagName) {
21492             ret+= "</"+tagName+">";
21493         }
21494         return ret;
21495         
21496     },
21497         
21498     applyBlacklists : function()
21499     {
21500         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21501         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21502         
21503         this.white = [];
21504         this.black = [];
21505         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21506             if (b.indexOf(tag) > -1) {
21507                 return;
21508             }
21509             this.white.push(tag);
21510             
21511         }, this);
21512         
21513         Roo.each(w, function(tag) {
21514             if (b.indexOf(tag) > -1) {
21515                 return;
21516             }
21517             if (this.white.indexOf(tag) > -1) {
21518                 return;
21519             }
21520             this.white.push(tag);
21521             
21522         }, this);
21523         
21524         
21525         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21526             if (w.indexOf(tag) > -1) {
21527                 return;
21528             }
21529             this.black.push(tag);
21530             
21531         }, this);
21532         
21533         Roo.each(b, function(tag) {
21534             if (w.indexOf(tag) > -1) {
21535                 return;
21536             }
21537             if (this.black.indexOf(tag) > -1) {
21538                 return;
21539             }
21540             this.black.push(tag);
21541             
21542         }, this);
21543         
21544         
21545         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21546         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21547         
21548         this.cwhite = [];
21549         this.cblack = [];
21550         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21551             if (b.indexOf(tag) > -1) {
21552                 return;
21553             }
21554             this.cwhite.push(tag);
21555             
21556         }, this);
21557         
21558         Roo.each(w, function(tag) {
21559             if (b.indexOf(tag) > -1) {
21560                 return;
21561             }
21562             if (this.cwhite.indexOf(tag) > -1) {
21563                 return;
21564             }
21565             this.cwhite.push(tag);
21566             
21567         }, this);
21568         
21569         
21570         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21571             if (w.indexOf(tag) > -1) {
21572                 return;
21573             }
21574             this.cblack.push(tag);
21575             
21576         }, this);
21577         
21578         Roo.each(b, function(tag) {
21579             if (w.indexOf(tag) > -1) {
21580                 return;
21581             }
21582             if (this.cblack.indexOf(tag) > -1) {
21583                 return;
21584             }
21585             this.cblack.push(tag);
21586             
21587         }, this);
21588     },
21589     
21590     setStylesheets : function(stylesheets)
21591     {
21592         if(typeof(stylesheets) == 'string'){
21593             Roo.get(this.iframe.contentDocument.head).createChild({
21594                 tag : 'link',
21595                 rel : 'stylesheet',
21596                 type : 'text/css',
21597                 href : stylesheets
21598             });
21599             
21600             return;
21601         }
21602         var _this = this;
21603      
21604         Roo.each(stylesheets, function(s) {
21605             if(!s.length){
21606                 return;
21607             }
21608             
21609             Roo.get(_this.iframe.contentDocument.head).createChild({
21610                 tag : 'link',
21611                 rel : 'stylesheet',
21612                 type : 'text/css',
21613                 href : s
21614             });
21615         });
21616
21617         
21618     },
21619     
21620     removeStylesheets : function()
21621     {
21622         var _this = this;
21623         
21624         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21625             s.remove();
21626         });
21627     }
21628     
21629     // hide stuff that is not compatible
21630     /**
21631      * @event blur
21632      * @hide
21633      */
21634     /**
21635      * @event change
21636      * @hide
21637      */
21638     /**
21639      * @event focus
21640      * @hide
21641      */
21642     /**
21643      * @event specialkey
21644      * @hide
21645      */
21646     /**
21647      * @cfg {String} fieldClass @hide
21648      */
21649     /**
21650      * @cfg {String} focusClass @hide
21651      */
21652     /**
21653      * @cfg {String} autoCreate @hide
21654      */
21655     /**
21656      * @cfg {String} inputType @hide
21657      */
21658     /**
21659      * @cfg {String} invalidClass @hide
21660      */
21661     /**
21662      * @cfg {String} invalidText @hide
21663      */
21664     /**
21665      * @cfg {String} msgFx @hide
21666      */
21667     /**
21668      * @cfg {String} validateOnBlur @hide
21669      */
21670 });
21671
21672 Roo.HtmlEditorCore.white = [
21673         'area', 'br', 'img', 'input', 'hr', 'wbr',
21674         
21675        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21676        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21677        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21678        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21679        'table',   'ul',         'xmp', 
21680        
21681        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21682       'thead',   'tr', 
21683      
21684       'dir', 'menu', 'ol', 'ul', 'dl',
21685        
21686       'embed',  'object'
21687 ];
21688
21689
21690 Roo.HtmlEditorCore.black = [
21691     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21692         'applet', // 
21693         'base',   'basefont', 'bgsound', 'blink',  'body', 
21694         'frame',  'frameset', 'head',    'html',   'ilayer', 
21695         'iframe', 'layer',  'link',     'meta',    'object',   
21696         'script', 'style' ,'title',  'xml' // clean later..
21697 ];
21698 Roo.HtmlEditorCore.clean = [
21699     'script', 'style', 'title', 'xml'
21700 ];
21701 Roo.HtmlEditorCore.remove = [
21702     'font'
21703 ];
21704 // attributes..
21705
21706 Roo.HtmlEditorCore.ablack = [
21707     'on'
21708 ];
21709     
21710 Roo.HtmlEditorCore.aclean = [ 
21711     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21712 ];
21713
21714 // protocols..
21715 Roo.HtmlEditorCore.pwhite= [
21716         'http',  'https',  'mailto'
21717 ];
21718
21719 // white listed style attributes.
21720 Roo.HtmlEditorCore.cwhite= [
21721       //  'text-align', /// default is to allow most things..
21722       
21723          
21724 //        'font-size'//??
21725 ];
21726
21727 // black listed style attributes.
21728 Roo.HtmlEditorCore.cblack= [
21729       //  'font-size' -- this can be set by the project 
21730 ];
21731
21732
21733 Roo.HtmlEditorCore.swapCodes   =[ 
21734     [    8211, "--" ], 
21735     [    8212, "--" ], 
21736     [    8216,  "'" ],  
21737     [    8217, "'" ],  
21738     [    8220, '"' ],  
21739     [    8221, '"' ],  
21740     [    8226, "*" ],  
21741     [    8230, "..." ]
21742 ]; 
21743
21744     /*
21745  * - LGPL
21746  *
21747  * HtmlEditor
21748  * 
21749  */
21750
21751 /**
21752  * @class Roo.bootstrap.HtmlEditor
21753  * @extends Roo.bootstrap.TextArea
21754  * Bootstrap HtmlEditor class
21755
21756  * @constructor
21757  * Create a new HtmlEditor
21758  * @param {Object} config The config object
21759  */
21760
21761 Roo.bootstrap.HtmlEditor = function(config){
21762     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21763     if (!this.toolbars) {
21764         this.toolbars = [];
21765     }
21766     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21767     this.addEvents({
21768             /**
21769              * @event initialize
21770              * Fires when the editor is fully initialized (including the iframe)
21771              * @param {HtmlEditor} this
21772              */
21773             initialize: true,
21774             /**
21775              * @event activate
21776              * Fires when the editor is first receives the focus. Any insertion must wait
21777              * until after this event.
21778              * @param {HtmlEditor} this
21779              */
21780             activate: true,
21781              /**
21782              * @event beforesync
21783              * Fires before the textarea is updated with content from the editor iframe. Return false
21784              * to cancel the sync.
21785              * @param {HtmlEditor} this
21786              * @param {String} html
21787              */
21788             beforesync: true,
21789              /**
21790              * @event beforepush
21791              * Fires before the iframe editor is updated with content from the textarea. Return false
21792              * to cancel the push.
21793              * @param {HtmlEditor} this
21794              * @param {String} html
21795              */
21796             beforepush: true,
21797              /**
21798              * @event sync
21799              * Fires when the textarea is updated with content from the editor iframe.
21800              * @param {HtmlEditor} this
21801              * @param {String} html
21802              */
21803             sync: true,
21804              /**
21805              * @event push
21806              * Fires when the iframe editor is updated with content from the textarea.
21807              * @param {HtmlEditor} this
21808              * @param {String} html
21809              */
21810             push: true,
21811              /**
21812              * @event editmodechange
21813              * Fires when the editor switches edit modes
21814              * @param {HtmlEditor} this
21815              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21816              */
21817             editmodechange: true,
21818             /**
21819              * @event editorevent
21820              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21821              * @param {HtmlEditor} this
21822              */
21823             editorevent: true,
21824             /**
21825              * @event firstfocus
21826              * Fires when on first focus - needed by toolbars..
21827              * @param {HtmlEditor} this
21828              */
21829             firstfocus: true,
21830             /**
21831              * @event autosave
21832              * Auto save the htmlEditor value as a file into Events
21833              * @param {HtmlEditor} this
21834              */
21835             autosave: true,
21836             /**
21837              * @event savedpreview
21838              * preview the saved version of htmlEditor
21839              * @param {HtmlEditor} this
21840              */
21841             savedpreview: true
21842         });
21843 };
21844
21845
21846 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21847     
21848     
21849       /**
21850      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21851      */
21852     toolbars : false,
21853    
21854      /**
21855      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21856      *                        Roo.resizable.
21857      */
21858     resizable : false,
21859      /**
21860      * @cfg {Number} height (in pixels)
21861      */   
21862     height: 300,
21863    /**
21864      * @cfg {Number} width (in pixels)
21865      */   
21866     width: false,
21867     
21868     /**
21869      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21870      * 
21871      */
21872     stylesheets: false,
21873     
21874     // id of frame..
21875     frameId: false,
21876     
21877     // private properties
21878     validationEvent : false,
21879     deferHeight: true,
21880     initialized : false,
21881     activated : false,
21882     
21883     onFocus : Roo.emptyFn,
21884     iframePad:3,
21885     hideMode:'offsets',
21886     
21887     
21888     tbContainer : false,
21889     
21890     toolbarContainer :function() {
21891         return this.wrap.select('.x-html-editor-tb',true).first();
21892     },
21893
21894     /**
21895      * Protected method that will not generally be called directly. It
21896      * is called when the editor creates its toolbar. Override this method if you need to
21897      * add custom toolbar buttons.
21898      * @param {HtmlEditor} editor
21899      */
21900     createToolbar : function(){
21901         
21902         Roo.log("create toolbars");
21903         
21904         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21905         this.toolbars[0].render(this.toolbarContainer());
21906         
21907         return;
21908         
21909 //        if (!editor.toolbars || !editor.toolbars.length) {
21910 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21911 //        }
21912 //        
21913 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21914 //            editor.toolbars[i] = Roo.factory(
21915 //                    typeof(editor.toolbars[i]) == 'string' ?
21916 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21917 //                Roo.bootstrap.HtmlEditor);
21918 //            editor.toolbars[i].init(editor);
21919 //        }
21920     },
21921
21922      
21923     // private
21924     onRender : function(ct, position)
21925     {
21926        // Roo.log("Call onRender: " + this.xtype);
21927         var _t = this;
21928         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21929       
21930         this.wrap = this.inputEl().wrap({
21931             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21932         });
21933         
21934         this.editorcore.onRender(ct, position);
21935          
21936         if (this.resizable) {
21937             this.resizeEl = new Roo.Resizable(this.wrap, {
21938                 pinned : true,
21939                 wrap: true,
21940                 dynamic : true,
21941                 minHeight : this.height,
21942                 height: this.height,
21943                 handles : this.resizable,
21944                 width: this.width,
21945                 listeners : {
21946                     resize : function(r, w, h) {
21947                         _t.onResize(w,h); // -something
21948                     }
21949                 }
21950             });
21951             
21952         }
21953         this.createToolbar(this);
21954        
21955         
21956         if(!this.width && this.resizable){
21957             this.setSize(this.wrap.getSize());
21958         }
21959         if (this.resizeEl) {
21960             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21961             // should trigger onReize..
21962         }
21963         
21964     },
21965
21966     // private
21967     onResize : function(w, h)
21968     {
21969         Roo.log('resize: ' +w + ',' + h );
21970         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21971         var ew = false;
21972         var eh = false;
21973         
21974         if(this.inputEl() ){
21975             if(typeof w == 'number'){
21976                 var aw = w - this.wrap.getFrameWidth('lr');
21977                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21978                 ew = aw;
21979             }
21980             if(typeof h == 'number'){
21981                  var tbh = -11;  // fixme it needs to tool bar size!
21982                 for (var i =0; i < this.toolbars.length;i++) {
21983                     // fixme - ask toolbars for heights?
21984                     tbh += this.toolbars[i].el.getHeight();
21985                     //if (this.toolbars[i].footer) {
21986                     //    tbh += this.toolbars[i].footer.el.getHeight();
21987                     //}
21988                 }
21989               
21990                 
21991                 
21992                 
21993                 
21994                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21995                 ah -= 5; // knock a few pixes off for look..
21996                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21997                 var eh = ah;
21998             }
21999         }
22000         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22001         this.editorcore.onResize(ew,eh);
22002         
22003     },
22004
22005     /**
22006      * Toggles the editor between standard and source edit mode.
22007      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22008      */
22009     toggleSourceEdit : function(sourceEditMode)
22010     {
22011         this.editorcore.toggleSourceEdit(sourceEditMode);
22012         
22013         if(this.editorcore.sourceEditMode){
22014             Roo.log('editor - showing textarea');
22015             
22016 //            Roo.log('in');
22017 //            Roo.log(this.syncValue());
22018             this.syncValue();
22019             this.inputEl().removeClass(['hide', 'x-hidden']);
22020             this.inputEl().dom.removeAttribute('tabIndex');
22021             this.inputEl().focus();
22022         }else{
22023             Roo.log('editor - hiding textarea');
22024 //            Roo.log('out')
22025 //            Roo.log(this.pushValue()); 
22026             this.pushValue();
22027             
22028             this.inputEl().addClass(['hide', 'x-hidden']);
22029             this.inputEl().dom.setAttribute('tabIndex', -1);
22030             //this.deferFocus();
22031         }
22032          
22033         if(this.resizable){
22034             this.setSize(this.wrap.getSize());
22035         }
22036         
22037         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22038     },
22039  
22040     // private (for BoxComponent)
22041     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22042
22043     // private (for BoxComponent)
22044     getResizeEl : function(){
22045         return this.wrap;
22046     },
22047
22048     // private (for BoxComponent)
22049     getPositionEl : function(){
22050         return this.wrap;
22051     },
22052
22053     // private
22054     initEvents : function(){
22055         this.originalValue = this.getValue();
22056     },
22057
22058 //    /**
22059 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22060 //     * @method
22061 //     */
22062 //    markInvalid : Roo.emptyFn,
22063 //    /**
22064 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22065 //     * @method
22066 //     */
22067 //    clearInvalid : Roo.emptyFn,
22068
22069     setValue : function(v){
22070         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22071         this.editorcore.pushValue();
22072     },
22073
22074      
22075     // private
22076     deferFocus : function(){
22077         this.focus.defer(10, this);
22078     },
22079
22080     // doc'ed in Field
22081     focus : function(){
22082         this.editorcore.focus();
22083         
22084     },
22085       
22086
22087     // private
22088     onDestroy : function(){
22089         
22090         
22091         
22092         if(this.rendered){
22093             
22094             for (var i =0; i < this.toolbars.length;i++) {
22095                 // fixme - ask toolbars for heights?
22096                 this.toolbars[i].onDestroy();
22097             }
22098             
22099             this.wrap.dom.innerHTML = '';
22100             this.wrap.remove();
22101         }
22102     },
22103
22104     // private
22105     onFirstFocus : function(){
22106         //Roo.log("onFirstFocus");
22107         this.editorcore.onFirstFocus();
22108          for (var i =0; i < this.toolbars.length;i++) {
22109             this.toolbars[i].onFirstFocus();
22110         }
22111         
22112     },
22113     
22114     // private
22115     syncValue : function()
22116     {   
22117         this.editorcore.syncValue();
22118     },
22119     
22120     pushValue : function()
22121     {   
22122         this.editorcore.pushValue();
22123     }
22124      
22125     
22126     // hide stuff that is not compatible
22127     /**
22128      * @event blur
22129      * @hide
22130      */
22131     /**
22132      * @event change
22133      * @hide
22134      */
22135     /**
22136      * @event focus
22137      * @hide
22138      */
22139     /**
22140      * @event specialkey
22141      * @hide
22142      */
22143     /**
22144      * @cfg {String} fieldClass @hide
22145      */
22146     /**
22147      * @cfg {String} focusClass @hide
22148      */
22149     /**
22150      * @cfg {String} autoCreate @hide
22151      */
22152     /**
22153      * @cfg {String} inputType @hide
22154      */
22155     /**
22156      * @cfg {String} invalidClass @hide
22157      */
22158     /**
22159      * @cfg {String} invalidText @hide
22160      */
22161     /**
22162      * @cfg {String} msgFx @hide
22163      */
22164     /**
22165      * @cfg {String} validateOnBlur @hide
22166      */
22167 });
22168  
22169     
22170    
22171    
22172    
22173       
22174 Roo.namespace('Roo.bootstrap.htmleditor');
22175 /**
22176  * @class Roo.bootstrap.HtmlEditorToolbar1
22177  * Basic Toolbar
22178  * 
22179  * Usage:
22180  *
22181  new Roo.bootstrap.HtmlEditor({
22182     ....
22183     toolbars : [
22184         new Roo.bootstrap.HtmlEditorToolbar1({
22185             disable : { fonts: 1 , format: 1, ..., ... , ...],
22186             btns : [ .... ]
22187         })
22188     }
22189      
22190  * 
22191  * @cfg {Object} disable List of elements to disable..
22192  * @cfg {Array} btns List of additional buttons.
22193  * 
22194  * 
22195  * NEEDS Extra CSS? 
22196  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22197  */
22198  
22199 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22200 {
22201     
22202     Roo.apply(this, config);
22203     
22204     // default disabled, based on 'good practice'..
22205     this.disable = this.disable || {};
22206     Roo.applyIf(this.disable, {
22207         fontSize : true,
22208         colors : true,
22209         specialElements : true
22210     });
22211     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22212     
22213     this.editor = config.editor;
22214     this.editorcore = config.editor.editorcore;
22215     
22216     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22217     
22218     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22219     // dont call parent... till later.
22220 }
22221 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22222      
22223     bar : true,
22224     
22225     editor : false,
22226     editorcore : false,
22227     
22228     
22229     formats : [
22230         "p" ,  
22231         "h1","h2","h3","h4","h5","h6", 
22232         "pre", "code", 
22233         "abbr", "acronym", "address", "cite", "samp", "var",
22234         'div','span'
22235     ],
22236     
22237     onRender : function(ct, position)
22238     {
22239        // Roo.log("Call onRender: " + this.xtype);
22240         
22241        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22242        Roo.log(this.el);
22243        this.el.dom.style.marginBottom = '0';
22244        var _this = this;
22245        var editorcore = this.editorcore;
22246        var editor= this.editor;
22247        
22248        var children = [];
22249        var btn = function(id,cmd , toggle, handler){
22250        
22251             var  event = toggle ? 'toggle' : 'click';
22252        
22253             var a = {
22254                 size : 'sm',
22255                 xtype: 'Button',
22256                 xns: Roo.bootstrap,
22257                 glyphicon : id,
22258                 cmd : id || cmd,
22259                 enableToggle:toggle !== false,
22260                 //html : 'submit'
22261                 pressed : toggle ? false : null,
22262                 listeners : {}
22263             };
22264             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22265                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22266             };
22267             children.push(a);
22268             return a;
22269        }
22270         
22271         var style = {
22272                 xtype: 'Button',
22273                 size : 'sm',
22274                 xns: Roo.bootstrap,
22275                 glyphicon : 'font',
22276                 //html : 'submit'
22277                 menu : {
22278                     xtype: 'Menu',
22279                     xns: Roo.bootstrap,
22280                     items:  []
22281                 }
22282         };
22283         Roo.each(this.formats, function(f) {
22284             style.menu.items.push({
22285                 xtype :'MenuItem',
22286                 xns: Roo.bootstrap,
22287                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22288                 tagname : f,
22289                 listeners : {
22290                     click : function()
22291                     {
22292                         editorcore.insertTag(this.tagname);
22293                         editor.focus();
22294                     }
22295                 }
22296                 
22297             });
22298         });
22299          children.push(style);   
22300             
22301             
22302         btn('bold',false,true);
22303         btn('italic',false,true);
22304         btn('align-left', 'justifyleft',true);
22305         btn('align-center', 'justifycenter',true);
22306         btn('align-right' , 'justifyright',true);
22307         btn('link', false, false, function(btn) {
22308             //Roo.log("create link?");
22309             var url = prompt(this.createLinkText, this.defaultLinkValue);
22310             if(url && url != 'http:/'+'/'){
22311                 this.editorcore.relayCmd('createlink', url);
22312             }
22313         }),
22314         btn('list','insertunorderedlist',true);
22315         btn('pencil', false,true, function(btn){
22316                 Roo.log(this);
22317                 
22318                 this.toggleSourceEdit(btn.pressed);
22319         });
22320         /*
22321         var cog = {
22322                 xtype: 'Button',
22323                 size : 'sm',
22324                 xns: Roo.bootstrap,
22325                 glyphicon : 'cog',
22326                 //html : 'submit'
22327                 menu : {
22328                     xtype: 'Menu',
22329                     xns: Roo.bootstrap,
22330                     items:  []
22331                 }
22332         };
22333         
22334         cog.menu.items.push({
22335             xtype :'MenuItem',
22336             xns: Roo.bootstrap,
22337             html : Clean styles,
22338             tagname : f,
22339             listeners : {
22340                 click : function()
22341                 {
22342                     editorcore.insertTag(this.tagname);
22343                     editor.focus();
22344                 }
22345             }
22346             
22347         });
22348        */
22349         
22350          
22351        this.xtype = 'NavSimplebar';
22352         
22353         for(var i=0;i< children.length;i++) {
22354             
22355             this.buttons.add(this.addxtypeChild(children[i]));
22356             
22357         }
22358         
22359         editor.on('editorevent', this.updateToolbar, this);
22360     },
22361     onBtnClick : function(id)
22362     {
22363        this.editorcore.relayCmd(id);
22364        this.editorcore.focus();
22365     },
22366     
22367     /**
22368      * Protected method that will not generally be called directly. It triggers
22369      * a toolbar update by reading the markup state of the current selection in the editor.
22370      */
22371     updateToolbar: function(){
22372
22373         if(!this.editorcore.activated){
22374             this.editor.onFirstFocus(); // is this neeed?
22375             return;
22376         }
22377
22378         var btns = this.buttons; 
22379         var doc = this.editorcore.doc;
22380         btns.get('bold').setActive(doc.queryCommandState('bold'));
22381         btns.get('italic').setActive(doc.queryCommandState('italic'));
22382         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22383         
22384         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22385         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22386         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22387         
22388         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22389         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22390          /*
22391         
22392         var ans = this.editorcore.getAllAncestors();
22393         if (this.formatCombo) {
22394             
22395             
22396             var store = this.formatCombo.store;
22397             this.formatCombo.setValue("");
22398             for (var i =0; i < ans.length;i++) {
22399                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22400                     // select it..
22401                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22402                     break;
22403                 }
22404             }
22405         }
22406         
22407         
22408         
22409         // hides menus... - so this cant be on a menu...
22410         Roo.bootstrap.MenuMgr.hideAll();
22411         */
22412         Roo.bootstrap.MenuMgr.hideAll();
22413         //this.editorsyncValue();
22414     },
22415     onFirstFocus: function() {
22416         this.buttons.each(function(item){
22417            item.enable();
22418         });
22419     },
22420     toggleSourceEdit : function(sourceEditMode){
22421         
22422           
22423         if(sourceEditMode){
22424             Roo.log("disabling buttons");
22425            this.buttons.each( function(item){
22426                 if(item.cmd != 'pencil'){
22427                     item.disable();
22428                 }
22429             });
22430           
22431         }else{
22432             Roo.log("enabling buttons");
22433             if(this.editorcore.initialized){
22434                 this.buttons.each( function(item){
22435                     item.enable();
22436                 });
22437             }
22438             
22439         }
22440         Roo.log("calling toggole on editor");
22441         // tell the editor that it's been pressed..
22442         this.editor.toggleSourceEdit(sourceEditMode);
22443        
22444     }
22445 });
22446
22447
22448
22449
22450
22451 /**
22452  * @class Roo.bootstrap.Table.AbstractSelectionModel
22453  * @extends Roo.util.Observable
22454  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22455  * implemented by descendant classes.  This class should not be directly instantiated.
22456  * @constructor
22457  */
22458 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22459     this.locked = false;
22460     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22461 };
22462
22463
22464 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22465     /** @ignore Called by the grid automatically. Do not call directly. */
22466     init : function(grid){
22467         this.grid = grid;
22468         this.initEvents();
22469     },
22470
22471     /**
22472      * Locks the selections.
22473      */
22474     lock : function(){
22475         this.locked = true;
22476     },
22477
22478     /**
22479      * Unlocks the selections.
22480      */
22481     unlock : function(){
22482         this.locked = false;
22483     },
22484
22485     /**
22486      * Returns true if the selections are locked.
22487      * @return {Boolean}
22488      */
22489     isLocked : function(){
22490         return this.locked;
22491     }
22492 });
22493 /**
22494  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22495  * @class Roo.bootstrap.Table.RowSelectionModel
22496  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22497  * It supports multiple selections and keyboard selection/navigation. 
22498  * @constructor
22499  * @param {Object} config
22500  */
22501
22502 Roo.bootstrap.Table.RowSelectionModel = function(config){
22503     Roo.apply(this, config);
22504     this.selections = new Roo.util.MixedCollection(false, function(o){
22505         return o.id;
22506     });
22507
22508     this.last = false;
22509     this.lastActive = false;
22510
22511     this.addEvents({
22512         /**
22513              * @event selectionchange
22514              * Fires when the selection changes
22515              * @param {SelectionModel} this
22516              */
22517             "selectionchange" : true,
22518         /**
22519              * @event afterselectionchange
22520              * Fires after the selection changes (eg. by key press or clicking)
22521              * @param {SelectionModel} this
22522              */
22523             "afterselectionchange" : true,
22524         /**
22525              * @event beforerowselect
22526              * Fires when a row is selected being selected, return false to cancel.
22527              * @param {SelectionModel} this
22528              * @param {Number} rowIndex The selected index
22529              * @param {Boolean} keepExisting False if other selections will be cleared
22530              */
22531             "beforerowselect" : true,
22532         /**
22533              * @event rowselect
22534              * Fires when a row is selected.
22535              * @param {SelectionModel} this
22536              * @param {Number} rowIndex The selected index
22537              * @param {Roo.data.Record} r The record
22538              */
22539             "rowselect" : true,
22540         /**
22541              * @event rowdeselect
22542              * Fires when a row is deselected.
22543              * @param {SelectionModel} this
22544              * @param {Number} rowIndex The selected index
22545              */
22546         "rowdeselect" : true
22547     });
22548     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22549     this.locked = false;
22550  };
22551
22552 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22553     /**
22554      * @cfg {Boolean} singleSelect
22555      * True to allow selection of only one row at a time (defaults to false)
22556      */
22557     singleSelect : false,
22558
22559     // private
22560     initEvents : function()
22561     {
22562
22563         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22564         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22565         //}else{ // allow click to work like normal
22566          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22567         //}
22568         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22569         this.grid.on("rowclick", this.handleMouseDown, this);
22570         
22571         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22572             "up" : function(e){
22573                 if(!e.shiftKey){
22574                     this.selectPrevious(e.shiftKey);
22575                 }else if(this.last !== false && this.lastActive !== false){
22576                     var last = this.last;
22577                     this.selectRange(this.last,  this.lastActive-1);
22578                     this.grid.getView().focusRow(this.lastActive);
22579                     if(last !== false){
22580                         this.last = last;
22581                     }
22582                 }else{
22583                     this.selectFirstRow();
22584                 }
22585                 this.fireEvent("afterselectionchange", this);
22586             },
22587             "down" : function(e){
22588                 if(!e.shiftKey){
22589                     this.selectNext(e.shiftKey);
22590                 }else if(this.last !== false && this.lastActive !== false){
22591                     var last = this.last;
22592                     this.selectRange(this.last,  this.lastActive+1);
22593                     this.grid.getView().focusRow(this.lastActive);
22594                     if(last !== false){
22595                         this.last = last;
22596                     }
22597                 }else{
22598                     this.selectFirstRow();
22599                 }
22600                 this.fireEvent("afterselectionchange", this);
22601             },
22602             scope: this
22603         });
22604         this.grid.store.on('load', function(){
22605             this.selections.clear();
22606         },this);
22607         /*
22608         var view = this.grid.view;
22609         view.on("refresh", this.onRefresh, this);
22610         view.on("rowupdated", this.onRowUpdated, this);
22611         view.on("rowremoved", this.onRemove, this);
22612         */
22613     },
22614
22615     // private
22616     onRefresh : function()
22617     {
22618         var ds = this.grid.store, i, v = this.grid.view;
22619         var s = this.selections;
22620         s.each(function(r){
22621             if((i = ds.indexOfId(r.id)) != -1){
22622                 v.onRowSelect(i);
22623             }else{
22624                 s.remove(r);
22625             }
22626         });
22627     },
22628
22629     // private
22630     onRemove : function(v, index, r){
22631         this.selections.remove(r);
22632     },
22633
22634     // private
22635     onRowUpdated : function(v, index, r){
22636         if(this.isSelected(r)){
22637             v.onRowSelect(index);
22638         }
22639     },
22640
22641     /**
22642      * Select records.
22643      * @param {Array} records The records to select
22644      * @param {Boolean} keepExisting (optional) True to keep existing selections
22645      */
22646     selectRecords : function(records, keepExisting)
22647     {
22648         if(!keepExisting){
22649             this.clearSelections();
22650         }
22651             var ds = this.grid.store;
22652         for(var i = 0, len = records.length; i < len; i++){
22653             this.selectRow(ds.indexOf(records[i]), true);
22654         }
22655     },
22656
22657     /**
22658      * Gets the number of selected rows.
22659      * @return {Number}
22660      */
22661     getCount : function(){
22662         return this.selections.length;
22663     },
22664
22665     /**
22666      * Selects the first row in the grid.
22667      */
22668     selectFirstRow : function(){
22669         this.selectRow(0);
22670     },
22671
22672     /**
22673      * Select the last row.
22674      * @param {Boolean} keepExisting (optional) True to keep existing selections
22675      */
22676     selectLastRow : function(keepExisting){
22677         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22678         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22679     },
22680
22681     /**
22682      * Selects the row immediately following the last selected row.
22683      * @param {Boolean} keepExisting (optional) True to keep existing selections
22684      */
22685     selectNext : function(keepExisting)
22686     {
22687             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22688             this.selectRow(this.last+1, keepExisting);
22689             this.grid.getView().focusRow(this.last);
22690         }
22691     },
22692
22693     /**
22694      * Selects the row that precedes the last selected row.
22695      * @param {Boolean} keepExisting (optional) True to keep existing selections
22696      */
22697     selectPrevious : function(keepExisting){
22698         if(this.last){
22699             this.selectRow(this.last-1, keepExisting);
22700             this.grid.getView().focusRow(this.last);
22701         }
22702     },
22703
22704     /**
22705      * Returns the selected records
22706      * @return {Array} Array of selected records
22707      */
22708     getSelections : function(){
22709         return [].concat(this.selections.items);
22710     },
22711
22712     /**
22713      * Returns the first selected record.
22714      * @return {Record}
22715      */
22716     getSelected : function(){
22717         return this.selections.itemAt(0);
22718     },
22719
22720
22721     /**
22722      * Clears all selections.
22723      */
22724     clearSelections : function(fast)
22725     {
22726         if(this.locked) {
22727             return;
22728         }
22729         if(fast !== true){
22730                 var ds = this.grid.store;
22731             var s = this.selections;
22732             s.each(function(r){
22733                 this.deselectRow(ds.indexOfId(r.id));
22734             }, this);
22735             s.clear();
22736         }else{
22737             this.selections.clear();
22738         }
22739         this.last = false;
22740     },
22741
22742
22743     /**
22744      * Selects all rows.
22745      */
22746     selectAll : function(){
22747         if(this.locked) {
22748             return;
22749         }
22750         this.selections.clear();
22751         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22752             this.selectRow(i, true);
22753         }
22754     },
22755
22756     /**
22757      * Returns True if there is a selection.
22758      * @return {Boolean}
22759      */
22760     hasSelection : function(){
22761         return this.selections.length > 0;
22762     },
22763
22764     /**
22765      * Returns True if the specified row is selected.
22766      * @param {Number/Record} record The record or index of the record to check
22767      * @return {Boolean}
22768      */
22769     isSelected : function(index){
22770             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22771         return (r && this.selections.key(r.id) ? true : false);
22772     },
22773
22774     /**
22775      * Returns True if the specified record id is selected.
22776      * @param {String} id The id of record to check
22777      * @return {Boolean}
22778      */
22779     isIdSelected : function(id){
22780         return (this.selections.key(id) ? true : false);
22781     },
22782
22783
22784     // private
22785     handleMouseDBClick : function(e, t){
22786         
22787     },
22788     // private
22789     handleMouseDown : function(e, t)
22790     {
22791             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22792         if(this.isLocked() || rowIndex < 0 ){
22793             return;
22794         };
22795         if(e.shiftKey && this.last !== false){
22796             var last = this.last;
22797             this.selectRange(last, rowIndex, e.ctrlKey);
22798             this.last = last; // reset the last
22799             t.focus();
22800     
22801         }else{
22802             var isSelected = this.isSelected(rowIndex);
22803             //Roo.log("select row:" + rowIndex);
22804             if(isSelected){
22805                 this.deselectRow(rowIndex);
22806             } else {
22807                         this.selectRow(rowIndex, true);
22808             }
22809     
22810             /*
22811                 if(e.button !== 0 && isSelected){
22812                 alert('rowIndex 2: ' + rowIndex);
22813                     view.focusRow(rowIndex);
22814                 }else if(e.ctrlKey && isSelected){
22815                     this.deselectRow(rowIndex);
22816                 }else if(!isSelected){
22817                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22818                     view.focusRow(rowIndex);
22819                 }
22820             */
22821         }
22822         this.fireEvent("afterselectionchange", this);
22823     },
22824     // private
22825     handleDragableRowClick :  function(grid, rowIndex, e) 
22826     {
22827         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22828             this.selectRow(rowIndex, false);
22829             grid.view.focusRow(rowIndex);
22830              this.fireEvent("afterselectionchange", this);
22831         }
22832     },
22833     
22834     /**
22835      * Selects multiple rows.
22836      * @param {Array} rows Array of the indexes of the row to select
22837      * @param {Boolean} keepExisting (optional) True to keep existing selections
22838      */
22839     selectRows : function(rows, keepExisting){
22840         if(!keepExisting){
22841             this.clearSelections();
22842         }
22843         for(var i = 0, len = rows.length; i < len; i++){
22844             this.selectRow(rows[i], true);
22845         }
22846     },
22847
22848     /**
22849      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22850      * @param {Number} startRow The index of the first row in the range
22851      * @param {Number} endRow The index of the last row in the range
22852      * @param {Boolean} keepExisting (optional) True to retain existing selections
22853      */
22854     selectRange : function(startRow, endRow, keepExisting){
22855         if(this.locked) {
22856             return;
22857         }
22858         if(!keepExisting){
22859             this.clearSelections();
22860         }
22861         if(startRow <= endRow){
22862             for(var i = startRow; i <= endRow; i++){
22863                 this.selectRow(i, true);
22864             }
22865         }else{
22866             for(var i = startRow; i >= endRow; i--){
22867                 this.selectRow(i, true);
22868             }
22869         }
22870     },
22871
22872     /**
22873      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22874      * @param {Number} startRow The index of the first row in the range
22875      * @param {Number} endRow The index of the last row in the range
22876      */
22877     deselectRange : function(startRow, endRow, preventViewNotify){
22878         if(this.locked) {
22879             return;
22880         }
22881         for(var i = startRow; i <= endRow; i++){
22882             this.deselectRow(i, preventViewNotify);
22883         }
22884     },
22885
22886     /**
22887      * Selects a row.
22888      * @param {Number} row The index of the row to select
22889      * @param {Boolean} keepExisting (optional) True to keep existing selections
22890      */
22891     selectRow : function(index, keepExisting, preventViewNotify)
22892     {
22893             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
22894             return;
22895         }
22896         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22897             if(!keepExisting || this.singleSelect){
22898                 this.clearSelections();
22899             }
22900             
22901             var r = this.grid.store.getAt(index);
22902             //console.log('selectRow - record id :' + r.id);
22903             
22904             this.selections.add(r);
22905             this.last = this.lastActive = index;
22906             if(!preventViewNotify){
22907                 var proxy = new Roo.Element(
22908                                 this.grid.getRowDom(index)
22909                 );
22910                 proxy.addClass('bg-info info');
22911             }
22912             this.fireEvent("rowselect", this, index, r);
22913             this.fireEvent("selectionchange", this);
22914         }
22915     },
22916
22917     /**
22918      * Deselects a row.
22919      * @param {Number} row The index of the row to deselect
22920      */
22921     deselectRow : function(index, preventViewNotify)
22922     {
22923         if(this.locked) {
22924             return;
22925         }
22926         if(this.last == index){
22927             this.last = false;
22928         }
22929         if(this.lastActive == index){
22930             this.lastActive = false;
22931         }
22932         
22933         var r = this.grid.store.getAt(index);
22934         if (!r) {
22935             return;
22936         }
22937         
22938         this.selections.remove(r);
22939         //.console.log('deselectRow - record id :' + r.id);
22940         if(!preventViewNotify){
22941         
22942             var proxy = new Roo.Element(
22943                 this.grid.getRowDom(index)
22944             );
22945             proxy.removeClass('bg-info info');
22946         }
22947         this.fireEvent("rowdeselect", this, index);
22948         this.fireEvent("selectionchange", this);
22949     },
22950
22951     // private
22952     restoreLast : function(){
22953         if(this._last){
22954             this.last = this._last;
22955         }
22956     },
22957
22958     // private
22959     acceptsNav : function(row, col, cm){
22960         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22961     },
22962
22963     // private
22964     onEditorKey : function(field, e){
22965         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22966         if(k == e.TAB){
22967             e.stopEvent();
22968             ed.completeEdit();
22969             if(e.shiftKey){
22970                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22971             }else{
22972                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22973             }
22974         }else if(k == e.ENTER && !e.ctrlKey){
22975             e.stopEvent();
22976             ed.completeEdit();
22977             if(e.shiftKey){
22978                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22979             }else{
22980                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22981             }
22982         }else if(k == e.ESC){
22983             ed.cancelEdit();
22984         }
22985         if(newCell){
22986             g.startEditing(newCell[0], newCell[1]);
22987         }
22988     }
22989 });
22990 /*
22991  * Based on:
22992  * Ext JS Library 1.1.1
22993  * Copyright(c) 2006-2007, Ext JS, LLC.
22994  *
22995  * Originally Released Under LGPL - original licence link has changed is not relivant.
22996  *
22997  * Fork - LGPL
22998  * <script type="text/javascript">
22999  */
23000  
23001 /**
23002  * @class Roo.bootstrap.PagingToolbar
23003  * @extends Roo.bootstrap.NavSimplebar
23004  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23005  * @constructor
23006  * Create a new PagingToolbar
23007  * @param {Object} config The config object
23008  * @param {Roo.data.Store} store
23009  */
23010 Roo.bootstrap.PagingToolbar = function(config)
23011 {
23012     // old args format still supported... - xtype is prefered..
23013         // created from xtype...
23014     
23015     this.ds = config.dataSource;
23016     
23017     if (config.store && !this.ds) {
23018         this.store= Roo.factory(config.store, Roo.data);
23019         this.ds = this.store;
23020         this.ds.xmodule = this.xmodule || false;
23021     }
23022     
23023     this.toolbarItems = [];
23024     if (config.items) {
23025         this.toolbarItems = config.items;
23026     }
23027     
23028     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23029     
23030     this.cursor = 0;
23031     
23032     if (this.ds) { 
23033         this.bind(this.ds);
23034     }
23035     
23036     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23037     
23038 };
23039
23040 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23041     /**
23042      * @cfg {Roo.data.Store} dataSource
23043      * The underlying data store providing the paged data
23044      */
23045     /**
23046      * @cfg {String/HTMLElement/Element} container
23047      * container The id or element that will contain the toolbar
23048      */
23049     /**
23050      * @cfg {Boolean} displayInfo
23051      * True to display the displayMsg (defaults to false)
23052      */
23053     /**
23054      * @cfg {Number} pageSize
23055      * The number of records to display per page (defaults to 20)
23056      */
23057     pageSize: 20,
23058     /**
23059      * @cfg {String} displayMsg
23060      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23061      */
23062     displayMsg : 'Displaying {0} - {1} of {2}',
23063     /**
23064      * @cfg {String} emptyMsg
23065      * The message to display when no records are found (defaults to "No data to display")
23066      */
23067     emptyMsg : 'No data to display',
23068     /**
23069      * Customizable piece of the default paging text (defaults to "Page")
23070      * @type String
23071      */
23072     beforePageText : "Page",
23073     /**
23074      * Customizable piece of the default paging text (defaults to "of %0")
23075      * @type String
23076      */
23077     afterPageText : "of {0}",
23078     /**
23079      * Customizable piece of the default paging text (defaults to "First Page")
23080      * @type String
23081      */
23082     firstText : "First Page",
23083     /**
23084      * Customizable piece of the default paging text (defaults to "Previous Page")
23085      * @type String
23086      */
23087     prevText : "Previous Page",
23088     /**
23089      * Customizable piece of the default paging text (defaults to "Next Page")
23090      * @type String
23091      */
23092     nextText : "Next Page",
23093     /**
23094      * Customizable piece of the default paging text (defaults to "Last Page")
23095      * @type String
23096      */
23097     lastText : "Last Page",
23098     /**
23099      * Customizable piece of the default paging text (defaults to "Refresh")
23100      * @type String
23101      */
23102     refreshText : "Refresh",
23103
23104     buttons : false,
23105     // private
23106     onRender : function(ct, position) 
23107     {
23108         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23109         this.navgroup.parentId = this.id;
23110         this.navgroup.onRender(this.el, null);
23111         // add the buttons to the navgroup
23112         
23113         if(this.displayInfo){
23114             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23115             this.displayEl = this.el.select('.x-paging-info', true).first();
23116 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23117 //            this.displayEl = navel.el.select('span',true).first();
23118         }
23119         
23120         var _this = this;
23121         
23122         if(this.buttons){
23123             Roo.each(_this.buttons, function(e){ // this might need to use render????
23124                Roo.factory(e).onRender(_this.el, null);
23125             });
23126         }
23127             
23128         Roo.each(_this.toolbarItems, function(e) {
23129             _this.navgroup.addItem(e);
23130         });
23131         
23132         
23133         this.first = this.navgroup.addItem({
23134             tooltip: this.firstText,
23135             cls: "prev",
23136             icon : 'fa fa-backward',
23137             disabled: true,
23138             preventDefault: true,
23139             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23140         });
23141         
23142         this.prev =  this.navgroup.addItem({
23143             tooltip: this.prevText,
23144             cls: "prev",
23145             icon : 'fa fa-step-backward',
23146             disabled: true,
23147             preventDefault: true,
23148             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23149         });
23150     //this.addSeparator();
23151         
23152         
23153         var field = this.navgroup.addItem( {
23154             tagtype : 'span',
23155             cls : 'x-paging-position',
23156             
23157             html : this.beforePageText  +
23158                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23159                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23160          } ); //?? escaped?
23161         
23162         this.field = field.el.select('input', true).first();
23163         this.field.on("keydown", this.onPagingKeydown, this);
23164         this.field.on("focus", function(){this.dom.select();});
23165     
23166     
23167         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23168         //this.field.setHeight(18);
23169         //this.addSeparator();
23170         this.next = this.navgroup.addItem({
23171             tooltip: this.nextText,
23172             cls: "next",
23173             html : ' <i class="fa fa-step-forward">',
23174             disabled: true,
23175             preventDefault: true,
23176             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23177         });
23178         this.last = this.navgroup.addItem({
23179             tooltip: this.lastText,
23180             icon : 'fa fa-forward',
23181             cls: "next",
23182             disabled: true,
23183             preventDefault: true,
23184             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23185         });
23186     //this.addSeparator();
23187         this.loading = this.navgroup.addItem({
23188             tooltip: this.refreshText,
23189             icon: 'fa fa-refresh',
23190             preventDefault: true,
23191             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23192         });
23193         
23194     },
23195
23196     // private
23197     updateInfo : function(){
23198         if(this.displayEl){
23199             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23200             var msg = count == 0 ?
23201                 this.emptyMsg :
23202                 String.format(
23203                     this.displayMsg,
23204                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23205                 );
23206             this.displayEl.update(msg);
23207         }
23208     },
23209
23210     // private
23211     onLoad : function(ds, r, o){
23212        this.cursor = o.params ? o.params.start : 0;
23213        var d = this.getPageData(),
23214             ap = d.activePage,
23215             ps = d.pages;
23216         
23217        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23218        this.field.dom.value = ap;
23219        this.first.setDisabled(ap == 1);
23220        this.prev.setDisabled(ap == 1);
23221        this.next.setDisabled(ap == ps);
23222        this.last.setDisabled(ap == ps);
23223        this.loading.enable();
23224        this.updateInfo();
23225     },
23226
23227     // private
23228     getPageData : function(){
23229         var total = this.ds.getTotalCount();
23230         return {
23231             total : total,
23232             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23233             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23234         };
23235     },
23236
23237     // private
23238     onLoadError : function(){
23239         this.loading.enable();
23240     },
23241
23242     // private
23243     onPagingKeydown : function(e){
23244         var k = e.getKey();
23245         var d = this.getPageData();
23246         if(k == e.RETURN){
23247             var v = this.field.dom.value, pageNum;
23248             if(!v || isNaN(pageNum = parseInt(v, 10))){
23249                 this.field.dom.value = d.activePage;
23250                 return;
23251             }
23252             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23253             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23254             e.stopEvent();
23255         }
23256         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))
23257         {
23258           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23259           this.field.dom.value = pageNum;
23260           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23261           e.stopEvent();
23262         }
23263         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23264         {
23265           var v = this.field.dom.value, pageNum; 
23266           var increment = (e.shiftKey) ? 10 : 1;
23267           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23268                 increment *= -1;
23269           }
23270           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23271             this.field.dom.value = d.activePage;
23272             return;
23273           }
23274           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23275           {
23276             this.field.dom.value = parseInt(v, 10) + increment;
23277             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23278             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23279           }
23280           e.stopEvent();
23281         }
23282     },
23283
23284     // private
23285     beforeLoad : function(){
23286         if(this.loading){
23287             this.loading.disable();
23288         }
23289     },
23290
23291     // private
23292     onClick : function(which){
23293         
23294         var ds = this.ds;
23295         if (!ds) {
23296             return;
23297         }
23298         
23299         switch(which){
23300             case "first":
23301                 ds.load({params:{start: 0, limit: this.pageSize}});
23302             break;
23303             case "prev":
23304                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23305             break;
23306             case "next":
23307                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23308             break;
23309             case "last":
23310                 var total = ds.getTotalCount();
23311                 var extra = total % this.pageSize;
23312                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23313                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23314             break;
23315             case "refresh":
23316                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23317             break;
23318         }
23319     },
23320
23321     /**
23322      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23323      * @param {Roo.data.Store} store The data store to unbind
23324      */
23325     unbind : function(ds){
23326         ds.un("beforeload", this.beforeLoad, this);
23327         ds.un("load", this.onLoad, this);
23328         ds.un("loadexception", this.onLoadError, this);
23329         ds.un("remove", this.updateInfo, this);
23330         ds.un("add", this.updateInfo, this);
23331         this.ds = undefined;
23332     },
23333
23334     /**
23335      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23336      * @param {Roo.data.Store} store The data store to bind
23337      */
23338     bind : function(ds){
23339         ds.on("beforeload", this.beforeLoad, this);
23340         ds.on("load", this.onLoad, this);
23341         ds.on("loadexception", this.onLoadError, this);
23342         ds.on("remove", this.updateInfo, this);
23343         ds.on("add", this.updateInfo, this);
23344         this.ds = ds;
23345     }
23346 });/*
23347  * - LGPL
23348  *
23349  * element
23350  * 
23351  */
23352
23353 /**
23354  * @class Roo.bootstrap.MessageBar
23355  * @extends Roo.bootstrap.Component
23356  * Bootstrap MessageBar class
23357  * @cfg {String} html contents of the MessageBar
23358  * @cfg {String} weight (info | success | warning | danger) default info
23359  * @cfg {String} beforeClass insert the bar before the given class
23360  * @cfg {Boolean} closable (true | false) default false
23361  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23362  * 
23363  * @constructor
23364  * Create a new Element
23365  * @param {Object} config The config object
23366  */
23367
23368 Roo.bootstrap.MessageBar = function(config){
23369     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23370 };
23371
23372 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23373     
23374     html: '',
23375     weight: 'info',
23376     closable: false,
23377     fixed: false,
23378     beforeClass: 'bootstrap-sticky-wrap',
23379     
23380     getAutoCreate : function(){
23381         
23382         var cfg = {
23383             tag: 'div',
23384             cls: 'alert alert-dismissable alert-' + this.weight,
23385             cn: [
23386                 {
23387                     tag: 'span',
23388                     cls: 'message',
23389                     html: this.html || ''
23390                 }
23391             ]
23392         };
23393         
23394         if(this.fixed){
23395             cfg.cls += ' alert-messages-fixed';
23396         }
23397         
23398         if(this.closable){
23399             cfg.cn.push({
23400                 tag: 'button',
23401                 cls: 'close',
23402                 html: 'x'
23403             });
23404         }
23405         
23406         return cfg;
23407     },
23408     
23409     onRender : function(ct, position)
23410     {
23411         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23412         
23413         if(!this.el){
23414             var cfg = Roo.apply({},  this.getAutoCreate());
23415             cfg.id = Roo.id();
23416             
23417             if (this.cls) {
23418                 cfg.cls += ' ' + this.cls;
23419             }
23420             if (this.style) {
23421                 cfg.style = this.style;
23422             }
23423             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23424             
23425             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23426         }
23427         
23428         this.el.select('>button.close').on('click', this.hide, this);
23429         
23430     },
23431     
23432     show : function()
23433     {
23434         if (!this.rendered) {
23435             this.render();
23436         }
23437         
23438         this.el.show();
23439         
23440         this.fireEvent('show', this);
23441         
23442     },
23443     
23444     hide : function()
23445     {
23446         if (!this.rendered) {
23447             this.render();
23448         }
23449         
23450         this.el.hide();
23451         
23452         this.fireEvent('hide', this);
23453     },
23454     
23455     update : function()
23456     {
23457 //        var e = this.el.dom.firstChild;
23458 //        
23459 //        if(this.closable){
23460 //            e = e.nextSibling;
23461 //        }
23462 //        
23463 //        e.data = this.html || '';
23464
23465         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23466     }
23467    
23468 });
23469
23470  
23471
23472      /*
23473  * - LGPL
23474  *
23475  * Graph
23476  * 
23477  */
23478
23479
23480 /**
23481  * @class Roo.bootstrap.Graph
23482  * @extends Roo.bootstrap.Component
23483  * Bootstrap Graph class
23484 > Prameters
23485  -sm {number} sm 4
23486  -md {number} md 5
23487  @cfg {String} graphtype  bar | vbar | pie
23488  @cfg {number} g_x coodinator | centre x (pie)
23489  @cfg {number} g_y coodinator | centre y (pie)
23490  @cfg {number} g_r radius (pie)
23491  @cfg {number} g_height height of the chart (respected by all elements in the set)
23492  @cfg {number} g_width width of the chart (respected by all elements in the set)
23493  @cfg {Object} title The title of the chart
23494     
23495  -{Array}  values
23496  -opts (object) options for the chart 
23497      o {
23498      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23499      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23500      o vgutter (number)
23501      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.
23502      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23503      o to
23504      o stretch (boolean)
23505      o }
23506  -opts (object) options for the pie
23507      o{
23508      o cut
23509      o startAngle (number)
23510      o endAngle (number)
23511      } 
23512  *
23513  * @constructor
23514  * Create a new Input
23515  * @param {Object} config The config object
23516  */
23517
23518 Roo.bootstrap.Graph = function(config){
23519     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23520     
23521     this.addEvents({
23522         // img events
23523         /**
23524          * @event click
23525          * The img click event for the img.
23526          * @param {Roo.EventObject} e
23527          */
23528         "click" : true
23529     });
23530 };
23531
23532 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23533     
23534     sm: 4,
23535     md: 5,
23536     graphtype: 'bar',
23537     g_height: 250,
23538     g_width: 400,
23539     g_x: 50,
23540     g_y: 50,
23541     g_r: 30,
23542     opts:{
23543         //g_colors: this.colors,
23544         g_type: 'soft',
23545         g_gutter: '20%'
23546
23547     },
23548     title : false,
23549
23550     getAutoCreate : function(){
23551         
23552         var cfg = {
23553             tag: 'div',
23554             html : null
23555         };
23556         
23557         
23558         return  cfg;
23559     },
23560
23561     onRender : function(ct,position){
23562         
23563         
23564         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23565         
23566         if (typeof(Raphael) == 'undefined') {
23567             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23568             return;
23569         }
23570         
23571         this.raphael = Raphael(this.el.dom);
23572         
23573                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23574                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23575                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23576                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23577                 /*
23578                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23579                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23580                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23581                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23582                 
23583                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23584                 r.barchart(330, 10, 300, 220, data1);
23585                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23586                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23587                 */
23588                 
23589                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23590                 // r.barchart(30, 30, 560, 250,  xdata, {
23591                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23592                 //     axis : "0 0 1 1",
23593                 //     axisxlabels :  xdata
23594                 //     //yvalues : cols,
23595                    
23596                 // });
23597 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23598 //        
23599 //        this.load(null,xdata,{
23600 //                axis : "0 0 1 1",
23601 //                axisxlabels :  xdata
23602 //                });
23603
23604     },
23605
23606     load : function(graphtype,xdata,opts)
23607     {
23608         this.raphael.clear();
23609         if(!graphtype) {
23610             graphtype = this.graphtype;
23611         }
23612         if(!opts){
23613             opts = this.opts;
23614         }
23615         var r = this.raphael,
23616             fin = function () {
23617                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23618             },
23619             fout = function () {
23620                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23621             },
23622             pfin = function() {
23623                 this.sector.stop();
23624                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23625
23626                 if (this.label) {
23627                     this.label[0].stop();
23628                     this.label[0].attr({ r: 7.5 });
23629                     this.label[1].attr({ "font-weight": 800 });
23630                 }
23631             },
23632             pfout = function() {
23633                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23634
23635                 if (this.label) {
23636                     this.label[0].animate({ r: 5 }, 500, "bounce");
23637                     this.label[1].attr({ "font-weight": 400 });
23638                 }
23639             };
23640
23641         switch(graphtype){
23642             case 'bar':
23643                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23644                 break;
23645             case 'hbar':
23646                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23647                 break;
23648             case 'pie':
23649 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23650 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23651 //            
23652                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23653                 
23654                 break;
23655
23656         }
23657         
23658         if(this.title){
23659             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23660         }
23661         
23662     },
23663     
23664     setTitle: function(o)
23665     {
23666         this.title = o;
23667     },
23668     
23669     initEvents: function() {
23670         
23671         if(!this.href){
23672             this.el.on('click', this.onClick, this);
23673         }
23674     },
23675     
23676     onClick : function(e)
23677     {
23678         Roo.log('img onclick');
23679         this.fireEvent('click', this, e);
23680     }
23681    
23682 });
23683
23684  
23685 /*
23686  * - LGPL
23687  *
23688  * numberBox
23689  * 
23690  */
23691 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23692
23693 /**
23694  * @class Roo.bootstrap.dash.NumberBox
23695  * @extends Roo.bootstrap.Component
23696  * Bootstrap NumberBox class
23697  * @cfg {String} headline Box headline
23698  * @cfg {String} content Box content
23699  * @cfg {String} icon Box icon
23700  * @cfg {String} footer Footer text
23701  * @cfg {String} fhref Footer href
23702  * 
23703  * @constructor
23704  * Create a new NumberBox
23705  * @param {Object} config The config object
23706  */
23707
23708
23709 Roo.bootstrap.dash.NumberBox = function(config){
23710     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23711     
23712 };
23713
23714 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23715     
23716     headline : '',
23717     content : '',
23718     icon : '',
23719     footer : '',
23720     fhref : '',
23721     ficon : '',
23722     
23723     getAutoCreate : function(){
23724         
23725         var cfg = {
23726             tag : 'div',
23727             cls : 'small-box ',
23728             cn : [
23729                 {
23730                     tag : 'div',
23731                     cls : 'inner',
23732                     cn :[
23733                         {
23734                             tag : 'h3',
23735                             cls : 'roo-headline',
23736                             html : this.headline
23737                         },
23738                         {
23739                             tag : 'p',
23740                             cls : 'roo-content',
23741                             html : this.content
23742                         }
23743                     ]
23744                 }
23745             ]
23746         };
23747         
23748         if(this.icon){
23749             cfg.cn.push({
23750                 tag : 'div',
23751                 cls : 'icon',
23752                 cn :[
23753                     {
23754                         tag : 'i',
23755                         cls : 'ion ' + this.icon
23756                     }
23757                 ]
23758             });
23759         }
23760         
23761         if(this.footer){
23762             var footer = {
23763                 tag : 'a',
23764                 cls : 'small-box-footer',
23765                 href : this.fhref || '#',
23766                 html : this.footer
23767             };
23768             
23769             cfg.cn.push(footer);
23770             
23771         }
23772         
23773         return  cfg;
23774     },
23775
23776     onRender : function(ct,position){
23777         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23778
23779
23780        
23781                 
23782     },
23783
23784     setHeadline: function (value)
23785     {
23786         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23787     },
23788     
23789     setFooter: function (value, href)
23790     {
23791         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23792         
23793         if(href){
23794             this.el.select('a.small-box-footer',true).first().attr('href', href);
23795         }
23796         
23797     },
23798
23799     setContent: function (value)
23800     {
23801         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23802     },
23803
23804     initEvents: function() 
23805     {   
23806         
23807     }
23808     
23809 });
23810
23811  
23812 /*
23813  * - LGPL
23814  *
23815  * TabBox
23816  * 
23817  */
23818 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23819
23820 /**
23821  * @class Roo.bootstrap.dash.TabBox
23822  * @extends Roo.bootstrap.Component
23823  * Bootstrap TabBox class
23824  * @cfg {String} title Title of the TabBox
23825  * @cfg {String} icon Icon of the TabBox
23826  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23827  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23828  * 
23829  * @constructor
23830  * Create a new TabBox
23831  * @param {Object} config The config object
23832  */
23833
23834
23835 Roo.bootstrap.dash.TabBox = function(config){
23836     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23837     this.addEvents({
23838         // raw events
23839         /**
23840          * @event addpane
23841          * When a pane is added
23842          * @param {Roo.bootstrap.dash.TabPane} pane
23843          */
23844         "addpane" : true,
23845         /**
23846          * @event activatepane
23847          * When a pane is activated
23848          * @param {Roo.bootstrap.dash.TabPane} pane
23849          */
23850         "activatepane" : true
23851         
23852          
23853     });
23854     
23855     this.panes = [];
23856 };
23857
23858 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23859
23860     title : '',
23861     icon : false,
23862     showtabs : true,
23863     tabScrollable : false,
23864     
23865     getChildContainer : function()
23866     {
23867         return this.el.select('.tab-content', true).first();
23868     },
23869     
23870     getAutoCreate : function(){
23871         
23872         var header = {
23873             tag: 'li',
23874             cls: 'pull-left header',
23875             html: this.title,
23876             cn : []
23877         };
23878         
23879         if(this.icon){
23880             header.cn.push({
23881                 tag: 'i',
23882                 cls: 'fa ' + this.icon
23883             });
23884         }
23885         
23886         var h = {
23887             tag: 'ul',
23888             cls: 'nav nav-tabs pull-right',
23889             cn: [
23890                 header
23891             ]
23892         };
23893         
23894         if(this.tabScrollable){
23895             h = {
23896                 tag: 'div',
23897                 cls: 'tab-header',
23898                 cn: [
23899                     {
23900                         tag: 'ul',
23901                         cls: 'nav nav-tabs pull-right',
23902                         cn: [
23903                             header
23904                         ]
23905                     }
23906                 ]
23907             };
23908         }
23909         
23910         var cfg = {
23911             tag: 'div',
23912             cls: 'nav-tabs-custom',
23913             cn: [
23914                 h,
23915                 {
23916                     tag: 'div',
23917                     cls: 'tab-content no-padding',
23918                     cn: []
23919                 }
23920             ]
23921         };
23922
23923         return  cfg;
23924     },
23925     initEvents : function()
23926     {
23927         //Roo.log('add add pane handler');
23928         this.on('addpane', this.onAddPane, this);
23929     },
23930      /**
23931      * Updates the box title
23932      * @param {String} html to set the title to.
23933      */
23934     setTitle : function(value)
23935     {
23936         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23937     },
23938     onAddPane : function(pane)
23939     {
23940         this.panes.push(pane);
23941         //Roo.log('addpane');
23942         //Roo.log(pane);
23943         // tabs are rendere left to right..
23944         if(!this.showtabs){
23945             return;
23946         }
23947         
23948         var ctr = this.el.select('.nav-tabs', true).first();
23949          
23950          
23951         var existing = ctr.select('.nav-tab',true);
23952         var qty = existing.getCount();;
23953         
23954         
23955         var tab = ctr.createChild({
23956             tag : 'li',
23957             cls : 'nav-tab' + (qty ? '' : ' active'),
23958             cn : [
23959                 {
23960                     tag : 'a',
23961                     href:'#',
23962                     html : pane.title
23963                 }
23964             ]
23965         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23966         pane.tab = tab;
23967         
23968         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23969         if (!qty) {
23970             pane.el.addClass('active');
23971         }
23972         
23973                 
23974     },
23975     onTabClick : function(ev,un,ob,pane)
23976     {
23977         //Roo.log('tab - prev default');
23978         ev.preventDefault();
23979         
23980         
23981         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23982         pane.tab.addClass('active');
23983         //Roo.log(pane.title);
23984         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23985         // technically we should have a deactivate event.. but maybe add later.
23986         // and it should not de-activate the selected tab...
23987         this.fireEvent('activatepane', pane);
23988         pane.el.addClass('active');
23989         pane.fireEvent('activate');
23990         
23991         
23992     },
23993     
23994     getActivePane : function()
23995     {
23996         var r = false;
23997         Roo.each(this.panes, function(p) {
23998             if(p.el.hasClass('active')){
23999                 r = p;
24000                 return false;
24001             }
24002             
24003             return;
24004         });
24005         
24006         return r;
24007     }
24008     
24009     
24010 });
24011
24012  
24013 /*
24014  * - LGPL
24015  *
24016  * Tab pane
24017  * 
24018  */
24019 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24020 /**
24021  * @class Roo.bootstrap.TabPane
24022  * @extends Roo.bootstrap.Component
24023  * Bootstrap TabPane class
24024  * @cfg {Boolean} active (false | true) Default false
24025  * @cfg {String} title title of panel
24026
24027  * 
24028  * @constructor
24029  * Create a new TabPane
24030  * @param {Object} config The config object
24031  */
24032
24033 Roo.bootstrap.dash.TabPane = function(config){
24034     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24035     
24036     this.addEvents({
24037         // raw events
24038         /**
24039          * @event activate
24040          * When a pane is activated
24041          * @param {Roo.bootstrap.dash.TabPane} pane
24042          */
24043         "activate" : true
24044          
24045     });
24046 };
24047
24048 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24049     
24050     active : false,
24051     title : '',
24052     
24053     // the tabBox that this is attached to.
24054     tab : false,
24055      
24056     getAutoCreate : function() 
24057     {
24058         var cfg = {
24059             tag: 'div',
24060             cls: 'tab-pane'
24061         };
24062         
24063         if(this.active){
24064             cfg.cls += ' active';
24065         }
24066         
24067         return cfg;
24068     },
24069     initEvents  : function()
24070     {
24071         //Roo.log('trigger add pane handler');
24072         this.parent().fireEvent('addpane', this)
24073     },
24074     
24075      /**
24076      * Updates the tab title 
24077      * @param {String} html to set the title to.
24078      */
24079     setTitle: function(str)
24080     {
24081         if (!this.tab) {
24082             return;
24083         }
24084         this.title = str;
24085         this.tab.select('a', true).first().dom.innerHTML = str;
24086         
24087     }
24088     
24089     
24090     
24091 });
24092
24093  
24094
24095
24096  /*
24097  * - LGPL
24098  *
24099  * menu
24100  * 
24101  */
24102 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24103
24104 /**
24105  * @class Roo.bootstrap.menu.Menu
24106  * @extends Roo.bootstrap.Component
24107  * Bootstrap Menu class - container for Menu
24108  * @cfg {String} html Text of the menu
24109  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24110  * @cfg {String} icon Font awesome icon
24111  * @cfg {String} pos Menu align to (top | bottom) default bottom
24112  * 
24113  * 
24114  * @constructor
24115  * Create a new Menu
24116  * @param {Object} config The config object
24117  */
24118
24119
24120 Roo.bootstrap.menu.Menu = function(config){
24121     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24122     
24123     this.addEvents({
24124         /**
24125          * @event beforeshow
24126          * Fires before this menu is displayed
24127          * @param {Roo.bootstrap.menu.Menu} this
24128          */
24129         beforeshow : true,
24130         /**
24131          * @event beforehide
24132          * Fires before this menu is hidden
24133          * @param {Roo.bootstrap.menu.Menu} this
24134          */
24135         beforehide : true,
24136         /**
24137          * @event show
24138          * Fires after this menu is displayed
24139          * @param {Roo.bootstrap.menu.Menu} this
24140          */
24141         show : true,
24142         /**
24143          * @event hide
24144          * Fires after this menu is hidden
24145          * @param {Roo.bootstrap.menu.Menu} this
24146          */
24147         hide : true,
24148         /**
24149          * @event click
24150          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24151          * @param {Roo.bootstrap.menu.Menu} this
24152          * @param {Roo.EventObject} e
24153          */
24154         click : true
24155     });
24156     
24157 };
24158
24159 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24160     
24161     submenu : false,
24162     html : '',
24163     weight : 'default',
24164     icon : false,
24165     pos : 'bottom',
24166     
24167     
24168     getChildContainer : function() {
24169         if(this.isSubMenu){
24170             return this.el;
24171         }
24172         
24173         return this.el.select('ul.dropdown-menu', true).first();  
24174     },
24175     
24176     getAutoCreate : function()
24177     {
24178         var text = [
24179             {
24180                 tag : 'span',
24181                 cls : 'roo-menu-text',
24182                 html : this.html
24183             }
24184         ];
24185         
24186         if(this.icon){
24187             text.unshift({
24188                 tag : 'i',
24189                 cls : 'fa ' + this.icon
24190             })
24191         }
24192         
24193         
24194         var cfg = {
24195             tag : 'div',
24196             cls : 'btn-group',
24197             cn : [
24198                 {
24199                     tag : 'button',
24200                     cls : 'dropdown-button btn btn-' + this.weight,
24201                     cn : text
24202                 },
24203                 {
24204                     tag : 'button',
24205                     cls : 'dropdown-toggle btn btn-' + this.weight,
24206                     cn : [
24207                         {
24208                             tag : 'span',
24209                             cls : 'caret'
24210                         }
24211                     ]
24212                 },
24213                 {
24214                     tag : 'ul',
24215                     cls : 'dropdown-menu'
24216                 }
24217             ]
24218             
24219         };
24220         
24221         if(this.pos == 'top'){
24222             cfg.cls += ' dropup';
24223         }
24224         
24225         if(this.isSubMenu){
24226             cfg = {
24227                 tag : 'ul',
24228                 cls : 'dropdown-menu'
24229             }
24230         }
24231         
24232         return cfg;
24233     },
24234     
24235     onRender : function(ct, position)
24236     {
24237         this.isSubMenu = ct.hasClass('dropdown-submenu');
24238         
24239         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24240     },
24241     
24242     initEvents : function() 
24243     {
24244         if(this.isSubMenu){
24245             return;
24246         }
24247         
24248         this.hidden = true;
24249         
24250         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24251         this.triggerEl.on('click', this.onTriggerPress, this);
24252         
24253         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24254         this.buttonEl.on('click', this.onClick, this);
24255         
24256     },
24257     
24258     list : function()
24259     {
24260         if(this.isSubMenu){
24261             return this.el;
24262         }
24263         
24264         return this.el.select('ul.dropdown-menu', true).first();
24265     },
24266     
24267     onClick : function(e)
24268     {
24269         this.fireEvent("click", this, e);
24270     },
24271     
24272     onTriggerPress  : function(e)
24273     {   
24274         if (this.isVisible()) {
24275             this.hide();
24276         } else {
24277             this.show();
24278         }
24279     },
24280     
24281     isVisible : function(){
24282         return !this.hidden;
24283     },
24284     
24285     show : function()
24286     {
24287         this.fireEvent("beforeshow", this);
24288         
24289         this.hidden = false;
24290         this.el.addClass('open');
24291         
24292         Roo.get(document).on("mouseup", this.onMouseUp, this);
24293         
24294         this.fireEvent("show", this);
24295         
24296         
24297     },
24298     
24299     hide : function()
24300     {
24301         this.fireEvent("beforehide", this);
24302         
24303         this.hidden = true;
24304         this.el.removeClass('open');
24305         
24306         Roo.get(document).un("mouseup", this.onMouseUp);
24307         
24308         this.fireEvent("hide", this);
24309     },
24310     
24311     onMouseUp : function()
24312     {
24313         this.hide();
24314     }
24315     
24316 });
24317
24318  
24319  /*
24320  * - LGPL
24321  *
24322  * menu item
24323  * 
24324  */
24325 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24326
24327 /**
24328  * @class Roo.bootstrap.menu.Item
24329  * @extends Roo.bootstrap.Component
24330  * Bootstrap MenuItem class
24331  * @cfg {Boolean} submenu (true | false) default false
24332  * @cfg {String} html text of the item
24333  * @cfg {String} href the link
24334  * @cfg {Boolean} disable (true | false) default false
24335  * @cfg {Boolean} preventDefault (true | false) default true
24336  * @cfg {String} icon Font awesome icon
24337  * @cfg {String} pos Submenu align to (left | right) default right 
24338  * 
24339  * 
24340  * @constructor
24341  * Create a new Item
24342  * @param {Object} config The config object
24343  */
24344
24345
24346 Roo.bootstrap.menu.Item = function(config){
24347     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24348     this.addEvents({
24349         /**
24350          * @event mouseover
24351          * Fires when the mouse is hovering over this menu
24352          * @param {Roo.bootstrap.menu.Item} this
24353          * @param {Roo.EventObject} e
24354          */
24355         mouseover : true,
24356         /**
24357          * @event mouseout
24358          * Fires when the mouse exits this menu
24359          * @param {Roo.bootstrap.menu.Item} this
24360          * @param {Roo.EventObject} e
24361          */
24362         mouseout : true,
24363         // raw events
24364         /**
24365          * @event click
24366          * The raw click event for the entire grid.
24367          * @param {Roo.EventObject} e
24368          */
24369         click : true
24370     });
24371 };
24372
24373 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24374     
24375     submenu : false,
24376     href : '',
24377     html : '',
24378     preventDefault: true,
24379     disable : false,
24380     icon : false,
24381     pos : 'right',
24382     
24383     getAutoCreate : function()
24384     {
24385         var text = [
24386             {
24387                 tag : 'span',
24388                 cls : 'roo-menu-item-text',
24389                 html : this.html
24390             }
24391         ];
24392         
24393         if(this.icon){
24394             text.unshift({
24395                 tag : 'i',
24396                 cls : 'fa ' + this.icon
24397             })
24398         }
24399         
24400         var cfg = {
24401             tag : 'li',
24402             cn : [
24403                 {
24404                     tag : 'a',
24405                     href : this.href || '#',
24406                     cn : text
24407                 }
24408             ]
24409         };
24410         
24411         if(this.disable){
24412             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24413         }
24414         
24415         if(this.submenu){
24416             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24417             
24418             if(this.pos == 'left'){
24419                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24420             }
24421         }
24422         
24423         return cfg;
24424     },
24425     
24426     initEvents : function() 
24427     {
24428         this.el.on('mouseover', this.onMouseOver, this);
24429         this.el.on('mouseout', this.onMouseOut, this);
24430         
24431         this.el.select('a', true).first().on('click', this.onClick, this);
24432         
24433     },
24434     
24435     onClick : function(e)
24436     {
24437         if(this.preventDefault){
24438             e.preventDefault();
24439         }
24440         
24441         this.fireEvent("click", this, e);
24442     },
24443     
24444     onMouseOver : function(e)
24445     {
24446         if(this.submenu && this.pos == 'left'){
24447             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24448         }
24449         
24450         this.fireEvent("mouseover", this, e);
24451     },
24452     
24453     onMouseOut : function(e)
24454     {
24455         this.fireEvent("mouseout", this, e);
24456     }
24457 });
24458
24459  
24460
24461  /*
24462  * - LGPL
24463  *
24464  * menu separator
24465  * 
24466  */
24467 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24468
24469 /**
24470  * @class Roo.bootstrap.menu.Separator
24471  * @extends Roo.bootstrap.Component
24472  * Bootstrap Separator class
24473  * 
24474  * @constructor
24475  * Create a new Separator
24476  * @param {Object} config The config object
24477  */
24478
24479
24480 Roo.bootstrap.menu.Separator = function(config){
24481     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24482 };
24483
24484 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24485     
24486     getAutoCreate : function(){
24487         var cfg = {
24488             tag : 'li',
24489             cls: 'divider'
24490         };
24491         
24492         return cfg;
24493     }
24494    
24495 });
24496
24497  
24498
24499  /*
24500  * - LGPL
24501  *
24502  * Tooltip
24503  * 
24504  */
24505
24506 /**
24507  * @class Roo.bootstrap.Tooltip
24508  * Bootstrap Tooltip class
24509  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24510  * to determine which dom element triggers the tooltip.
24511  * 
24512  * It needs to add support for additional attributes like tooltip-position
24513  * 
24514  * @constructor
24515  * Create a new Toolti
24516  * @param {Object} config The config object
24517  */
24518
24519 Roo.bootstrap.Tooltip = function(config){
24520     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24521 };
24522
24523 Roo.apply(Roo.bootstrap.Tooltip, {
24524     /**
24525      * @function init initialize tooltip monitoring.
24526      * @static
24527      */
24528     currentEl : false,
24529     currentTip : false,
24530     currentRegion : false,
24531     
24532     //  init : delay?
24533     
24534     init : function()
24535     {
24536         Roo.get(document).on('mouseover', this.enter ,this);
24537         Roo.get(document).on('mouseout', this.leave, this);
24538          
24539         
24540         this.currentTip = new Roo.bootstrap.Tooltip();
24541     },
24542     
24543     enter : function(ev)
24544     {
24545         var dom = ev.getTarget();
24546         
24547         //Roo.log(['enter',dom]);
24548         var el = Roo.fly(dom);
24549         if (this.currentEl) {
24550             //Roo.log(dom);
24551             //Roo.log(this.currentEl);
24552             //Roo.log(this.currentEl.contains(dom));
24553             if (this.currentEl == el) {
24554                 return;
24555             }
24556             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24557                 return;
24558             }
24559
24560         }
24561         
24562         if (this.currentTip.el) {
24563             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24564         }    
24565         //Roo.log(ev);
24566         
24567         if(!el || el.dom == document){
24568             return;
24569         }
24570         
24571         var bindEl = el;
24572         
24573         // you can not look for children, as if el is the body.. then everythign is the child..
24574         if (!el.attr('tooltip')) { //
24575             if (!el.select("[tooltip]").elements.length) {
24576                 return;
24577             }
24578             // is the mouse over this child...?
24579             bindEl = el.select("[tooltip]").first();
24580             var xy = ev.getXY();
24581             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24582                 //Roo.log("not in region.");
24583                 return;
24584             }
24585             //Roo.log("child element over..");
24586             
24587         }
24588         this.currentEl = bindEl;
24589         this.currentTip.bind(bindEl);
24590         this.currentRegion = Roo.lib.Region.getRegion(dom);
24591         this.currentTip.enter();
24592         
24593     },
24594     leave : function(ev)
24595     {
24596         var dom = ev.getTarget();
24597         //Roo.log(['leave',dom]);
24598         if (!this.currentEl) {
24599             return;
24600         }
24601         
24602         
24603         if (dom != this.currentEl.dom) {
24604             return;
24605         }
24606         var xy = ev.getXY();
24607         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24608             return;
24609         }
24610         // only activate leave if mouse cursor is outside... bounding box..
24611         
24612         
24613         
24614         
24615         if (this.currentTip) {
24616             this.currentTip.leave();
24617         }
24618         //Roo.log('clear currentEl');
24619         this.currentEl = false;
24620         
24621         
24622     },
24623     alignment : {
24624         'left' : ['r-l', [-2,0], 'right'],
24625         'right' : ['l-r', [2,0], 'left'],
24626         'bottom' : ['t-b', [0,2], 'top'],
24627         'top' : [ 'b-t', [0,-2], 'bottom']
24628     }
24629     
24630 });
24631
24632
24633 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24634     
24635     
24636     bindEl : false,
24637     
24638     delay : null, // can be { show : 300 , hide: 500}
24639     
24640     timeout : null,
24641     
24642     hoverState : null, //???
24643     
24644     placement : 'bottom', 
24645     
24646     getAutoCreate : function(){
24647     
24648         var cfg = {
24649            cls : 'tooltip',
24650            role : 'tooltip',
24651            cn : [
24652                 {
24653                     cls : 'tooltip-arrow'
24654                 },
24655                 {
24656                     cls : 'tooltip-inner'
24657                 }
24658            ]
24659         };
24660         
24661         return cfg;
24662     },
24663     bind : function(el)
24664     {
24665         this.bindEl = el;
24666     },
24667       
24668     
24669     enter : function () {
24670        
24671         if (this.timeout != null) {
24672             clearTimeout(this.timeout);
24673         }
24674         
24675         this.hoverState = 'in';
24676          //Roo.log("enter - show");
24677         if (!this.delay || !this.delay.show) {
24678             this.show();
24679             return;
24680         }
24681         var _t = this;
24682         this.timeout = setTimeout(function () {
24683             if (_t.hoverState == 'in') {
24684                 _t.show();
24685             }
24686         }, this.delay.show);
24687     },
24688     leave : function()
24689     {
24690         clearTimeout(this.timeout);
24691     
24692         this.hoverState = 'out';
24693          if (!this.delay || !this.delay.hide) {
24694             this.hide();
24695             return;
24696         }
24697        
24698         var _t = this;
24699         this.timeout = setTimeout(function () {
24700             //Roo.log("leave - timeout");
24701             
24702             if (_t.hoverState == 'out') {
24703                 _t.hide();
24704                 Roo.bootstrap.Tooltip.currentEl = false;
24705             }
24706         }, delay);
24707     },
24708     
24709     show : function ()
24710     {
24711         if (!this.el) {
24712             this.render(document.body);
24713         }
24714         // set content.
24715         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24716         
24717         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24718         
24719         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24720         
24721         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24722         
24723         var placement = typeof this.placement == 'function' ?
24724             this.placement.call(this, this.el, on_el) :
24725             this.placement;
24726             
24727         var autoToken = /\s?auto?\s?/i;
24728         var autoPlace = autoToken.test(placement);
24729         if (autoPlace) {
24730             placement = placement.replace(autoToken, '') || 'top';
24731         }
24732         
24733         //this.el.detach()
24734         //this.el.setXY([0,0]);
24735         this.el.show();
24736         //this.el.dom.style.display='block';
24737         
24738         //this.el.appendTo(on_el);
24739         
24740         var p = this.getPosition();
24741         var box = this.el.getBox();
24742         
24743         if (autoPlace) {
24744             // fixme..
24745         }
24746         
24747         var align = Roo.bootstrap.Tooltip.alignment[placement];
24748         
24749         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24750         
24751         if(placement == 'top' || placement == 'bottom'){
24752             if(xy[0] < 0){
24753                 placement = 'right';
24754             }
24755             
24756             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24757                 placement = 'left';
24758             }
24759             
24760             var scroll = Roo.select('body', true).first().getScroll();
24761             
24762             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24763                 placement = 'top';
24764             }
24765             
24766         }
24767         
24768         align = Roo.bootstrap.Tooltip.alignment[placement];
24769         
24770         this.el.alignTo(this.bindEl, align[0],align[1]);
24771         //var arrow = this.el.select('.arrow',true).first();
24772         //arrow.set(align[2], 
24773         
24774         this.el.addClass(placement);
24775         
24776         this.el.addClass('in fade');
24777         
24778         this.hoverState = null;
24779         
24780         if (this.el.hasClass('fade')) {
24781             // fade it?
24782         }
24783         
24784     },
24785     hide : function()
24786     {
24787          
24788         if (!this.el) {
24789             return;
24790         }
24791         //this.el.setXY([0,0]);
24792         this.el.removeClass('in');
24793         //this.el.hide();
24794         
24795     }
24796     
24797 });
24798  
24799
24800  /*
24801  * - LGPL
24802  *
24803  * Location Picker
24804  * 
24805  */
24806
24807 /**
24808  * @class Roo.bootstrap.LocationPicker
24809  * @extends Roo.bootstrap.Component
24810  * Bootstrap LocationPicker class
24811  * @cfg {Number} latitude Position when init default 0
24812  * @cfg {Number} longitude Position when init default 0
24813  * @cfg {Number} zoom default 15
24814  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24815  * @cfg {Boolean} mapTypeControl default false
24816  * @cfg {Boolean} disableDoubleClickZoom default false
24817  * @cfg {Boolean} scrollwheel default true
24818  * @cfg {Boolean} streetViewControl default false
24819  * @cfg {Number} radius default 0
24820  * @cfg {String} locationName
24821  * @cfg {Boolean} draggable default true
24822  * @cfg {Boolean} enableAutocomplete default false
24823  * @cfg {Boolean} enableReverseGeocode default true
24824  * @cfg {String} markerTitle
24825  * 
24826  * @constructor
24827  * Create a new LocationPicker
24828  * @param {Object} config The config object
24829  */
24830
24831
24832 Roo.bootstrap.LocationPicker = function(config){
24833     
24834     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24835     
24836     this.addEvents({
24837         /**
24838          * @event initial
24839          * Fires when the picker initialized.
24840          * @param {Roo.bootstrap.LocationPicker} this
24841          * @param {Google Location} location
24842          */
24843         initial : true,
24844         /**
24845          * @event positionchanged
24846          * Fires when the picker position changed.
24847          * @param {Roo.bootstrap.LocationPicker} this
24848          * @param {Google Location} location
24849          */
24850         positionchanged : true,
24851         /**
24852          * @event resize
24853          * Fires when the map resize.
24854          * @param {Roo.bootstrap.LocationPicker} this
24855          */
24856         resize : true,
24857         /**
24858          * @event show
24859          * Fires when the map show.
24860          * @param {Roo.bootstrap.LocationPicker} this
24861          */
24862         show : true,
24863         /**
24864          * @event hide
24865          * Fires when the map hide.
24866          * @param {Roo.bootstrap.LocationPicker} this
24867          */
24868         hide : true,
24869         /**
24870          * @event mapClick
24871          * Fires when click the map.
24872          * @param {Roo.bootstrap.LocationPicker} this
24873          * @param {Map event} e
24874          */
24875         mapClick : true,
24876         /**
24877          * @event mapRightClick
24878          * Fires when right click the map.
24879          * @param {Roo.bootstrap.LocationPicker} this
24880          * @param {Map event} e
24881          */
24882         mapRightClick : true,
24883         /**
24884          * @event markerClick
24885          * Fires when click the marker.
24886          * @param {Roo.bootstrap.LocationPicker} this
24887          * @param {Map event} e
24888          */
24889         markerClick : true,
24890         /**
24891          * @event markerRightClick
24892          * Fires when right click the marker.
24893          * @param {Roo.bootstrap.LocationPicker} this
24894          * @param {Map event} e
24895          */
24896         markerRightClick : true,
24897         /**
24898          * @event OverlayViewDraw
24899          * Fires when OverlayView Draw
24900          * @param {Roo.bootstrap.LocationPicker} this
24901          */
24902         OverlayViewDraw : true,
24903         /**
24904          * @event OverlayViewOnAdd
24905          * Fires when OverlayView Draw
24906          * @param {Roo.bootstrap.LocationPicker} this
24907          */
24908         OverlayViewOnAdd : true,
24909         /**
24910          * @event OverlayViewOnRemove
24911          * Fires when OverlayView Draw
24912          * @param {Roo.bootstrap.LocationPicker} this
24913          */
24914         OverlayViewOnRemove : true,
24915         /**
24916          * @event OverlayViewShow
24917          * Fires when OverlayView Draw
24918          * @param {Roo.bootstrap.LocationPicker} this
24919          * @param {Pixel} cpx
24920          */
24921         OverlayViewShow : true,
24922         /**
24923          * @event OverlayViewHide
24924          * Fires when OverlayView Draw
24925          * @param {Roo.bootstrap.LocationPicker} this
24926          */
24927         OverlayViewHide : true,
24928         /**
24929          * @event loadexception
24930          * Fires when load google lib failed.
24931          * @param {Roo.bootstrap.LocationPicker} this
24932          */
24933         loadexception : true
24934     });
24935         
24936 };
24937
24938 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24939     
24940     gMapContext: false,
24941     
24942     latitude: 0,
24943     longitude: 0,
24944     zoom: 15,
24945     mapTypeId: false,
24946     mapTypeControl: false,
24947     disableDoubleClickZoom: false,
24948     scrollwheel: true,
24949     streetViewControl: false,
24950     radius: 0,
24951     locationName: '',
24952     draggable: true,
24953     enableAutocomplete: false,
24954     enableReverseGeocode: true,
24955     markerTitle: '',
24956     
24957     getAutoCreate: function()
24958     {
24959
24960         var cfg = {
24961             tag: 'div',
24962             cls: 'roo-location-picker'
24963         };
24964         
24965         return cfg
24966     },
24967     
24968     initEvents: function(ct, position)
24969     {       
24970         if(!this.el.getWidth() || this.isApplied()){
24971             return;
24972         }
24973         
24974         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24975         
24976         this.initial();
24977     },
24978     
24979     initial: function()
24980     {
24981         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24982             this.fireEvent('loadexception', this);
24983             return;
24984         }
24985         
24986         if(!this.mapTypeId){
24987             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24988         }
24989         
24990         this.gMapContext = this.GMapContext();
24991         
24992         this.initOverlayView();
24993         
24994         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24995         
24996         var _this = this;
24997                 
24998         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24999             _this.setPosition(_this.gMapContext.marker.position);
25000         });
25001         
25002         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25003             _this.fireEvent('mapClick', this, event);
25004             
25005         });
25006
25007         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25008             _this.fireEvent('mapRightClick', this, event);
25009             
25010         });
25011         
25012         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25013             _this.fireEvent('markerClick', this, event);
25014             
25015         });
25016
25017         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25018             _this.fireEvent('markerRightClick', this, event);
25019             
25020         });
25021         
25022         this.setPosition(this.gMapContext.location);
25023         
25024         this.fireEvent('initial', this, this.gMapContext.location);
25025     },
25026     
25027     initOverlayView: function()
25028     {
25029         var _this = this;
25030         
25031         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25032             
25033             draw: function()
25034             {
25035                 _this.fireEvent('OverlayViewDraw', _this);
25036             },
25037             
25038             onAdd: function()
25039             {
25040                 _this.fireEvent('OverlayViewOnAdd', _this);
25041             },
25042             
25043             onRemove: function()
25044             {
25045                 _this.fireEvent('OverlayViewOnRemove', _this);
25046             },
25047             
25048             show: function(cpx)
25049             {
25050                 _this.fireEvent('OverlayViewShow', _this, cpx);
25051             },
25052             
25053             hide: function()
25054             {
25055                 _this.fireEvent('OverlayViewHide', _this);
25056             }
25057             
25058         });
25059     },
25060     
25061     fromLatLngToContainerPixel: function(event)
25062     {
25063         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25064     },
25065     
25066     isApplied: function() 
25067     {
25068         return this.getGmapContext() == false ? false : true;
25069     },
25070     
25071     getGmapContext: function() 
25072     {
25073         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25074     },
25075     
25076     GMapContext: function() 
25077     {
25078         var position = new google.maps.LatLng(this.latitude, this.longitude);
25079         
25080         var _map = new google.maps.Map(this.el.dom, {
25081             center: position,
25082             zoom: this.zoom,
25083             mapTypeId: this.mapTypeId,
25084             mapTypeControl: this.mapTypeControl,
25085             disableDoubleClickZoom: this.disableDoubleClickZoom,
25086             scrollwheel: this.scrollwheel,
25087             streetViewControl: this.streetViewControl,
25088             locationName: this.locationName,
25089             draggable: this.draggable,
25090             enableAutocomplete: this.enableAutocomplete,
25091             enableReverseGeocode: this.enableReverseGeocode
25092         });
25093         
25094         var _marker = new google.maps.Marker({
25095             position: position,
25096             map: _map,
25097             title: this.markerTitle,
25098             draggable: this.draggable
25099         });
25100         
25101         return {
25102             map: _map,
25103             marker: _marker,
25104             circle: null,
25105             location: position,
25106             radius: this.radius,
25107             locationName: this.locationName,
25108             addressComponents: {
25109                 formatted_address: null,
25110                 addressLine1: null,
25111                 addressLine2: null,
25112                 streetName: null,
25113                 streetNumber: null,
25114                 city: null,
25115                 district: null,
25116                 state: null,
25117                 stateOrProvince: null
25118             },
25119             settings: this,
25120             domContainer: this.el.dom,
25121             geodecoder: new google.maps.Geocoder()
25122         };
25123     },
25124     
25125     drawCircle: function(center, radius, options) 
25126     {
25127         if (this.gMapContext.circle != null) {
25128             this.gMapContext.circle.setMap(null);
25129         }
25130         if (radius > 0) {
25131             radius *= 1;
25132             options = Roo.apply({}, options, {
25133                 strokeColor: "#0000FF",
25134                 strokeOpacity: .35,
25135                 strokeWeight: 2,
25136                 fillColor: "#0000FF",
25137                 fillOpacity: .2
25138             });
25139             
25140             options.map = this.gMapContext.map;
25141             options.radius = radius;
25142             options.center = center;
25143             this.gMapContext.circle = new google.maps.Circle(options);
25144             return this.gMapContext.circle;
25145         }
25146         
25147         return null;
25148     },
25149     
25150     setPosition: function(location) 
25151     {
25152         this.gMapContext.location = location;
25153         this.gMapContext.marker.setPosition(location);
25154         this.gMapContext.map.panTo(location);
25155         this.drawCircle(location, this.gMapContext.radius, {});
25156         
25157         var _this = this;
25158         
25159         if (this.gMapContext.settings.enableReverseGeocode) {
25160             this.gMapContext.geodecoder.geocode({
25161                 latLng: this.gMapContext.location
25162             }, function(results, status) {
25163                 
25164                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25165                     _this.gMapContext.locationName = results[0].formatted_address;
25166                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25167                     
25168                     _this.fireEvent('positionchanged', this, location);
25169                 }
25170             });
25171             
25172             return;
25173         }
25174         
25175         this.fireEvent('positionchanged', this, location);
25176     },
25177     
25178     resize: function()
25179     {
25180         google.maps.event.trigger(this.gMapContext.map, "resize");
25181         
25182         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25183         
25184         this.fireEvent('resize', this);
25185     },
25186     
25187     setPositionByLatLng: function(latitude, longitude)
25188     {
25189         this.setPosition(new google.maps.LatLng(latitude, longitude));
25190     },
25191     
25192     getCurrentPosition: function() 
25193     {
25194         return {
25195             latitude: this.gMapContext.location.lat(),
25196             longitude: this.gMapContext.location.lng()
25197         };
25198     },
25199     
25200     getAddressName: function() 
25201     {
25202         return this.gMapContext.locationName;
25203     },
25204     
25205     getAddressComponents: function() 
25206     {
25207         return this.gMapContext.addressComponents;
25208     },
25209     
25210     address_component_from_google_geocode: function(address_components) 
25211     {
25212         var result = {};
25213         
25214         for (var i = 0; i < address_components.length; i++) {
25215             var component = address_components[i];
25216             if (component.types.indexOf("postal_code") >= 0) {
25217                 result.postalCode = component.short_name;
25218             } else if (component.types.indexOf("street_number") >= 0) {
25219                 result.streetNumber = component.short_name;
25220             } else if (component.types.indexOf("route") >= 0) {
25221                 result.streetName = component.short_name;
25222             } else if (component.types.indexOf("neighborhood") >= 0) {
25223                 result.city = component.short_name;
25224             } else if (component.types.indexOf("locality") >= 0) {
25225                 result.city = component.short_name;
25226             } else if (component.types.indexOf("sublocality") >= 0) {
25227                 result.district = component.short_name;
25228             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25229                 result.stateOrProvince = component.short_name;
25230             } else if (component.types.indexOf("country") >= 0) {
25231                 result.country = component.short_name;
25232             }
25233         }
25234         
25235         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25236         result.addressLine2 = "";
25237         return result;
25238     },
25239     
25240     setZoomLevel: function(zoom)
25241     {
25242         this.gMapContext.map.setZoom(zoom);
25243     },
25244     
25245     show: function()
25246     {
25247         if(!this.el){
25248             return;
25249         }
25250         
25251         this.el.show();
25252         
25253         this.resize();
25254         
25255         this.fireEvent('show', this);
25256     },
25257     
25258     hide: function()
25259     {
25260         if(!this.el){
25261             return;
25262         }
25263         
25264         this.el.hide();
25265         
25266         this.fireEvent('hide', this);
25267     }
25268     
25269 });
25270
25271 Roo.apply(Roo.bootstrap.LocationPicker, {
25272     
25273     OverlayView : function(map, options)
25274     {
25275         options = options || {};
25276         
25277         this.setMap(map);
25278     }
25279     
25280     
25281 });/*
25282  * - LGPL
25283  *
25284  * Alert
25285  * 
25286  */
25287
25288 /**
25289  * @class Roo.bootstrap.Alert
25290  * @extends Roo.bootstrap.Component
25291  * Bootstrap Alert class
25292  * @cfg {String} title The title of alert
25293  * @cfg {String} html The content of alert
25294  * @cfg {String} weight (  success | info | warning | danger )
25295  * @cfg {String} faicon font-awesomeicon
25296  * 
25297  * @constructor
25298  * Create a new alert
25299  * @param {Object} config The config object
25300  */
25301
25302
25303 Roo.bootstrap.Alert = function(config){
25304     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25305     
25306 };
25307
25308 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25309     
25310     title: '',
25311     html: '',
25312     weight: false,
25313     faicon: false,
25314     
25315     getAutoCreate : function()
25316     {
25317         
25318         var cfg = {
25319             tag : 'div',
25320             cls : 'alert',
25321             cn : [
25322                 {
25323                     tag : 'i',
25324                     cls : 'roo-alert-icon'
25325                     
25326                 },
25327                 {
25328                     tag : 'b',
25329                     cls : 'roo-alert-title',
25330                     html : this.title
25331                 },
25332                 {
25333                     tag : 'span',
25334                     cls : 'roo-alert-text',
25335                     html : this.html
25336                 }
25337             ]
25338         };
25339         
25340         if(this.faicon){
25341             cfg.cn[0].cls += ' fa ' + this.faicon;
25342         }
25343         
25344         if(this.weight){
25345             cfg.cls += ' alert-' + this.weight;
25346         }
25347         
25348         return cfg;
25349     },
25350     
25351     initEvents: function() 
25352     {
25353         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25354     },
25355     
25356     setTitle : function(str)
25357     {
25358         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25359     },
25360     
25361     setText : function(str)
25362     {
25363         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25364     },
25365     
25366     setWeight : function(weight)
25367     {
25368         if(this.weight){
25369             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25370         }
25371         
25372         this.weight = weight;
25373         
25374         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25375     },
25376     
25377     setIcon : function(icon)
25378     {
25379         if(this.faicon){
25380             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25381         }
25382         
25383         this.faicon = icon;
25384         
25385         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25386     },
25387     
25388     hide: function() 
25389     {
25390         this.el.hide();   
25391     },
25392     
25393     show: function() 
25394     {  
25395         this.el.show();   
25396     }
25397     
25398 });
25399
25400  
25401 /*
25402 * Licence: LGPL
25403 */
25404
25405 /**
25406  * @class Roo.bootstrap.UploadCropbox
25407  * @extends Roo.bootstrap.Component
25408  * Bootstrap UploadCropbox class
25409  * @cfg {String} emptyText show when image has been loaded
25410  * @cfg {String} rotateNotify show when image too small to rotate
25411  * @cfg {Number} errorTimeout default 3000
25412  * @cfg {Number} minWidth default 300
25413  * @cfg {Number} minHeight default 300
25414  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25415  * @cfg {Boolean} isDocument (true|false) default false
25416  * @cfg {String} url action url
25417  * @cfg {String} paramName default 'imageUpload'
25418  * @cfg {String} method default POST
25419  * @cfg {Boolean} loadMask (true|false) default true
25420  * @cfg {Boolean} loadingText default 'Loading...'
25421  * 
25422  * @constructor
25423  * Create a new UploadCropbox
25424  * @param {Object} config The config object
25425  */
25426
25427 Roo.bootstrap.UploadCropbox = function(config){
25428     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25429     
25430     this.addEvents({
25431         /**
25432          * @event beforeselectfile
25433          * Fire before select file
25434          * @param {Roo.bootstrap.UploadCropbox} this
25435          */
25436         "beforeselectfile" : true,
25437         /**
25438          * @event initial
25439          * Fire after initEvent
25440          * @param {Roo.bootstrap.UploadCropbox} this
25441          */
25442         "initial" : true,
25443         /**
25444          * @event crop
25445          * Fire after initEvent
25446          * @param {Roo.bootstrap.UploadCropbox} this
25447          * @param {String} data
25448          */
25449         "crop" : true,
25450         /**
25451          * @event prepare
25452          * Fire when preparing the file data
25453          * @param {Roo.bootstrap.UploadCropbox} this
25454          * @param {Object} file
25455          */
25456         "prepare" : true,
25457         /**
25458          * @event exception
25459          * Fire when get exception
25460          * @param {Roo.bootstrap.UploadCropbox} this
25461          * @param {XMLHttpRequest} xhr
25462          */
25463         "exception" : true,
25464         /**
25465          * @event beforeloadcanvas
25466          * Fire before load the canvas
25467          * @param {Roo.bootstrap.UploadCropbox} this
25468          * @param {String} src
25469          */
25470         "beforeloadcanvas" : true,
25471         /**
25472          * @event trash
25473          * Fire when trash image
25474          * @param {Roo.bootstrap.UploadCropbox} this
25475          */
25476         "trash" : true,
25477         /**
25478          * @event download
25479          * Fire when download the image
25480          * @param {Roo.bootstrap.UploadCropbox} this
25481          */
25482         "download" : true,
25483         /**
25484          * @event footerbuttonclick
25485          * Fire when footerbuttonclick
25486          * @param {Roo.bootstrap.UploadCropbox} this
25487          * @param {String} type
25488          */
25489         "footerbuttonclick" : true,
25490         /**
25491          * @event resize
25492          * Fire when resize
25493          * @param {Roo.bootstrap.UploadCropbox} this
25494          */
25495         "resize" : true,
25496         /**
25497          * @event rotate
25498          * Fire when rotate the image
25499          * @param {Roo.bootstrap.UploadCropbox} this
25500          * @param {String} pos
25501          */
25502         "rotate" : true,
25503         /**
25504          * @event inspect
25505          * Fire when inspect the file
25506          * @param {Roo.bootstrap.UploadCropbox} this
25507          * @param {Object} file
25508          */
25509         "inspect" : true,
25510         /**
25511          * @event upload
25512          * Fire when xhr upload the file
25513          * @param {Roo.bootstrap.UploadCropbox} this
25514          * @param {Object} data
25515          */
25516         "upload" : true,
25517         /**
25518          * @event arrange
25519          * Fire when arrange the file data
25520          * @param {Roo.bootstrap.UploadCropbox} this
25521          * @param {Object} formData
25522          */
25523         "arrange" : true
25524     });
25525     
25526     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25527 };
25528
25529 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25530     
25531     emptyText : 'Click to upload image',
25532     rotateNotify : 'Image is too small to rotate',
25533     errorTimeout : 3000,
25534     scale : 0,
25535     baseScale : 1,
25536     rotate : 0,
25537     dragable : false,
25538     pinching : false,
25539     mouseX : 0,
25540     mouseY : 0,
25541     cropData : false,
25542     minWidth : 300,
25543     minHeight : 300,
25544     file : false,
25545     exif : {},
25546     baseRotate : 1,
25547     cropType : 'image/jpeg',
25548     buttons : false,
25549     canvasLoaded : false,
25550     isDocument : false,
25551     method : 'POST',
25552     paramName : 'imageUpload',
25553     loadMask : true,
25554     loadingText : 'Loading...',
25555     maskEl : false,
25556     
25557     getAutoCreate : function()
25558     {
25559         var cfg = {
25560             tag : 'div',
25561             cls : 'roo-upload-cropbox',
25562             cn : [
25563                 {
25564                     tag : 'input',
25565                     cls : 'roo-upload-cropbox-selector',
25566                     type : 'file'
25567                 },
25568                 {
25569                     tag : 'div',
25570                     cls : 'roo-upload-cropbox-body',
25571                     style : 'cursor:pointer',
25572                     cn : [
25573                         {
25574                             tag : 'div',
25575                             cls : 'roo-upload-cropbox-preview'
25576                         },
25577                         {
25578                             tag : 'div',
25579                             cls : 'roo-upload-cropbox-thumb'
25580                         },
25581                         {
25582                             tag : 'div',
25583                             cls : 'roo-upload-cropbox-empty-notify',
25584                             html : this.emptyText
25585                         },
25586                         {
25587                             tag : 'div',
25588                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25589                             html : this.rotateNotify
25590                         }
25591                     ]
25592                 },
25593                 {
25594                     tag : 'div',
25595                     cls : 'roo-upload-cropbox-footer',
25596                     cn : {
25597                         tag : 'div',
25598                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25599                         cn : []
25600                     }
25601                 }
25602             ]
25603         };
25604         
25605         return cfg;
25606     },
25607     
25608     onRender : function(ct, position)
25609     {
25610         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25611         
25612         if (this.buttons.length) {
25613             
25614             Roo.each(this.buttons, function(bb) {
25615                 
25616                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25617                 
25618                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25619                 
25620             }, this);
25621         }
25622         
25623         if(this.loadMask){
25624             this.maskEl = this.el;
25625         }
25626     },
25627     
25628     initEvents : function()
25629     {
25630         this.urlAPI = (window.createObjectURL && window) || 
25631                                 (window.URL && URL.revokeObjectURL && URL) || 
25632                                 (window.webkitURL && webkitURL);
25633                         
25634         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25635         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25636         
25637         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25638         this.selectorEl.hide();
25639         
25640         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25641         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25642         
25643         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25644         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25645         this.thumbEl.hide();
25646         
25647         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25648         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25649         
25650         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25651         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25652         this.errorEl.hide();
25653         
25654         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25655         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25656         this.footerEl.hide();
25657         
25658         this.setThumbBoxSize();
25659         
25660         this.bind();
25661         
25662         this.resize();
25663         
25664         this.fireEvent('initial', this);
25665     },
25666
25667     bind : function()
25668     {
25669         var _this = this;
25670         
25671         window.addEventListener("resize", function() { _this.resize(); } );
25672         
25673         this.bodyEl.on('click', this.beforeSelectFile, this);
25674         
25675         if(Roo.isTouch){
25676             this.bodyEl.on('touchstart', this.onTouchStart, this);
25677             this.bodyEl.on('touchmove', this.onTouchMove, this);
25678             this.bodyEl.on('touchend', this.onTouchEnd, this);
25679         }
25680         
25681         if(!Roo.isTouch){
25682             this.bodyEl.on('mousedown', this.onMouseDown, this);
25683             this.bodyEl.on('mousemove', this.onMouseMove, this);
25684             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25685             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25686             Roo.get(document).on('mouseup', this.onMouseUp, this);
25687         }
25688         
25689         this.selectorEl.on('change', this.onFileSelected, this);
25690     },
25691     
25692     reset : function()
25693     {    
25694         this.scale = 0;
25695         this.baseScale = 1;
25696         this.rotate = 0;
25697         this.baseRotate = 1;
25698         this.dragable = false;
25699         this.pinching = false;
25700         this.mouseX = 0;
25701         this.mouseY = 0;
25702         this.cropData = false;
25703         this.notifyEl.dom.innerHTML = this.emptyText;
25704         
25705         this.selectorEl.dom.value = '';
25706         
25707     },
25708     
25709     resize : function()
25710     {
25711         if(this.fireEvent('resize', this) != false){
25712             this.setThumbBoxPosition();
25713             this.setCanvasPosition();
25714         }
25715     },
25716     
25717     onFooterButtonClick : function(e, el, o, type)
25718     {
25719         switch (type) {
25720             case 'rotate-left' :
25721                 this.onRotateLeft(e);
25722                 break;
25723             case 'rotate-right' :
25724                 this.onRotateRight(e);
25725                 break;
25726             case 'picture' :
25727                 this.beforeSelectFile(e);
25728                 break;
25729             case 'trash' :
25730                 this.trash(e);
25731                 break;
25732             case 'crop' :
25733                 this.crop(e);
25734                 break;
25735             case 'download' :
25736                 this.download(e);
25737                 break;
25738             default :
25739                 break;
25740         }
25741         
25742         this.fireEvent('footerbuttonclick', this, type);
25743     },
25744     
25745     beforeSelectFile : function(e)
25746     {
25747         e.preventDefault();
25748         
25749         if(this.fireEvent('beforeselectfile', this) != false){
25750             this.selectorEl.dom.click();
25751         }
25752     },
25753     
25754     onFileSelected : function(e)
25755     {
25756         e.preventDefault();
25757         
25758         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25759             return;
25760         }
25761         
25762         var file = this.selectorEl.dom.files[0];
25763         
25764         if(this.fireEvent('inspect', this, file) != false){
25765             this.prepare(file);
25766         }
25767         
25768     },
25769     
25770     trash : function(e)
25771     {
25772         this.fireEvent('trash', this);
25773     },
25774     
25775     download : function(e)
25776     {
25777         this.fireEvent('download', this);
25778     },
25779     
25780     loadCanvas : function(src)
25781     {   
25782         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25783             
25784             this.reset();
25785             
25786             this.imageEl = document.createElement('img');
25787             
25788             var _this = this;
25789             
25790             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25791             
25792             this.imageEl.src = src;
25793         }
25794     },
25795     
25796     onLoadCanvas : function()
25797     {   
25798         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25799         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25800         
25801         this.bodyEl.un('click', this.beforeSelectFile, this);
25802         
25803         this.notifyEl.hide();
25804         this.thumbEl.show();
25805         this.footerEl.show();
25806         
25807         this.baseRotateLevel();
25808         
25809         if(this.isDocument){
25810             this.setThumbBoxSize();
25811         }
25812         
25813         this.setThumbBoxPosition();
25814         
25815         this.baseScaleLevel();
25816         
25817         this.draw();
25818         
25819         this.resize();
25820         
25821         this.canvasLoaded = true;
25822         
25823         if(this.loadMask){
25824             this.maskEl.unmask();
25825         }
25826         
25827     },
25828     
25829     setCanvasPosition : function()
25830     {   
25831         if(!this.canvasEl){
25832             return;
25833         }
25834         
25835         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25836         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25837         
25838         this.previewEl.setLeft(pw);
25839         this.previewEl.setTop(ph);
25840         
25841     },
25842     
25843     onMouseDown : function(e)
25844     {   
25845         e.stopEvent();
25846         
25847         this.dragable = true;
25848         this.pinching = false;
25849         
25850         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25851             this.dragable = false;
25852             return;
25853         }
25854         
25855         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25856         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25857         
25858     },
25859     
25860     onMouseMove : function(e)
25861     {   
25862         e.stopEvent();
25863         
25864         if(!this.canvasLoaded){
25865             return;
25866         }
25867         
25868         if (!this.dragable){
25869             return;
25870         }
25871         
25872         var minX = Math.ceil(this.thumbEl.getLeft(true));
25873         var minY = Math.ceil(this.thumbEl.getTop(true));
25874         
25875         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25876         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25877         
25878         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25879         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25880         
25881         x = x - this.mouseX;
25882         y = y - this.mouseY;
25883         
25884         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25885         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25886         
25887         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25888         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25889         
25890         this.previewEl.setLeft(bgX);
25891         this.previewEl.setTop(bgY);
25892         
25893         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25894         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25895     },
25896     
25897     onMouseUp : function(e)
25898     {   
25899         e.stopEvent();
25900         
25901         this.dragable = false;
25902     },
25903     
25904     onMouseWheel : function(e)
25905     {   
25906         e.stopEvent();
25907         
25908         this.startScale = this.scale;
25909         
25910         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25911         
25912         if(!this.zoomable()){
25913             this.scale = this.startScale;
25914             return;
25915         }
25916         
25917         this.draw();
25918         
25919         return;
25920     },
25921     
25922     zoomable : function()
25923     {
25924         var minScale = this.thumbEl.getWidth() / this.minWidth;
25925         
25926         if(this.minWidth < this.minHeight){
25927             minScale = this.thumbEl.getHeight() / this.minHeight;
25928         }
25929         
25930         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25931         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25932         
25933         if(
25934                 this.isDocument &&
25935                 (this.rotate == 0 || this.rotate == 180) && 
25936                 (
25937                     width > this.imageEl.OriginWidth || 
25938                     height > this.imageEl.OriginHeight ||
25939                     (width < this.minWidth && height < this.minHeight)
25940                 )
25941         ){
25942             return false;
25943         }
25944         
25945         if(
25946                 this.isDocument &&
25947                 (this.rotate == 90 || this.rotate == 270) && 
25948                 (
25949                     width > this.imageEl.OriginWidth || 
25950                     height > this.imageEl.OriginHeight ||
25951                     (width < this.minHeight && height < this.minWidth)
25952                 )
25953         ){
25954             return false;
25955         }
25956         
25957         if(
25958                 !this.isDocument &&
25959                 (this.rotate == 0 || this.rotate == 180) && 
25960                 (
25961                     width < this.minWidth || 
25962                     width > this.imageEl.OriginWidth || 
25963                     height < this.minHeight || 
25964                     height > this.imageEl.OriginHeight
25965                 )
25966         ){
25967             return false;
25968         }
25969         
25970         if(
25971                 !this.isDocument &&
25972                 (this.rotate == 90 || this.rotate == 270) && 
25973                 (
25974                     width < this.minHeight || 
25975                     width > this.imageEl.OriginWidth || 
25976                     height < this.minWidth || 
25977                     height > this.imageEl.OriginHeight
25978                 )
25979         ){
25980             return false;
25981         }
25982         
25983         return true;
25984         
25985     },
25986     
25987     onRotateLeft : function(e)
25988     {   
25989         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25990             
25991             var minScale = this.thumbEl.getWidth() / this.minWidth;
25992             
25993             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25994             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25995             
25996             this.startScale = this.scale;
25997             
25998             while (this.getScaleLevel() < minScale){
25999             
26000                 this.scale = this.scale + 1;
26001                 
26002                 if(!this.zoomable()){
26003                     break;
26004                 }
26005                 
26006                 if(
26007                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26008                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26009                 ){
26010                     continue;
26011                 }
26012                 
26013                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26014
26015                 this.draw();
26016                 
26017                 return;
26018             }
26019             
26020             this.scale = this.startScale;
26021             
26022             this.onRotateFail();
26023             
26024             return false;
26025         }
26026         
26027         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26028
26029         if(this.isDocument){
26030             this.setThumbBoxSize();
26031             this.setThumbBoxPosition();
26032             this.setCanvasPosition();
26033         }
26034         
26035         this.draw();
26036         
26037         this.fireEvent('rotate', this, 'left');
26038         
26039     },
26040     
26041     onRotateRight : function(e)
26042     {
26043         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26044             
26045             var minScale = this.thumbEl.getWidth() / this.minWidth;
26046         
26047             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26048             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26049             
26050             this.startScale = this.scale;
26051             
26052             while (this.getScaleLevel() < minScale){
26053             
26054                 this.scale = this.scale + 1;
26055                 
26056                 if(!this.zoomable()){
26057                     break;
26058                 }
26059                 
26060                 if(
26061                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26062                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26063                 ){
26064                     continue;
26065                 }
26066                 
26067                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26068
26069                 this.draw();
26070                 
26071                 return;
26072             }
26073             
26074             this.scale = this.startScale;
26075             
26076             this.onRotateFail();
26077             
26078             return false;
26079         }
26080         
26081         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26082
26083         if(this.isDocument){
26084             this.setThumbBoxSize();
26085             this.setThumbBoxPosition();
26086             this.setCanvasPosition();
26087         }
26088         
26089         this.draw();
26090         
26091         this.fireEvent('rotate', this, 'right');
26092     },
26093     
26094     onRotateFail : function()
26095     {
26096         this.errorEl.show(true);
26097         
26098         var _this = this;
26099         
26100         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26101     },
26102     
26103     draw : function()
26104     {
26105         this.previewEl.dom.innerHTML = '';
26106         
26107         var canvasEl = document.createElement("canvas");
26108         
26109         var contextEl = canvasEl.getContext("2d");
26110         
26111         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26112         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26113         var center = this.imageEl.OriginWidth / 2;
26114         
26115         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26116             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26117             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26118             center = this.imageEl.OriginHeight / 2;
26119         }
26120         
26121         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26122         
26123         contextEl.translate(center, center);
26124         contextEl.rotate(this.rotate * Math.PI / 180);
26125
26126         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26127         
26128         this.canvasEl = document.createElement("canvas");
26129         
26130         this.contextEl = this.canvasEl.getContext("2d");
26131         
26132         switch (this.rotate) {
26133             case 0 :
26134                 
26135                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26136                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26137                 
26138                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26139                 
26140                 break;
26141             case 90 : 
26142                 
26143                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26144                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26145                 
26146                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26147                     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);
26148                     break;
26149                 }
26150                 
26151                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26152                 
26153                 break;
26154             case 180 :
26155                 
26156                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26157                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26158                 
26159                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26160                     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);
26161                     break;
26162                 }
26163                 
26164                 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);
26165                 
26166                 break;
26167             case 270 :
26168                 
26169                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26170                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26171         
26172                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26173                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26174                     break;
26175                 }
26176                 
26177                 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);
26178                 
26179                 break;
26180             default : 
26181                 break;
26182         }
26183         
26184         this.previewEl.appendChild(this.canvasEl);
26185         
26186         this.setCanvasPosition();
26187     },
26188     
26189     crop : function()
26190     {
26191         if(!this.canvasLoaded){
26192             return;
26193         }
26194         
26195         var imageCanvas = document.createElement("canvas");
26196         
26197         var imageContext = imageCanvas.getContext("2d");
26198         
26199         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26200         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26201         
26202         var center = imageCanvas.width / 2;
26203         
26204         imageContext.translate(center, center);
26205         
26206         imageContext.rotate(this.rotate * Math.PI / 180);
26207         
26208         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26209         
26210         var canvas = document.createElement("canvas");
26211         
26212         var context = canvas.getContext("2d");
26213                 
26214         canvas.width = this.minWidth;
26215         canvas.height = this.minHeight;
26216
26217         switch (this.rotate) {
26218             case 0 :
26219                 
26220                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26221                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26222                 
26223                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26224                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26225                 
26226                 var targetWidth = this.minWidth - 2 * x;
26227                 var targetHeight = this.minHeight - 2 * y;
26228                 
26229                 var scale = 1;
26230                 
26231                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26232                     scale = targetWidth / width;
26233                 }
26234                 
26235                 if(x > 0 && y == 0){
26236                     scale = targetHeight / height;
26237                 }
26238                 
26239                 if(x > 0 && y > 0){
26240                     scale = targetWidth / width;
26241                     
26242                     if(width < height){
26243                         scale = targetHeight / height;
26244                     }
26245                 }
26246                 
26247                 context.scale(scale, scale);
26248                 
26249                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26250                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26251
26252                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26253                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26254
26255                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26256                 
26257                 break;
26258             case 90 : 
26259                 
26260                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26261                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26262                 
26263                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26264                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26265                 
26266                 var targetWidth = this.minWidth - 2 * x;
26267                 var targetHeight = this.minHeight - 2 * y;
26268                 
26269                 var scale = 1;
26270                 
26271                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26272                     scale = targetWidth / width;
26273                 }
26274                 
26275                 if(x > 0 && y == 0){
26276                     scale = targetHeight / height;
26277                 }
26278                 
26279                 if(x > 0 && y > 0){
26280                     scale = targetWidth / width;
26281                     
26282                     if(width < height){
26283                         scale = targetHeight / height;
26284                     }
26285                 }
26286                 
26287                 context.scale(scale, scale);
26288                 
26289                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26290                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26291
26292                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26293                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26294                 
26295                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26296                 
26297                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26298                 
26299                 break;
26300             case 180 :
26301                 
26302                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26303                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26304                 
26305                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26306                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26307                 
26308                 var targetWidth = this.minWidth - 2 * x;
26309                 var targetHeight = this.minHeight - 2 * y;
26310                 
26311                 var scale = 1;
26312                 
26313                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26314                     scale = targetWidth / width;
26315                 }
26316                 
26317                 if(x > 0 && y == 0){
26318                     scale = targetHeight / height;
26319                 }
26320                 
26321                 if(x > 0 && y > 0){
26322                     scale = targetWidth / width;
26323                     
26324                     if(width < height){
26325                         scale = targetHeight / height;
26326                     }
26327                 }
26328                 
26329                 context.scale(scale, scale);
26330                 
26331                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26332                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26333
26334                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26335                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26336
26337                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26338                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26339                 
26340                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26341                 
26342                 break;
26343             case 270 :
26344                 
26345                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26346                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26347                 
26348                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26349                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26350                 
26351                 var targetWidth = this.minWidth - 2 * x;
26352                 var targetHeight = this.minHeight - 2 * y;
26353                 
26354                 var scale = 1;
26355                 
26356                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26357                     scale = targetWidth / width;
26358                 }
26359                 
26360                 if(x > 0 && y == 0){
26361                     scale = targetHeight / height;
26362                 }
26363                 
26364                 if(x > 0 && y > 0){
26365                     scale = targetWidth / width;
26366                     
26367                     if(width < height){
26368                         scale = targetHeight / height;
26369                     }
26370                 }
26371                 
26372                 context.scale(scale, scale);
26373                 
26374                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26375                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26376
26377                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26378                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26379                 
26380                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26381                 
26382                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26383                 
26384                 break;
26385             default : 
26386                 break;
26387         }
26388         
26389         this.cropData = canvas.toDataURL(this.cropType);
26390         
26391         if(this.fireEvent('crop', this, this.cropData) !== false){
26392             this.process(this.file, this.cropData);
26393         }
26394         
26395         return;
26396         
26397     },
26398     
26399     setThumbBoxSize : function()
26400     {
26401         var width, height;
26402         
26403         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26404             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26405             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26406             
26407             this.minWidth = width;
26408             this.minHeight = height;
26409             
26410             if(this.rotate == 90 || this.rotate == 270){
26411                 this.minWidth = height;
26412                 this.minHeight = width;
26413             }
26414         }
26415         
26416         height = 300;
26417         width = Math.ceil(this.minWidth * height / this.minHeight);
26418         
26419         if(this.minWidth > this.minHeight){
26420             width = 300;
26421             height = Math.ceil(this.minHeight * width / this.minWidth);
26422         }
26423         
26424         this.thumbEl.setStyle({
26425             width : width + 'px',
26426             height : height + 'px'
26427         });
26428
26429         return;
26430             
26431     },
26432     
26433     setThumbBoxPosition : function()
26434     {
26435         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26436         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26437         
26438         this.thumbEl.setLeft(x);
26439         this.thumbEl.setTop(y);
26440         
26441     },
26442     
26443     baseRotateLevel : function()
26444     {
26445         this.baseRotate = 1;
26446         
26447         if(
26448                 typeof(this.exif) != 'undefined' &&
26449                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26450                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26451         ){
26452             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26453         }
26454         
26455         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26456         
26457     },
26458     
26459     baseScaleLevel : function()
26460     {
26461         var width, height;
26462         
26463         if(this.isDocument){
26464             
26465             if(this.baseRotate == 6 || this.baseRotate == 8){
26466             
26467                 height = this.thumbEl.getHeight();
26468                 this.baseScale = height / this.imageEl.OriginWidth;
26469
26470                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26471                     width = this.thumbEl.getWidth();
26472                     this.baseScale = width / this.imageEl.OriginHeight;
26473                 }
26474
26475                 return;
26476             }
26477
26478             height = this.thumbEl.getHeight();
26479             this.baseScale = height / this.imageEl.OriginHeight;
26480
26481             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26482                 width = this.thumbEl.getWidth();
26483                 this.baseScale = width / this.imageEl.OriginWidth;
26484             }
26485
26486             return;
26487         }
26488         
26489         if(this.baseRotate == 6 || this.baseRotate == 8){
26490             
26491             width = this.thumbEl.getHeight();
26492             this.baseScale = width / this.imageEl.OriginHeight;
26493             
26494             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26495                 height = this.thumbEl.getWidth();
26496                 this.baseScale = height / this.imageEl.OriginHeight;
26497             }
26498             
26499             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26500                 height = this.thumbEl.getWidth();
26501                 this.baseScale = height / this.imageEl.OriginHeight;
26502                 
26503                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26504                     width = this.thumbEl.getHeight();
26505                     this.baseScale = width / this.imageEl.OriginWidth;
26506                 }
26507             }
26508             
26509             return;
26510         }
26511         
26512         width = this.thumbEl.getWidth();
26513         this.baseScale = width / this.imageEl.OriginWidth;
26514         
26515         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26516             height = this.thumbEl.getHeight();
26517             this.baseScale = height / this.imageEl.OriginHeight;
26518         }
26519         
26520         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26521             
26522             height = this.thumbEl.getHeight();
26523             this.baseScale = height / this.imageEl.OriginHeight;
26524             
26525             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26526                 width = this.thumbEl.getWidth();
26527                 this.baseScale = width / this.imageEl.OriginWidth;
26528             }
26529             
26530         }
26531         
26532         return;
26533     },
26534     
26535     getScaleLevel : function()
26536     {
26537         return this.baseScale * Math.pow(1.1, this.scale);
26538     },
26539     
26540     onTouchStart : function(e)
26541     {
26542         if(!this.canvasLoaded){
26543             this.beforeSelectFile(e);
26544             return;
26545         }
26546         
26547         var touches = e.browserEvent.touches;
26548         
26549         if(!touches){
26550             return;
26551         }
26552         
26553         if(touches.length == 1){
26554             this.onMouseDown(e);
26555             return;
26556         }
26557         
26558         if(touches.length != 2){
26559             return;
26560         }
26561         
26562         var coords = [];
26563         
26564         for(var i = 0, finger; finger = touches[i]; i++){
26565             coords.push(finger.pageX, finger.pageY);
26566         }
26567         
26568         var x = Math.pow(coords[0] - coords[2], 2);
26569         var y = Math.pow(coords[1] - coords[3], 2);
26570         
26571         this.startDistance = Math.sqrt(x + y);
26572         
26573         this.startScale = this.scale;
26574         
26575         this.pinching = true;
26576         this.dragable = false;
26577         
26578     },
26579     
26580     onTouchMove : function(e)
26581     {
26582         if(!this.pinching && !this.dragable){
26583             return;
26584         }
26585         
26586         var touches = e.browserEvent.touches;
26587         
26588         if(!touches){
26589             return;
26590         }
26591         
26592         if(this.dragable){
26593             this.onMouseMove(e);
26594             return;
26595         }
26596         
26597         var coords = [];
26598         
26599         for(var i = 0, finger; finger = touches[i]; i++){
26600             coords.push(finger.pageX, finger.pageY);
26601         }
26602         
26603         var x = Math.pow(coords[0] - coords[2], 2);
26604         var y = Math.pow(coords[1] - coords[3], 2);
26605         
26606         this.endDistance = Math.sqrt(x + y);
26607         
26608         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26609         
26610         if(!this.zoomable()){
26611             this.scale = this.startScale;
26612             return;
26613         }
26614         
26615         this.draw();
26616         
26617     },
26618     
26619     onTouchEnd : function(e)
26620     {
26621         this.pinching = false;
26622         this.dragable = false;
26623         
26624     },
26625     
26626     process : function(file, crop)
26627     {
26628         if(this.loadMask){
26629             this.maskEl.mask(this.loadingText);
26630         }
26631         
26632         this.xhr = new XMLHttpRequest();
26633         
26634         file.xhr = this.xhr;
26635
26636         this.xhr.open(this.method, this.url, true);
26637         
26638         var headers = {
26639             "Accept": "application/json",
26640             "Cache-Control": "no-cache",
26641             "X-Requested-With": "XMLHttpRequest"
26642         };
26643         
26644         for (var headerName in headers) {
26645             var headerValue = headers[headerName];
26646             if (headerValue) {
26647                 this.xhr.setRequestHeader(headerName, headerValue);
26648             }
26649         }
26650         
26651         var _this = this;
26652         
26653         this.xhr.onload = function()
26654         {
26655             _this.xhrOnLoad(_this.xhr);
26656         }
26657         
26658         this.xhr.onerror = function()
26659         {
26660             _this.xhrOnError(_this.xhr);
26661         }
26662         
26663         var formData = new FormData();
26664
26665         formData.append('returnHTML', 'NO');
26666         
26667         if(crop){
26668             formData.append('crop', crop);
26669         }
26670         
26671         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26672             formData.append(this.paramName, file, file.name);
26673         }
26674         
26675         if(typeof(file.filename) != 'undefined'){
26676             formData.append('filename', file.filename);
26677         }
26678         
26679         if(typeof(file.mimetype) != 'undefined'){
26680             formData.append('mimetype', file.mimetype);
26681         }
26682         
26683         if(this.fireEvent('arrange', this, formData) != false){
26684             this.xhr.send(formData);
26685         };
26686     },
26687     
26688     xhrOnLoad : function(xhr)
26689     {
26690         if(this.loadMask){
26691             this.maskEl.unmask();
26692         }
26693         
26694         if (xhr.readyState !== 4) {
26695             this.fireEvent('exception', this, xhr);
26696             return;
26697         }
26698
26699         var response = Roo.decode(xhr.responseText);
26700         
26701         if(!response.success){
26702             this.fireEvent('exception', this, xhr);
26703             return;
26704         }
26705         
26706         var response = Roo.decode(xhr.responseText);
26707         
26708         this.fireEvent('upload', this, response);
26709         
26710     },
26711     
26712     xhrOnError : function()
26713     {
26714         if(this.loadMask){
26715             this.maskEl.unmask();
26716         }
26717         
26718         Roo.log('xhr on error');
26719         
26720         var response = Roo.decode(xhr.responseText);
26721           
26722         Roo.log(response);
26723         
26724     },
26725     
26726     prepare : function(file)
26727     {   
26728         if(this.loadMask){
26729             this.maskEl.mask(this.loadingText);
26730         }
26731         
26732         this.file = false;
26733         this.exif = {};
26734         
26735         if(typeof(file) === 'string'){
26736             this.loadCanvas(file);
26737             return;
26738         }
26739         
26740         if(!file || !this.urlAPI){
26741             return;
26742         }
26743         
26744         this.file = file;
26745         this.cropType = file.type;
26746         
26747         var _this = this;
26748         
26749         if(this.fireEvent('prepare', this, this.file) != false){
26750             
26751             var reader = new FileReader();
26752             
26753             reader.onload = function (e) {
26754                 if (e.target.error) {
26755                     Roo.log(e.target.error);
26756                     return;
26757                 }
26758                 
26759                 var buffer = e.target.result,
26760                     dataView = new DataView(buffer),
26761                     offset = 2,
26762                     maxOffset = dataView.byteLength - 4,
26763                     markerBytes,
26764                     markerLength;
26765                 
26766                 if (dataView.getUint16(0) === 0xffd8) {
26767                     while (offset < maxOffset) {
26768                         markerBytes = dataView.getUint16(offset);
26769                         
26770                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26771                             markerLength = dataView.getUint16(offset + 2) + 2;
26772                             if (offset + markerLength > dataView.byteLength) {
26773                                 Roo.log('Invalid meta data: Invalid segment size.');
26774                                 break;
26775                             }
26776                             
26777                             if(markerBytes == 0xffe1){
26778                                 _this.parseExifData(
26779                                     dataView,
26780                                     offset,
26781                                     markerLength
26782                                 );
26783                             }
26784                             
26785                             offset += markerLength;
26786                             
26787                             continue;
26788                         }
26789                         
26790                         break;
26791                     }
26792                     
26793                 }
26794                 
26795                 var url = _this.urlAPI.createObjectURL(_this.file);
26796                 
26797                 _this.loadCanvas(url);
26798                 
26799                 return;
26800             }
26801             
26802             reader.readAsArrayBuffer(this.file);
26803             
26804         }
26805         
26806     },
26807     
26808     parseExifData : function(dataView, offset, length)
26809     {
26810         var tiffOffset = offset + 10,
26811             littleEndian,
26812             dirOffset;
26813     
26814         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26815             // No Exif data, might be XMP data instead
26816             return;
26817         }
26818         
26819         // Check for the ASCII code for "Exif" (0x45786966):
26820         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26821             // No Exif data, might be XMP data instead
26822             return;
26823         }
26824         if (tiffOffset + 8 > dataView.byteLength) {
26825             Roo.log('Invalid Exif data: Invalid segment size.');
26826             return;
26827         }
26828         // Check for the two null bytes:
26829         if (dataView.getUint16(offset + 8) !== 0x0000) {
26830             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26831             return;
26832         }
26833         // Check the byte alignment:
26834         switch (dataView.getUint16(tiffOffset)) {
26835         case 0x4949:
26836             littleEndian = true;
26837             break;
26838         case 0x4D4D:
26839             littleEndian = false;
26840             break;
26841         default:
26842             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26843             return;
26844         }
26845         // Check for the TIFF tag marker (0x002A):
26846         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26847             Roo.log('Invalid Exif data: Missing TIFF marker.');
26848             return;
26849         }
26850         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26851         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26852         
26853         this.parseExifTags(
26854             dataView,
26855             tiffOffset,
26856             tiffOffset + dirOffset,
26857             littleEndian
26858         );
26859     },
26860     
26861     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26862     {
26863         var tagsNumber,
26864             dirEndOffset,
26865             i;
26866         if (dirOffset + 6 > dataView.byteLength) {
26867             Roo.log('Invalid Exif data: Invalid directory offset.');
26868             return;
26869         }
26870         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26871         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26872         if (dirEndOffset + 4 > dataView.byteLength) {
26873             Roo.log('Invalid Exif data: Invalid directory size.');
26874             return;
26875         }
26876         for (i = 0; i < tagsNumber; i += 1) {
26877             this.parseExifTag(
26878                 dataView,
26879                 tiffOffset,
26880                 dirOffset + 2 + 12 * i, // tag offset
26881                 littleEndian
26882             );
26883         }
26884         // Return the offset to the next directory:
26885         return dataView.getUint32(dirEndOffset, littleEndian);
26886     },
26887     
26888     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26889     {
26890         var tag = dataView.getUint16(offset, littleEndian);
26891         
26892         this.exif[tag] = this.getExifValue(
26893             dataView,
26894             tiffOffset,
26895             offset,
26896             dataView.getUint16(offset + 2, littleEndian), // tag type
26897             dataView.getUint32(offset + 4, littleEndian), // tag length
26898             littleEndian
26899         );
26900     },
26901     
26902     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26903     {
26904         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26905             tagSize,
26906             dataOffset,
26907             values,
26908             i,
26909             str,
26910             c;
26911     
26912         if (!tagType) {
26913             Roo.log('Invalid Exif data: Invalid tag type.');
26914             return;
26915         }
26916         
26917         tagSize = tagType.size * length;
26918         // Determine if the value is contained in the dataOffset bytes,
26919         // or if the value at the dataOffset is a pointer to the actual data:
26920         dataOffset = tagSize > 4 ?
26921                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26922         if (dataOffset + tagSize > dataView.byteLength) {
26923             Roo.log('Invalid Exif data: Invalid data offset.');
26924             return;
26925         }
26926         if (length === 1) {
26927             return tagType.getValue(dataView, dataOffset, littleEndian);
26928         }
26929         values = [];
26930         for (i = 0; i < length; i += 1) {
26931             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26932         }
26933         
26934         if (tagType.ascii) {
26935             str = '';
26936             // Concatenate the chars:
26937             for (i = 0; i < values.length; i += 1) {
26938                 c = values[i];
26939                 // Ignore the terminating NULL byte(s):
26940                 if (c === '\u0000') {
26941                     break;
26942                 }
26943                 str += c;
26944             }
26945             return str;
26946         }
26947         return values;
26948     }
26949     
26950 });
26951
26952 Roo.apply(Roo.bootstrap.UploadCropbox, {
26953     tags : {
26954         'Orientation': 0x0112
26955     },
26956     
26957     Orientation: {
26958             1: 0, //'top-left',
26959 //            2: 'top-right',
26960             3: 180, //'bottom-right',
26961 //            4: 'bottom-left',
26962 //            5: 'left-top',
26963             6: 90, //'right-top',
26964 //            7: 'right-bottom',
26965             8: 270 //'left-bottom'
26966     },
26967     
26968     exifTagTypes : {
26969         // byte, 8-bit unsigned int:
26970         1: {
26971             getValue: function (dataView, dataOffset) {
26972                 return dataView.getUint8(dataOffset);
26973             },
26974             size: 1
26975         },
26976         // ascii, 8-bit byte:
26977         2: {
26978             getValue: function (dataView, dataOffset) {
26979                 return String.fromCharCode(dataView.getUint8(dataOffset));
26980             },
26981             size: 1,
26982             ascii: true
26983         },
26984         // short, 16 bit int:
26985         3: {
26986             getValue: function (dataView, dataOffset, littleEndian) {
26987                 return dataView.getUint16(dataOffset, littleEndian);
26988             },
26989             size: 2
26990         },
26991         // long, 32 bit int:
26992         4: {
26993             getValue: function (dataView, dataOffset, littleEndian) {
26994                 return dataView.getUint32(dataOffset, littleEndian);
26995             },
26996             size: 4
26997         },
26998         // rational = two long values, first is numerator, second is denominator:
26999         5: {
27000             getValue: function (dataView, dataOffset, littleEndian) {
27001                 return dataView.getUint32(dataOffset, littleEndian) /
27002                     dataView.getUint32(dataOffset + 4, littleEndian);
27003             },
27004             size: 8
27005         },
27006         // slong, 32 bit signed int:
27007         9: {
27008             getValue: function (dataView, dataOffset, littleEndian) {
27009                 return dataView.getInt32(dataOffset, littleEndian);
27010             },
27011             size: 4
27012         },
27013         // srational, two slongs, first is numerator, second is denominator:
27014         10: {
27015             getValue: function (dataView, dataOffset, littleEndian) {
27016                 return dataView.getInt32(dataOffset, littleEndian) /
27017                     dataView.getInt32(dataOffset + 4, littleEndian);
27018             },
27019             size: 8
27020         }
27021     },
27022     
27023     footer : {
27024         STANDARD : [
27025             {
27026                 tag : 'div',
27027                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27028                 action : 'rotate-left',
27029                 cn : [
27030                     {
27031                         tag : 'button',
27032                         cls : 'btn btn-default',
27033                         html : '<i class="fa fa-undo"></i>'
27034                     }
27035                 ]
27036             },
27037             {
27038                 tag : 'div',
27039                 cls : 'btn-group roo-upload-cropbox-picture',
27040                 action : 'picture',
27041                 cn : [
27042                     {
27043                         tag : 'button',
27044                         cls : 'btn btn-default',
27045                         html : '<i class="fa fa-picture-o"></i>'
27046                     }
27047                 ]
27048             },
27049             {
27050                 tag : 'div',
27051                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27052                 action : 'rotate-right',
27053                 cn : [
27054                     {
27055                         tag : 'button',
27056                         cls : 'btn btn-default',
27057                         html : '<i class="fa fa-repeat"></i>'
27058                     }
27059                 ]
27060             }
27061         ],
27062         DOCUMENT : [
27063             {
27064                 tag : 'div',
27065                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27066                 action : 'rotate-left',
27067                 cn : [
27068                     {
27069                         tag : 'button',
27070                         cls : 'btn btn-default',
27071                         html : '<i class="fa fa-undo"></i>'
27072                     }
27073                 ]
27074             },
27075             {
27076                 tag : 'div',
27077                 cls : 'btn-group roo-upload-cropbox-download',
27078                 action : 'download',
27079                 cn : [
27080                     {
27081                         tag : 'button',
27082                         cls : 'btn btn-default',
27083                         html : '<i class="fa fa-download"></i>'
27084                     }
27085                 ]
27086             },
27087             {
27088                 tag : 'div',
27089                 cls : 'btn-group roo-upload-cropbox-crop',
27090                 action : 'crop',
27091                 cn : [
27092                     {
27093                         tag : 'button',
27094                         cls : 'btn btn-default',
27095                         html : '<i class="fa fa-crop"></i>'
27096                     }
27097                 ]
27098             },
27099             {
27100                 tag : 'div',
27101                 cls : 'btn-group roo-upload-cropbox-trash',
27102                 action : 'trash',
27103                 cn : [
27104                     {
27105                         tag : 'button',
27106                         cls : 'btn btn-default',
27107                         html : '<i class="fa fa-trash"></i>'
27108                     }
27109                 ]
27110             },
27111             {
27112                 tag : 'div',
27113                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27114                 action : 'rotate-right',
27115                 cn : [
27116                     {
27117                         tag : 'button',
27118                         cls : 'btn btn-default',
27119                         html : '<i class="fa fa-repeat"></i>'
27120                     }
27121                 ]
27122             }
27123         ],
27124         ROTATOR : [
27125             {
27126                 tag : 'div',
27127                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27128                 action : 'rotate-left',
27129                 cn : [
27130                     {
27131                         tag : 'button',
27132                         cls : 'btn btn-default',
27133                         html : '<i class="fa fa-undo"></i>'
27134                     }
27135                 ]
27136             },
27137             {
27138                 tag : 'div',
27139                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27140                 action : 'rotate-right',
27141                 cn : [
27142                     {
27143                         tag : 'button',
27144                         cls : 'btn btn-default',
27145                         html : '<i class="fa fa-repeat"></i>'
27146                     }
27147                 ]
27148             }
27149         ]
27150     }
27151 });
27152
27153 /*
27154 * Licence: LGPL
27155 */
27156
27157 /**
27158  * @class Roo.bootstrap.DocumentManager
27159  * @extends Roo.bootstrap.Component
27160  * Bootstrap DocumentManager class
27161  * @cfg {String} paramName default 'imageUpload'
27162  * @cfg {String} method default POST
27163  * @cfg {String} url action url
27164  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27165  * @cfg {Boolean} multiple multiple upload default true
27166  * @cfg {Number} thumbSize default 300
27167  * @cfg {String} fieldLabel
27168  * @cfg {Number} labelWidth default 4
27169  * @cfg {String} labelAlign (left|top) default left
27170  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27171  * 
27172  * @constructor
27173  * Create a new DocumentManager
27174  * @param {Object} config The config object
27175  */
27176
27177 Roo.bootstrap.DocumentManager = function(config){
27178     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27179     
27180     this.addEvents({
27181         /**
27182          * @event initial
27183          * Fire when initial the DocumentManager
27184          * @param {Roo.bootstrap.DocumentManager} this
27185          */
27186         "initial" : true,
27187         /**
27188          * @event inspect
27189          * inspect selected file
27190          * @param {Roo.bootstrap.DocumentManager} this
27191          * @param {File} file
27192          */
27193         "inspect" : true,
27194         /**
27195          * @event exception
27196          * Fire when xhr load exception
27197          * @param {Roo.bootstrap.DocumentManager} this
27198          * @param {XMLHttpRequest} xhr
27199          */
27200         "exception" : true,
27201         /**
27202          * @event prepare
27203          * prepare the form data
27204          * @param {Roo.bootstrap.DocumentManager} this
27205          * @param {Object} formData
27206          */
27207         "prepare" : true,
27208         /**
27209          * @event remove
27210          * Fire when remove the file
27211          * @param {Roo.bootstrap.DocumentManager} this
27212          * @param {Object} file
27213          */
27214         "remove" : true,
27215         /**
27216          * @event refresh
27217          * Fire after refresh the file
27218          * @param {Roo.bootstrap.DocumentManager} this
27219          */
27220         "refresh" : true,
27221         /**
27222          * @event click
27223          * Fire after click the image
27224          * @param {Roo.bootstrap.DocumentManager} this
27225          * @param {Object} file
27226          */
27227         "click" : true,
27228         /**
27229          * @event edit
27230          * Fire when upload a image and editable set to true
27231          * @param {Roo.bootstrap.DocumentManager} this
27232          * @param {Object} file
27233          */
27234         "edit" : true,
27235         /**
27236          * @event beforeselectfile
27237          * Fire before select file
27238          * @param {Roo.bootstrap.DocumentManager} this
27239          */
27240         "beforeselectfile" : true,
27241         /**
27242          * @event process
27243          * Fire before process file
27244          * @param {Roo.bootstrap.DocumentManager} this
27245          * @param {Object} file
27246          */
27247         "process" : true
27248         
27249     });
27250 };
27251
27252 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27253     
27254     boxes : 0,
27255     inputName : '',
27256     thumbSize : 300,
27257     multiple : true,
27258     files : [],
27259     method : 'POST',
27260     url : '',
27261     paramName : 'imageUpload',
27262     fieldLabel : '',
27263     labelWidth : 4,
27264     labelAlign : 'left',
27265     editable : true,
27266     delegates : [],
27267     
27268     
27269     xhr : false, 
27270     
27271     getAutoCreate : function()
27272     {   
27273         var managerWidget = {
27274             tag : 'div',
27275             cls : 'roo-document-manager',
27276             cn : [
27277                 {
27278                     tag : 'input',
27279                     cls : 'roo-document-manager-selector',
27280                     type : 'file'
27281                 },
27282                 {
27283                     tag : 'div',
27284                     cls : 'roo-document-manager-uploader',
27285                     cn : [
27286                         {
27287                             tag : 'div',
27288                             cls : 'roo-document-manager-upload-btn',
27289                             html : '<i class="fa fa-plus"></i>'
27290                         }
27291                     ]
27292                     
27293                 }
27294             ]
27295         };
27296         
27297         var content = [
27298             {
27299                 tag : 'div',
27300                 cls : 'column col-md-12',
27301                 cn : managerWidget
27302             }
27303         ];
27304         
27305         if(this.fieldLabel.length){
27306             
27307             content = [
27308                 {
27309                     tag : 'div',
27310                     cls : 'column col-md-12',
27311                     html : this.fieldLabel
27312                 },
27313                 {
27314                     tag : 'div',
27315                     cls : 'column col-md-12',
27316                     cn : managerWidget
27317                 }
27318             ];
27319
27320             if(this.labelAlign == 'left'){
27321                 content = [
27322                     {
27323                         tag : 'div',
27324                         cls : 'column col-md-' + this.labelWidth,
27325                         html : this.fieldLabel
27326                     },
27327                     {
27328                         tag : 'div',
27329                         cls : 'column col-md-' + (12 - this.labelWidth),
27330                         cn : managerWidget
27331                     }
27332                 ];
27333                 
27334             }
27335         }
27336         
27337         var cfg = {
27338             tag : 'div',
27339             cls : 'row clearfix',
27340             cn : content
27341         };
27342         
27343         return cfg;
27344         
27345     },
27346     
27347     initEvents : function()
27348     {
27349         this.managerEl = this.el.select('.roo-document-manager', true).first();
27350         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27351         
27352         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27353         this.selectorEl.hide();
27354         
27355         if(this.multiple){
27356             this.selectorEl.attr('multiple', 'multiple');
27357         }
27358         
27359         this.selectorEl.on('change', this.onFileSelected, this);
27360         
27361         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27362         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27363         
27364         this.uploader.on('click', this.onUploaderClick, this);
27365         
27366         this.renderProgressDialog();
27367         
27368         var _this = this;
27369         
27370         window.addEventListener("resize", function() { _this.refresh(); } );
27371         
27372         this.fireEvent('initial', this);
27373     },
27374     
27375     renderProgressDialog : function()
27376     {
27377         var _this = this;
27378         
27379         this.progressDialog = new Roo.bootstrap.Modal({
27380             cls : 'roo-document-manager-progress-dialog',
27381             allow_close : false,
27382             title : '',
27383             buttons : [
27384                 {
27385                     name  :'cancel',
27386                     weight : 'danger',
27387                     html : 'Cancel'
27388                 }
27389             ], 
27390             listeners : { 
27391                 btnclick : function() {
27392                     _this.uploadCancel();
27393                     this.hide();
27394                 }
27395             }
27396         });
27397          
27398         this.progressDialog.render(Roo.get(document.body));
27399          
27400         this.progress = new Roo.bootstrap.Progress({
27401             cls : 'roo-document-manager-progress',
27402             active : true,
27403             striped : true
27404         });
27405         
27406         this.progress.render(this.progressDialog.getChildContainer());
27407         
27408         this.progressBar = new Roo.bootstrap.ProgressBar({
27409             cls : 'roo-document-manager-progress-bar',
27410             aria_valuenow : 0,
27411             aria_valuemin : 0,
27412             aria_valuemax : 12,
27413             panel : 'success'
27414         });
27415         
27416         this.progressBar.render(this.progress.getChildContainer());
27417     },
27418     
27419     onUploaderClick : function(e)
27420     {
27421         e.preventDefault();
27422      
27423         if(this.fireEvent('beforeselectfile', this) != false){
27424             this.selectorEl.dom.click();
27425         }
27426         
27427     },
27428     
27429     onFileSelected : function(e)
27430     {
27431         e.preventDefault();
27432         
27433         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27434             return;
27435         }
27436         
27437         Roo.each(this.selectorEl.dom.files, function(file){
27438             if(this.fireEvent('inspect', this, file) != false){
27439                 this.files.push(file);
27440             }
27441         }, this);
27442         
27443         this.queue();
27444         
27445     },
27446     
27447     queue : function()
27448     {
27449         this.selectorEl.dom.value = '';
27450         
27451         if(!this.files.length){
27452             return;
27453         }
27454         
27455         if(this.boxes > 0 && this.files.length > this.boxes){
27456             this.files = this.files.slice(0, this.boxes);
27457         }
27458         
27459         this.uploader.show();
27460         
27461         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27462             this.uploader.hide();
27463         }
27464         
27465         var _this = this;
27466         
27467         var files = [];
27468         
27469         var docs = [];
27470         
27471         Roo.each(this.files, function(file){
27472             
27473             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27474                 var f = this.renderPreview(file);
27475                 files.push(f);
27476                 return;
27477             }
27478             
27479             if(file.type.indexOf('image') != -1){
27480                 this.delegates.push(
27481                     (function(){
27482                         _this.process(file);
27483                     }).createDelegate(this)
27484                 );
27485         
27486                 return;
27487             }
27488             
27489             docs.push(
27490                 (function(){
27491                     _this.process(file);
27492                 }).createDelegate(this)
27493             );
27494             
27495         }, this);
27496         
27497         this.files = files;
27498         
27499         this.delegates = this.delegates.concat(docs);
27500         
27501         if(!this.delegates.length){
27502             this.refresh();
27503             return;
27504         }
27505         
27506         this.progressBar.aria_valuemax = this.delegates.length;
27507         
27508         this.arrange();
27509         
27510         return;
27511     },
27512     
27513     arrange : function()
27514     {
27515         if(!this.delegates.length){
27516             this.progressDialog.hide();
27517             this.refresh();
27518             return;
27519         }
27520         
27521         var delegate = this.delegates.shift();
27522         
27523         this.progressDialog.show();
27524         
27525         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27526         
27527         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27528         
27529         delegate();
27530     },
27531     
27532     refresh : function()
27533     {
27534         this.uploader.show();
27535         
27536         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27537             this.uploader.hide();
27538         }
27539         
27540         Roo.isTouch ? this.closable(false) : this.closable(true);
27541         
27542         this.fireEvent('refresh', this);
27543     },
27544     
27545     onRemove : function(e, el, o)
27546     {
27547         e.preventDefault();
27548         
27549         this.fireEvent('remove', this, o);
27550         
27551     },
27552     
27553     remove : function(o)
27554     {
27555         var files = [];
27556         
27557         Roo.each(this.files, function(file){
27558             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27559                 files.push(file);
27560                 return;
27561             }
27562
27563             o.target.remove();
27564
27565         }, this);
27566         
27567         this.files = files;
27568         
27569         this.refresh();
27570     },
27571     
27572     clear : function()
27573     {
27574         Roo.each(this.files, function(file){
27575             if(!file.target){
27576                 return;
27577             }
27578             
27579             file.target.remove();
27580
27581         }, this);
27582         
27583         this.files = [];
27584         
27585         this.refresh();
27586     },
27587     
27588     onClick : function(e, el, o)
27589     {
27590         e.preventDefault();
27591         
27592         this.fireEvent('click', this, o);
27593         
27594     },
27595     
27596     closable : function(closable)
27597     {
27598         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27599             
27600             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27601             
27602             if(closable){
27603                 el.show();
27604                 return;
27605             }
27606             
27607             el.hide();
27608             
27609         }, this);
27610     },
27611     
27612     xhrOnLoad : function(xhr)
27613     {
27614         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27615             el.remove();
27616         }, this);
27617         
27618         if (xhr.readyState !== 4) {
27619             this.arrange();
27620             this.fireEvent('exception', this, xhr);
27621             return;
27622         }
27623
27624         var response = Roo.decode(xhr.responseText);
27625         
27626         if(!response.success){
27627             this.arrange();
27628             this.fireEvent('exception', this, xhr);
27629             return;
27630         }
27631         
27632         var file = this.renderPreview(response.data);
27633         
27634         this.files.push(file);
27635         
27636         this.arrange();
27637         
27638     },
27639     
27640     xhrOnError : function(xhr)
27641     {
27642         Roo.log('xhr on error');
27643         
27644         var response = Roo.decode(xhr.responseText);
27645           
27646         Roo.log(response);
27647         
27648         this.arrange();
27649     },
27650     
27651     process : function(file)
27652     {
27653         if(this.fireEvent('process', this, file) !== false){
27654             if(this.editable && file.type.indexOf('image') != -1){
27655                 this.fireEvent('edit', this, file);
27656                 return;
27657             }
27658
27659             this.uploadStart(file, false);
27660
27661             return;
27662         }
27663         
27664     },
27665     
27666     uploadStart : function(file, crop)
27667     {
27668         this.xhr = new XMLHttpRequest();
27669         
27670         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27671             this.arrange();
27672             return;
27673         }
27674         
27675         file.xhr = this.xhr;
27676             
27677         this.managerEl.createChild({
27678             tag : 'div',
27679             cls : 'roo-document-manager-loading',
27680             cn : [
27681                 {
27682                     tag : 'div',
27683                     tooltip : file.name,
27684                     cls : 'roo-document-manager-thumb',
27685                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27686                 }
27687             ]
27688
27689         });
27690
27691         this.xhr.open(this.method, this.url, true);
27692         
27693         var headers = {
27694             "Accept": "application/json",
27695             "Cache-Control": "no-cache",
27696             "X-Requested-With": "XMLHttpRequest"
27697         };
27698         
27699         for (var headerName in headers) {
27700             var headerValue = headers[headerName];
27701             if (headerValue) {
27702                 this.xhr.setRequestHeader(headerName, headerValue);
27703             }
27704         }
27705         
27706         var _this = this;
27707         
27708         this.xhr.onload = function()
27709         {
27710             _this.xhrOnLoad(_this.xhr);
27711         }
27712         
27713         this.xhr.onerror = function()
27714         {
27715             _this.xhrOnError(_this.xhr);
27716         }
27717         
27718         var formData = new FormData();
27719
27720         formData.append('returnHTML', 'NO');
27721         
27722         if(crop){
27723             formData.append('crop', crop);
27724         }
27725         
27726         formData.append(this.paramName, file, file.name);
27727         
27728         if(this.fireEvent('prepare', this, formData) != false){
27729             this.xhr.send(formData);
27730         };
27731     },
27732     
27733     uploadCancel : function()
27734     {
27735         if (this.xhr) {
27736             this.xhr.abort();
27737         }
27738         
27739         
27740         this.delegates = [];
27741         
27742         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27743             el.remove();
27744         }, this);
27745         
27746         this.arrange();
27747     },
27748     
27749     renderPreview : function(file)
27750     {
27751         if(typeof(file.target) != 'undefined' && file.target){
27752             return file;
27753         }
27754         
27755         var previewEl = this.managerEl.createChild({
27756             tag : 'div',
27757             cls : 'roo-document-manager-preview',
27758             cn : [
27759                 {
27760                     tag : 'div',
27761                     tooltip : file.filename,
27762                     cls : 'roo-document-manager-thumb',
27763                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27764                 },
27765                 {
27766                     tag : 'button',
27767                     cls : 'close',
27768                     html : '<i class="fa fa-times-circle"></i>'
27769                 }
27770             ]
27771         });
27772
27773         var close = previewEl.select('button.close', true).first();
27774
27775         close.on('click', this.onRemove, this, file);
27776
27777         file.target = previewEl;
27778
27779         var image = previewEl.select('img', true).first();
27780         
27781         var _this = this;
27782         
27783         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27784         
27785         image.on('click', this.onClick, this, file);
27786         
27787         return file;
27788         
27789     },
27790     
27791     onPreviewLoad : function(file, image)
27792     {
27793         if(typeof(file.target) == 'undefined' || !file.target){
27794             return;
27795         }
27796         
27797         var width = image.dom.naturalWidth || image.dom.width;
27798         var height = image.dom.naturalHeight || image.dom.height;
27799         
27800         if(width > height){
27801             file.target.addClass('wide');
27802             return;
27803         }
27804         
27805         file.target.addClass('tall');
27806         return;
27807         
27808     },
27809     
27810     uploadFromSource : function(file, crop)
27811     {
27812         this.xhr = new XMLHttpRequest();
27813         
27814         this.managerEl.createChild({
27815             tag : 'div',
27816             cls : 'roo-document-manager-loading',
27817             cn : [
27818                 {
27819                     tag : 'div',
27820                     tooltip : file.name,
27821                     cls : 'roo-document-manager-thumb',
27822                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27823                 }
27824             ]
27825
27826         });
27827
27828         this.xhr.open(this.method, this.url, true);
27829         
27830         var headers = {
27831             "Accept": "application/json",
27832             "Cache-Control": "no-cache",
27833             "X-Requested-With": "XMLHttpRequest"
27834         };
27835         
27836         for (var headerName in headers) {
27837             var headerValue = headers[headerName];
27838             if (headerValue) {
27839                 this.xhr.setRequestHeader(headerName, headerValue);
27840             }
27841         }
27842         
27843         var _this = this;
27844         
27845         this.xhr.onload = function()
27846         {
27847             _this.xhrOnLoad(_this.xhr);
27848         }
27849         
27850         this.xhr.onerror = function()
27851         {
27852             _this.xhrOnError(_this.xhr);
27853         }
27854         
27855         var formData = new FormData();
27856
27857         formData.append('returnHTML', 'NO');
27858         
27859         formData.append('crop', crop);
27860         
27861         if(typeof(file.filename) != 'undefined'){
27862             formData.append('filename', file.filename);
27863         }
27864         
27865         if(typeof(file.mimetype) != 'undefined'){
27866             formData.append('mimetype', file.mimetype);
27867         }
27868         
27869         if(this.fireEvent('prepare', this, formData) != false){
27870             this.xhr.send(formData);
27871         };
27872     }
27873 });
27874
27875 /*
27876 * Licence: LGPL
27877 */
27878
27879 /**
27880  * @class Roo.bootstrap.DocumentViewer
27881  * @extends Roo.bootstrap.Component
27882  * Bootstrap DocumentViewer class
27883  * 
27884  * @constructor
27885  * Create a new DocumentViewer
27886  * @param {Object} config The config object
27887  */
27888
27889 Roo.bootstrap.DocumentViewer = function(config){
27890     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27891     
27892     this.addEvents({
27893         /**
27894          * @event initial
27895          * Fire after initEvent
27896          * @param {Roo.bootstrap.DocumentViewer} this
27897          */
27898         "initial" : true,
27899         /**
27900          * @event click
27901          * Fire after click
27902          * @param {Roo.bootstrap.DocumentViewer} this
27903          */
27904         "click" : true,
27905         /**
27906          * @event trash
27907          * Fire after trash button
27908          * @param {Roo.bootstrap.DocumentViewer} this
27909          */
27910         "trash" : true
27911         
27912     });
27913 };
27914
27915 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27916     
27917     getAutoCreate : function()
27918     {
27919         var cfg = {
27920             tag : 'div',
27921             cls : 'roo-document-viewer',
27922             cn : [
27923                 {
27924                     tag : 'div',
27925                     cls : 'roo-document-viewer-body',
27926                     cn : [
27927                         {
27928                             tag : 'div',
27929                             cls : 'roo-document-viewer-thumb',
27930                             cn : [
27931                                 {
27932                                     tag : 'img',
27933                                     cls : 'roo-document-viewer-image'
27934                                 }
27935                             ]
27936                         }
27937                     ]
27938                 },
27939                 {
27940                     tag : 'div',
27941                     cls : 'roo-document-viewer-footer',
27942                     cn : {
27943                         tag : 'div',
27944                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27945                         cn : [
27946                             {
27947                                 tag : 'div',
27948                                 cls : 'btn-group',
27949                                 cn : [
27950                                     {
27951                                         tag : 'button',
27952                                         cls : 'btn btn-default roo-document-viewer-trash',
27953                                         html : '<i class="fa fa-trash"></i>'
27954                                     }
27955                                 ]
27956                             }
27957                         ]
27958                     }
27959                 }
27960             ]
27961         };
27962         
27963         return cfg;
27964     },
27965     
27966     initEvents : function()
27967     {
27968         
27969         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27970         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27971         
27972         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27973         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27974         
27975         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27976         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27977         
27978         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27979         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27980         
27981         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27982         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27983         
27984         this.bodyEl.on('click', this.onClick, this);
27985         
27986         this.trashBtn.on('click', this.onTrash, this);
27987         
27988     },
27989     
27990     initial : function()
27991     {
27992 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27993         
27994         
27995         this.fireEvent('initial', this);
27996         
27997     },
27998     
27999     onClick : function(e)
28000     {
28001         e.preventDefault();
28002         
28003         this.fireEvent('click', this);
28004     },
28005     
28006     onTrash : function(e)
28007     {
28008         e.preventDefault();
28009         
28010         this.fireEvent('trash', this);
28011     }
28012     
28013 });
28014 /*
28015  * - LGPL
28016  *
28017  * nav progress bar
28018  * 
28019  */
28020
28021 /**
28022  * @class Roo.bootstrap.NavProgressBar
28023  * @extends Roo.bootstrap.Component
28024  * Bootstrap NavProgressBar class
28025  * 
28026  * @constructor
28027  * Create a new nav progress bar
28028  * @param {Object} config The config object
28029  */
28030
28031 Roo.bootstrap.NavProgressBar = function(config){
28032     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28033
28034     this.bullets = this.bullets || [];
28035    
28036 //    Roo.bootstrap.NavProgressBar.register(this);
28037      this.addEvents({
28038         /**
28039              * @event changed
28040              * Fires when the active item changes
28041              * @param {Roo.bootstrap.NavProgressBar} this
28042              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28043              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28044          */
28045         'changed': true
28046      });
28047     
28048 };
28049
28050 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28051     
28052     bullets : [],
28053     barItems : [],
28054     
28055     getAutoCreate : function()
28056     {
28057         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28058         
28059         cfg = {
28060             tag : 'div',
28061             cls : 'roo-navigation-bar-group',
28062             cn : [
28063                 {
28064                     tag : 'div',
28065                     cls : 'roo-navigation-top-bar'
28066                 },
28067                 {
28068                     tag : 'div',
28069                     cls : 'roo-navigation-bullets-bar',
28070                     cn : [
28071                         {
28072                             tag : 'ul',
28073                             cls : 'roo-navigation-bar'
28074                         }
28075                     ]
28076                 },
28077                 
28078                 {
28079                     tag : 'div',
28080                     cls : 'roo-navigation-bottom-bar'
28081                 }
28082             ]
28083             
28084         };
28085         
28086         return cfg;
28087         
28088     },
28089     
28090     initEvents: function() 
28091     {
28092         
28093     },
28094     
28095     onRender : function(ct, position) 
28096     {
28097         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28098         
28099         if(this.bullets.length){
28100             Roo.each(this.bullets, function(b){
28101                this.addItem(b);
28102             }, this);
28103         }
28104         
28105         this.format();
28106         
28107     },
28108     
28109     addItem : function(cfg)
28110     {
28111         var item = new Roo.bootstrap.NavProgressItem(cfg);
28112         
28113         item.parentId = this.id;
28114         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28115         
28116         if(cfg.html){
28117             var top = new Roo.bootstrap.Element({
28118                 tag : 'div',
28119                 cls : 'roo-navigation-bar-text'
28120             });
28121             
28122             var bottom = new Roo.bootstrap.Element({
28123                 tag : 'div',
28124                 cls : 'roo-navigation-bar-text'
28125             });
28126             
28127             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28128             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28129             
28130             var topText = new Roo.bootstrap.Element({
28131                 tag : 'span',
28132                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28133             });
28134             
28135             var bottomText = new Roo.bootstrap.Element({
28136                 tag : 'span',
28137                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28138             });
28139             
28140             topText.onRender(top.el, null);
28141             bottomText.onRender(bottom.el, null);
28142             
28143             item.topEl = top;
28144             item.bottomEl = bottom;
28145         }
28146         
28147         this.barItems.push(item);
28148         
28149         return item;
28150     },
28151     
28152     getActive : function()
28153     {
28154         var active = false;
28155         
28156         Roo.each(this.barItems, function(v){
28157             
28158             if (!v.isActive()) {
28159                 return;
28160             }
28161             
28162             active = v;
28163             return false;
28164             
28165         });
28166         
28167         return active;
28168     },
28169     
28170     setActiveItem : function(item)
28171     {
28172         var prev = false;
28173         
28174         Roo.each(this.barItems, function(v){
28175             if (v.rid == item.rid) {
28176                 return ;
28177             }
28178             
28179             if (v.isActive()) {
28180                 v.setActive(false);
28181                 prev = v;
28182             }
28183         });
28184
28185         item.setActive(true);
28186         
28187         this.fireEvent('changed', this, item, prev);
28188     },
28189     
28190     getBarItem: function(rid)
28191     {
28192         var ret = false;
28193         
28194         Roo.each(this.barItems, function(e) {
28195             if (e.rid != rid) {
28196                 return;
28197             }
28198             
28199             ret =  e;
28200             return false;
28201         });
28202         
28203         return ret;
28204     },
28205     
28206     indexOfItem : function(item)
28207     {
28208         var index = false;
28209         
28210         Roo.each(this.barItems, function(v, i){
28211             
28212             if (v.rid != item.rid) {
28213                 return;
28214             }
28215             
28216             index = i;
28217             return false
28218         });
28219         
28220         return index;
28221     },
28222     
28223     setActiveNext : function()
28224     {
28225         var i = this.indexOfItem(this.getActive());
28226         
28227         if (i > this.barItems.length) {
28228             return;
28229         }
28230         
28231         this.setActiveItem(this.barItems[i+1]);
28232     },
28233     
28234     setActivePrev : function()
28235     {
28236         var i = this.indexOfItem(this.getActive());
28237         
28238         if (i  < 1) {
28239             return;
28240         }
28241         
28242         this.setActiveItem(this.barItems[i-1]);
28243     },
28244     
28245     format : function()
28246     {
28247         if(!this.barItems.length){
28248             return;
28249         }
28250      
28251         var width = 100 / this.barItems.length;
28252         
28253         Roo.each(this.barItems, function(i){
28254             i.el.setStyle('width', width + '%');
28255             i.topEl.el.setStyle('width', width + '%');
28256             i.bottomEl.el.setStyle('width', width + '%');
28257         }, this);
28258         
28259     }
28260     
28261 });
28262 /*
28263  * - LGPL
28264  *
28265  * Nav Progress Item
28266  * 
28267  */
28268
28269 /**
28270  * @class Roo.bootstrap.NavProgressItem
28271  * @extends Roo.bootstrap.Component
28272  * Bootstrap NavProgressItem class
28273  * @cfg {String} rid the reference id
28274  * @cfg {Boolean} active (true|false) Is item active default false
28275  * @cfg {Boolean} disabled (true|false) Is item active default false
28276  * @cfg {String} html
28277  * @cfg {String} position (top|bottom) text position default bottom
28278  * @cfg {String} icon show icon instead of number
28279  * 
28280  * @constructor
28281  * Create a new NavProgressItem
28282  * @param {Object} config The config object
28283  */
28284 Roo.bootstrap.NavProgressItem = function(config){
28285     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28286     this.addEvents({
28287         // raw events
28288         /**
28289          * @event click
28290          * The raw click event for the entire grid.
28291          * @param {Roo.bootstrap.NavProgressItem} this
28292          * @param {Roo.EventObject} e
28293          */
28294         "click" : true
28295     });
28296    
28297 };
28298
28299 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28300     
28301     rid : '',
28302     active : false,
28303     disabled : false,
28304     html : '',
28305     position : 'bottom',
28306     icon : false,
28307     
28308     getAutoCreate : function()
28309     {
28310         var iconCls = 'roo-navigation-bar-item-icon';
28311         
28312         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28313         
28314         var cfg = {
28315             tag: 'li',
28316             cls: 'roo-navigation-bar-item',
28317             cn : [
28318                 {
28319                     tag : 'i',
28320                     cls : iconCls
28321                 }
28322             ]
28323         };
28324         
28325         if(this.active){
28326             cfg.cls += ' active';
28327         }
28328         if(this.disabled){
28329             cfg.cls += ' disabled';
28330         }
28331         
28332         return cfg;
28333     },
28334     
28335     disable : function()
28336     {
28337         this.setDisabled(true);
28338     },
28339     
28340     enable : function()
28341     {
28342         this.setDisabled(false);
28343     },
28344     
28345     initEvents: function() 
28346     {
28347         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28348         
28349         this.iconEl.on('click', this.onClick, this);
28350     },
28351     
28352     onClick : function(e)
28353     {
28354         e.preventDefault();
28355         
28356         if(this.disabled){
28357             return;
28358         }
28359         
28360         if(this.fireEvent('click', this, e) === false){
28361             return;
28362         };
28363         
28364         this.parent().setActiveItem(this);
28365     },
28366     
28367     isActive: function () 
28368     {
28369         return this.active;
28370     },
28371     
28372     setActive : function(state)
28373     {
28374         if(this.active == state){
28375             return;
28376         }
28377         
28378         this.active = state;
28379         
28380         if (state) {
28381             this.el.addClass('active');
28382             return;
28383         }
28384         
28385         this.el.removeClass('active');
28386         
28387         return;
28388     },
28389     
28390     setDisabled : function(state)
28391     {
28392         if(this.disabled == state){
28393             return;
28394         }
28395         
28396         this.disabled = state;
28397         
28398         if (state) {
28399             this.el.addClass('disabled');
28400             return;
28401         }
28402         
28403         this.el.removeClass('disabled');
28404     },
28405     
28406     tooltipEl : function()
28407     {
28408         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28409     }
28410 });
28411  
28412
28413  /*
28414  * - LGPL
28415  *
28416  * FieldLabel
28417  * 
28418  */
28419
28420 /**
28421  * @class Roo.bootstrap.FieldLabel
28422  * @extends Roo.bootstrap.Component
28423  * Bootstrap FieldLabel class
28424  * @cfg {String} html contents of the element
28425  * @cfg {String} tag tag of the element default label
28426  * @cfg {String} cls class of the element
28427  * @cfg {String} target label target 
28428  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28429  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28430  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28431  * @cfg {String} iconTooltip default "This field is required"
28432  * 
28433  * @constructor
28434  * Create a new FieldLabel
28435  * @param {Object} config The config object
28436  */
28437
28438 Roo.bootstrap.FieldLabel = function(config){
28439     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28440     
28441     this.addEvents({
28442             /**
28443              * @event invalid
28444              * Fires after the field has been marked as invalid.
28445              * @param {Roo.form.FieldLabel} this
28446              * @param {String} msg The validation message
28447              */
28448             invalid : true,
28449             /**
28450              * @event valid
28451              * Fires after the field has been validated with no errors.
28452              * @param {Roo.form.FieldLabel} this
28453              */
28454             valid : true
28455         });
28456 };
28457
28458 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28459     
28460     tag: 'label',
28461     cls: '',
28462     html: '',
28463     target: '',
28464     allowBlank : true,
28465     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28466     validClass : 'text-success fa fa-lg fa-check',
28467     iconTooltip : 'This field is required',
28468     
28469     getAutoCreate : function(){
28470         
28471         var cfg = {
28472             tag : this.tag,
28473             cls : 'roo-bootstrap-field-label ' + this.cls,
28474             for : this.target,
28475             cn : [
28476                 {
28477                     tag : 'i',
28478                     cls : '',
28479                     tooltip : this.iconTooltip
28480                 },
28481                 {
28482                     tag : 'span',
28483                     html : this.html
28484                 }
28485             ] 
28486         };
28487         
28488         return cfg;
28489     },
28490     
28491     initEvents: function() 
28492     {
28493         Roo.bootstrap.Element.superclass.initEvents.call(this);
28494         
28495         this.iconEl = this.el.select('i', true).first();
28496         
28497         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28498         
28499         Roo.bootstrap.FieldLabel.register(this);
28500     },
28501     
28502     /**
28503      * Mark this field as valid
28504      */
28505     markValid : function()
28506     {
28507         this.iconEl.show();
28508         
28509         this.iconEl.removeClass(this.invalidClass);
28510         
28511         this.iconEl.addClass(this.validClass);
28512         
28513         this.fireEvent('valid', this);
28514     },
28515     
28516     /**
28517      * Mark this field as invalid
28518      * @param {String} msg The validation message
28519      */
28520     markInvalid : function(msg)
28521     {
28522         this.iconEl.show();
28523         
28524         this.iconEl.removeClass(this.validClass);
28525         
28526         this.iconEl.addClass(this.invalidClass);
28527         
28528         this.fireEvent('invalid', this, msg);
28529     }
28530     
28531    
28532 });
28533
28534 Roo.apply(Roo.bootstrap.FieldLabel, {
28535     
28536     groups: {},
28537     
28538      /**
28539     * register a FieldLabel Group
28540     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28541     */
28542     register : function(label)
28543     {
28544         if(this.groups.hasOwnProperty(label.target)){
28545             return;
28546         }
28547      
28548         this.groups[label.target] = label;
28549         
28550     },
28551     /**
28552     * fetch a FieldLabel Group based on the target
28553     * @param {string} target
28554     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28555     */
28556     get: function(target) {
28557         if (typeof(this.groups[target]) == 'undefined') {
28558             return false;
28559         }
28560         
28561         return this.groups[target] ;
28562     }
28563 });
28564
28565  
28566
28567  /*
28568  * - LGPL
28569  *
28570  * page DateSplitField.
28571  * 
28572  */
28573
28574
28575 /**
28576  * @class Roo.bootstrap.DateSplitField
28577  * @extends Roo.bootstrap.Component
28578  * Bootstrap DateSplitField class
28579  * @cfg {string} fieldLabel - the label associated
28580  * @cfg {Number} labelWidth set the width of label (0-12)
28581  * @cfg {String} labelAlign (top|left)
28582  * @cfg {Boolean} dayAllowBlank (true|false) default false
28583  * @cfg {Boolean} monthAllowBlank (true|false) default false
28584  * @cfg {Boolean} yearAllowBlank (true|false) default false
28585  * @cfg {string} dayPlaceholder 
28586  * @cfg {string} monthPlaceholder
28587  * @cfg {string} yearPlaceholder
28588  * @cfg {string} dayFormat default 'd'
28589  * @cfg {string} monthFormat default 'm'
28590  * @cfg {string} yearFormat default 'Y'
28591
28592  *     
28593  * @constructor
28594  * Create a new DateSplitField
28595  * @param {Object} config The config object
28596  */
28597
28598 Roo.bootstrap.DateSplitField = function(config){
28599     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28600     
28601     this.addEvents({
28602         // raw events
28603          /**
28604          * @event years
28605          * getting the data of years
28606          * @param {Roo.bootstrap.DateSplitField} this
28607          * @param {Object} years
28608          */
28609         "years" : true,
28610         /**
28611          * @event days
28612          * getting the data of days
28613          * @param {Roo.bootstrap.DateSplitField} this
28614          * @param {Object} days
28615          */
28616         "days" : true,
28617         /**
28618          * @event invalid
28619          * Fires after the field has been marked as invalid.
28620          * @param {Roo.form.Field} this
28621          * @param {String} msg The validation message
28622          */
28623         invalid : true,
28624        /**
28625          * @event valid
28626          * Fires after the field has been validated with no errors.
28627          * @param {Roo.form.Field} this
28628          */
28629         valid : true
28630     });
28631 };
28632
28633 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28634     
28635     fieldLabel : '',
28636     labelAlign : 'top',
28637     labelWidth : 3,
28638     dayAllowBlank : false,
28639     monthAllowBlank : false,
28640     yearAllowBlank : false,
28641     dayPlaceholder : '',
28642     monthPlaceholder : '',
28643     yearPlaceholder : '',
28644     dayFormat : 'd',
28645     monthFormat : 'm',
28646     yearFormat : 'Y',
28647     isFormField : true,
28648     
28649     getAutoCreate : function()
28650     {
28651         var cfg = {
28652             tag : 'div',
28653             cls : 'row roo-date-split-field-group',
28654             cn : [
28655                 {
28656                     tag : 'input',
28657                     type : 'hidden',
28658                     cls : 'form-hidden-field roo-date-split-field-group-value',
28659                     name : this.name
28660                 }
28661             ]
28662         };
28663         
28664         if(this.fieldLabel){
28665             cfg.cn.push({
28666                 tag : 'div',
28667                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28668                 cn : [
28669                     {
28670                         tag : 'label',
28671                         html : this.fieldLabel
28672                     }
28673                 ]
28674             });
28675         }
28676         
28677         Roo.each(['day', 'month', 'year'], function(t){
28678             cfg.cn.push({
28679                 tag : 'div',
28680                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28681             });
28682         }, this);
28683         
28684         return cfg;
28685     },
28686     
28687     inputEl: function ()
28688     {
28689         return this.el.select('.roo-date-split-field-group-value', true).first();
28690     },
28691     
28692     onRender : function(ct, position) 
28693     {
28694         var _this = this;
28695         
28696         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28697         
28698         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28699         
28700         this.dayField = new Roo.bootstrap.ComboBox({
28701             allowBlank : this.dayAllowBlank,
28702             alwaysQuery : true,
28703             displayField : 'value',
28704             editable : false,
28705             fieldLabel : '',
28706             forceSelection : true,
28707             mode : 'local',
28708             placeholder : this.dayPlaceholder,
28709             selectOnFocus : true,
28710             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28711             triggerAction : 'all',
28712             typeAhead : true,
28713             valueField : 'value',
28714             store : new Roo.data.SimpleStore({
28715                 data : (function() {    
28716                     var days = [];
28717                     _this.fireEvent('days', _this, days);
28718                     return days;
28719                 })(),
28720                 fields : [ 'value' ]
28721             }),
28722             listeners : {
28723                 select : function (_self, record, index)
28724                 {
28725                     _this.setValue(_this.getValue());
28726                 }
28727             }
28728         });
28729
28730         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28731         
28732         this.monthField = new Roo.bootstrap.MonthField({
28733             after : '<i class=\"fa fa-calendar\"></i>',
28734             allowBlank : this.monthAllowBlank,
28735             placeholder : this.monthPlaceholder,
28736             readOnly : true,
28737             listeners : {
28738                 render : function (_self)
28739                 {
28740                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28741                         e.preventDefault();
28742                         _self.focus();
28743                     });
28744                 },
28745                 select : function (_self, oldvalue, newvalue)
28746                 {
28747                     _this.setValue(_this.getValue());
28748                 }
28749             }
28750         });
28751         
28752         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28753         
28754         this.yearField = new Roo.bootstrap.ComboBox({
28755             allowBlank : this.yearAllowBlank,
28756             alwaysQuery : true,
28757             displayField : 'value',
28758             editable : false,
28759             fieldLabel : '',
28760             forceSelection : true,
28761             mode : 'local',
28762             placeholder : this.yearPlaceholder,
28763             selectOnFocus : true,
28764             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28765             triggerAction : 'all',
28766             typeAhead : true,
28767             valueField : 'value',
28768             store : new Roo.data.SimpleStore({
28769                 data : (function() {
28770                     var years = [];
28771                     _this.fireEvent('years', _this, years);
28772                     return years;
28773                 })(),
28774                 fields : [ 'value' ]
28775             }),
28776             listeners : {
28777                 select : function (_self, record, index)
28778                 {
28779                     _this.setValue(_this.getValue());
28780                 }
28781             }
28782         });
28783
28784         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28785     },
28786     
28787     setValue : function(v, format)
28788     {
28789         this.inputEl.dom.value = v;
28790         
28791         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28792         
28793         var d = Date.parseDate(v, f);
28794         
28795         if(!d){
28796             this.validate();
28797             return;
28798         }
28799         
28800         this.setDay(d.format(this.dayFormat));
28801         this.setMonth(d.format(this.monthFormat));
28802         this.setYear(d.format(this.yearFormat));
28803         
28804         this.validate();
28805         
28806         return;
28807     },
28808     
28809     setDay : function(v)
28810     {
28811         this.dayField.setValue(v);
28812         this.inputEl.dom.value = this.getValue();
28813         this.validate();
28814         return;
28815     },
28816     
28817     setMonth : function(v)
28818     {
28819         this.monthField.setValue(v, true);
28820         this.inputEl.dom.value = this.getValue();
28821         this.validate();
28822         return;
28823     },
28824     
28825     setYear : function(v)
28826     {
28827         this.yearField.setValue(v);
28828         this.inputEl.dom.value = this.getValue();
28829         this.validate();
28830         return;
28831     },
28832     
28833     getDay : function()
28834     {
28835         return this.dayField.getValue();
28836     },
28837     
28838     getMonth : function()
28839     {
28840         return this.monthField.getValue();
28841     },
28842     
28843     getYear : function()
28844     {
28845         return this.yearField.getValue();
28846     },
28847     
28848     getValue : function()
28849     {
28850         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28851         
28852         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28853         
28854         return date;
28855     },
28856     
28857     reset : function()
28858     {
28859         this.setDay('');
28860         this.setMonth('');
28861         this.setYear('');
28862         this.inputEl.dom.value = '';
28863         this.validate();
28864         return;
28865     },
28866     
28867     validate : function()
28868     {
28869         var d = this.dayField.validate();
28870         var m = this.monthField.validate();
28871         var y = this.yearField.validate();
28872         
28873         var valid = true;
28874         
28875         if(
28876                 (!this.dayAllowBlank && !d) ||
28877                 (!this.monthAllowBlank && !m) ||
28878                 (!this.yearAllowBlank && !y)
28879         ){
28880             valid = false;
28881         }
28882         
28883         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28884             return valid;
28885         }
28886         
28887         if(valid){
28888             this.markValid();
28889             return valid;
28890         }
28891         
28892         this.markInvalid();
28893         
28894         return valid;
28895     },
28896     
28897     markValid : function()
28898     {
28899         
28900         var label = this.el.select('label', true).first();
28901         var icon = this.el.select('i.fa-star', true).first();
28902
28903         if(label && icon){
28904             icon.remove();
28905         }
28906         
28907         this.fireEvent('valid', this);
28908     },
28909     
28910      /**
28911      * Mark this field as invalid
28912      * @param {String} msg The validation message
28913      */
28914     markInvalid : function(msg)
28915     {
28916         
28917         var label = this.el.select('label', true).first();
28918         var icon = this.el.select('i.fa-star', true).first();
28919
28920         if(label && !icon){
28921             this.el.select('.roo-date-split-field-label', true).createChild({
28922                 tag : 'i',
28923                 cls : 'text-danger fa fa-lg fa-star',
28924                 tooltip : 'This field is required',
28925                 style : 'margin-right:5px;'
28926             }, label, true);
28927         }
28928         
28929         this.fireEvent('invalid', this, msg);
28930     },
28931     
28932     clearInvalid : function()
28933     {
28934         var label = this.el.select('label', true).first();
28935         var icon = this.el.select('i.fa-star', true).first();
28936
28937         if(label && icon){
28938             icon.remove();
28939         }
28940         
28941         this.fireEvent('valid', this);
28942     },
28943     
28944     getName: function()
28945     {
28946         return this.name;
28947     }
28948     
28949 });
28950
28951  /**
28952  *
28953  * This is based on 
28954  * http://masonry.desandro.com
28955  *
28956  * The idea is to render all the bricks based on vertical width...
28957  *
28958  * The original code extends 'outlayer' - we might need to use that....
28959  * 
28960  */
28961
28962
28963 /**
28964  * @class Roo.bootstrap.LayoutMasonry
28965  * @extends Roo.bootstrap.Component
28966  * Bootstrap Layout Masonry class
28967  * 
28968  * @constructor
28969  * Create a new Element
28970  * @param {Object} config The config object
28971  */
28972
28973 Roo.bootstrap.LayoutMasonry = function(config){
28974     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28975     
28976     this.bricks = [];
28977     
28978 };
28979
28980 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28981     
28982     /**
28983      * @cfg {Boolean} isLayoutInstant = no animation?
28984      */   
28985     isLayoutInstant : false, // needed?
28986    
28987     /**
28988      * @cfg {Number} boxWidth  width of the columns
28989      */   
28990     boxWidth : 450,
28991     
28992       /**
28993      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28994      */   
28995     boxHeight : 0,
28996     
28997     /**
28998      * @cfg {Number} padWidth padding below box..
28999      */   
29000     padWidth : 10, 
29001     
29002     /**
29003      * @cfg {Number} gutter gutter width..
29004      */   
29005     gutter : 10,
29006     
29007      /**
29008      * @cfg {Number} maxCols maximum number of columns
29009      */   
29010     
29011     maxCols: 0,
29012     
29013     /**
29014      * @cfg {Boolean} isAutoInitial defalut true
29015      */   
29016     isAutoInitial : true, 
29017     
29018     containerWidth: 0,
29019     
29020     /**
29021      * @cfg {Boolean} isHorizontal defalut false
29022      */   
29023     isHorizontal : false, 
29024
29025     currentSize : null,
29026     
29027     tag: 'div',
29028     
29029     cls: '',
29030     
29031     bricks: null, //CompositeElement
29032     
29033     cols : 1,
29034     
29035     _isLayoutInited : false,
29036     
29037 //    isAlternative : false, // only use for vertical layout...
29038     
29039     /**
29040      * @cfg {Number} alternativePadWidth padding below box..
29041      */   
29042     alternativePadWidth : 50, 
29043     
29044     getAutoCreate : function(){
29045         
29046         var cfg = {
29047             tag: this.tag,
29048             cls: 'blog-masonary-wrapper ' + this.cls,
29049             cn : {
29050                 cls : 'mas-boxes masonary'
29051             }
29052         };
29053         
29054         return cfg;
29055     },
29056     
29057     getChildContainer: function( )
29058     {
29059         if (this.boxesEl) {
29060             return this.boxesEl;
29061         }
29062         
29063         this.boxesEl = this.el.select('.mas-boxes').first();
29064         
29065         return this.boxesEl;
29066     },
29067     
29068     
29069     initEvents : function()
29070     {
29071         var _this = this;
29072         
29073         if(this.isAutoInitial){
29074             Roo.log('hook children rendered');
29075             this.on('childrenrendered', function() {
29076                 Roo.log('children rendered');
29077                 _this.initial();
29078             } ,this);
29079         }
29080     },
29081     
29082     initial : function()
29083     {
29084         this.currentSize = this.el.getBox(true);
29085         
29086         Roo.EventManager.onWindowResize(this.resize, this); 
29087
29088         if(!this.isAutoInitial){
29089             this.layout();
29090             return;
29091         }
29092         
29093         this.layout();
29094         
29095         return;
29096         //this.layout.defer(500,this);
29097         
29098     },
29099     
29100     resize : function()
29101     {
29102         Roo.log('resize');
29103         
29104         var cs = this.el.getBox(true);
29105         
29106         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29107             Roo.log("no change in with or X");
29108             return;
29109         }
29110         
29111         this.currentSize = cs;
29112         
29113         this.layout();
29114         
29115     },
29116     
29117     layout : function()
29118     {   
29119         this._resetLayout();
29120         
29121         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29122         
29123         this.layoutItems( isInstant );
29124       
29125         this._isLayoutInited = true;
29126         
29127     },
29128     
29129     _resetLayout : function()
29130     {
29131         if(this.isHorizontal){
29132             this.horizontalMeasureColumns();
29133             return;
29134         }
29135         
29136         this.verticalMeasureColumns();
29137         
29138     },
29139     
29140     verticalMeasureColumns : function()
29141     {
29142         this.getContainerWidth();
29143         
29144 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29145 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29146 //            return;
29147 //        }
29148         
29149         var boxWidth = this.boxWidth + this.padWidth;
29150         
29151         if(this.containerWidth < this.boxWidth){
29152             boxWidth = this.containerWidth
29153         }
29154         
29155         var containerWidth = this.containerWidth;
29156         
29157         var cols = Math.floor(containerWidth / boxWidth);
29158         
29159         this.cols = Math.max( cols, 1 );
29160         
29161         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29162         
29163         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29164         
29165         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29166         
29167         this.colWidth = boxWidth + avail - this.padWidth;
29168         
29169         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29170         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29171     },
29172     
29173     horizontalMeasureColumns : function()
29174     {
29175         this.getContainerWidth();
29176         
29177         var boxWidth = this.boxWidth;
29178         
29179         if(this.containerWidth < boxWidth){
29180             boxWidth = this.containerWidth;
29181         }
29182         
29183         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29184         
29185         this.el.setHeight(boxWidth);
29186         
29187     },
29188     
29189     getContainerWidth : function()
29190     {
29191         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29192     },
29193     
29194     layoutItems : function( isInstant )
29195     {
29196         var items = Roo.apply([], this.bricks);
29197         
29198         if(this.isHorizontal){
29199             this._horizontalLayoutItems( items , isInstant );
29200             return;
29201         }
29202         
29203 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29204 //            this._verticalAlternativeLayoutItems( items , isInstant );
29205 //            return;
29206 //        }
29207         
29208         this._verticalLayoutItems( items , isInstant );
29209         
29210     },
29211     
29212     _verticalLayoutItems : function ( items , isInstant)
29213     {
29214         if ( !items || !items.length ) {
29215             return;
29216         }
29217         
29218         var standard = [
29219             ['xs', 'xs', 'xs', 'tall'],
29220             ['xs', 'xs', 'tall'],
29221             ['xs', 'xs', 'sm'],
29222             ['xs', 'xs', 'xs'],
29223             ['xs', 'tall'],
29224             ['xs', 'sm'],
29225             ['xs', 'xs'],
29226             ['xs'],
29227             
29228             ['sm', 'xs', 'xs'],
29229             ['sm', 'xs'],
29230             ['sm'],
29231             
29232             ['tall', 'xs', 'xs', 'xs'],
29233             ['tall', 'xs', 'xs'],
29234             ['tall', 'xs'],
29235             ['tall']
29236             
29237         ];
29238         
29239         var queue = [];
29240         
29241         var boxes = [];
29242         
29243         var box = [];
29244         
29245         Roo.each(items, function(item, k){
29246             
29247             switch (item.size) {
29248                 // these layouts take up a full box,
29249                 case 'md' :
29250                 case 'md-left' :
29251                 case 'md-right' :
29252                 case 'wide' :
29253                     
29254                     if(box.length){
29255                         boxes.push(box);
29256                         box = [];
29257                     }
29258                     
29259                     boxes.push([item]);
29260                     
29261                     break;
29262                     
29263                 case 'xs' :
29264                 case 'sm' :
29265                 case 'tall' :
29266                     
29267                     box.push(item);
29268                     
29269                     break;
29270                 default :
29271                     break;
29272                     
29273             }
29274             
29275         }, this);
29276         
29277         if(box.length){
29278             boxes.push(box);
29279             box = [];
29280         }
29281         
29282         var filterPattern = function(box, length)
29283         {
29284             if(!box.length){
29285                 return;
29286             }
29287             
29288             var match = false;
29289             
29290             var pattern = box.slice(0, length);
29291             
29292             var format = [];
29293             
29294             Roo.each(pattern, function(i){
29295                 format.push(i.size);
29296             }, this);
29297             
29298             Roo.each(standard, function(s){
29299                 
29300                 if(String(s) != String(format)){
29301                     return;
29302                 }
29303                 
29304                 match = true;
29305                 return false;
29306                 
29307             }, this);
29308             
29309             if(!match && length == 1){
29310                 return;
29311             }
29312             
29313             if(!match){
29314                 filterPattern(box, length - 1);
29315                 return;
29316             }
29317                 
29318             queue.push(pattern);
29319
29320             box = box.slice(length, box.length);
29321
29322             filterPattern(box, 4);
29323
29324             return;
29325             
29326         }
29327         
29328         Roo.each(boxes, function(box, k){
29329             
29330             if(!box.length){
29331                 return;
29332             }
29333             
29334             if(box.length == 1){
29335                 queue.push(box);
29336                 return;
29337             }
29338             
29339             filterPattern(box, 4);
29340             
29341         }, this);
29342         
29343         this._processVerticalLayoutQueue( queue, isInstant );
29344         
29345     },
29346     
29347 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29348 //    {
29349 //        if ( !items || !items.length ) {
29350 //            return;
29351 //        }
29352 //
29353 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29354 //        
29355 //    },
29356     
29357     _horizontalLayoutItems : function ( items , isInstant)
29358     {
29359         if ( !items || !items.length || items.length < 3) {
29360             return;
29361         }
29362         
29363         items.reverse();
29364         
29365         var eItems = items.slice(0, 3);
29366         
29367         items = items.slice(3, items.length);
29368         
29369         var standard = [
29370             ['xs', 'xs', 'xs', 'wide'],
29371             ['xs', 'xs', 'wide'],
29372             ['xs', 'xs', 'sm'],
29373             ['xs', 'xs', 'xs'],
29374             ['xs', 'wide'],
29375             ['xs', 'sm'],
29376             ['xs', 'xs'],
29377             ['xs'],
29378             
29379             ['sm', 'xs', 'xs'],
29380             ['sm', 'xs'],
29381             ['sm'],
29382             
29383             ['wide', 'xs', 'xs', 'xs'],
29384             ['wide', 'xs', 'xs'],
29385             ['wide', 'xs'],
29386             ['wide'],
29387             
29388             ['wide-thin']
29389         ];
29390         
29391         var queue = [];
29392         
29393         var boxes = [];
29394         
29395         var box = [];
29396         
29397         Roo.each(items, function(item, k){
29398             
29399             switch (item.size) {
29400                 case 'md' :
29401                 case 'md-left' :
29402                 case 'md-right' :
29403                 case 'tall' :
29404                     
29405                     if(box.length){
29406                         boxes.push(box);
29407                         box = [];
29408                     }
29409                     
29410                     boxes.push([item]);
29411                     
29412                     break;
29413                     
29414                 case 'xs' :
29415                 case 'sm' :
29416                 case 'wide' :
29417                 case 'wide-thin' :
29418                     
29419                     box.push(item);
29420                     
29421                     break;
29422                 default :
29423                     break;
29424                     
29425             }
29426             
29427         }, this);
29428         
29429         if(box.length){
29430             boxes.push(box);
29431             box = [];
29432         }
29433         
29434         var filterPattern = function(box, length)
29435         {
29436             if(!box.length){
29437                 return;
29438             }
29439             
29440             var match = false;
29441             
29442             var pattern = box.slice(0, length);
29443             
29444             var format = [];
29445             
29446             Roo.each(pattern, function(i){
29447                 format.push(i.size);
29448             }, this);
29449             
29450             Roo.each(standard, function(s){
29451                 
29452                 if(String(s) != String(format)){
29453                     return;
29454                 }
29455                 
29456                 match = true;
29457                 return false;
29458                 
29459             }, this);
29460             
29461             if(!match && length == 1){
29462                 return;
29463             }
29464             
29465             if(!match){
29466                 filterPattern(box, length - 1);
29467                 return;
29468             }
29469                 
29470             queue.push(pattern);
29471
29472             box = box.slice(length, box.length);
29473
29474             filterPattern(box, 4);
29475
29476             return;
29477             
29478         }
29479         
29480         Roo.each(boxes, function(box, k){
29481             
29482             if(!box.length){
29483                 return;
29484             }
29485             
29486             if(box.length == 1){
29487                 queue.push(box);
29488                 return;
29489             }
29490             
29491             filterPattern(box, 4);
29492             
29493         }, this);
29494         
29495         
29496         var prune = [];
29497         
29498         var pos = this.el.getBox(true);
29499         
29500         var minX = pos.x;
29501         
29502         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29503         
29504         var hit_end = false;
29505         
29506         Roo.each(queue, function(box){
29507             
29508             if(hit_end){
29509                 
29510                 Roo.each(box, function(b){
29511                 
29512                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29513                     b.el.hide();
29514
29515                 }, this);
29516
29517                 return;
29518             }
29519             
29520             var mx = 0;
29521             
29522             Roo.each(box, function(b){
29523                 
29524                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29525                 b.el.show();
29526
29527                 mx = Math.max(mx, b.x);
29528                 
29529             }, this);
29530             
29531             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29532             
29533             if(maxX < minX){
29534                 
29535                 Roo.each(box, function(b){
29536                 
29537                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29538                     b.el.hide();
29539                     
29540                 }, this);
29541                 
29542                 hit_end = true;
29543                 
29544                 return;
29545             }
29546             
29547             prune.push(box);
29548             
29549         }, this);
29550         
29551         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29552     },
29553     
29554     /** Sets position of item in DOM
29555     * @param {Element} item
29556     * @param {Number} x - horizontal position
29557     * @param {Number} y - vertical position
29558     * @param {Boolean} isInstant - disables transitions
29559     */
29560     _processVerticalLayoutQueue : function( queue, isInstant )
29561     {
29562         var pos = this.el.getBox(true);
29563         var x = pos.x;
29564         var y = pos.y;
29565         var maxY = [];
29566         
29567         for (var i = 0; i < this.cols; i++){
29568             maxY[i] = pos.y;
29569         }
29570         
29571         Roo.each(queue, function(box, k){
29572             
29573             var col = k % this.cols;
29574             
29575             Roo.each(box, function(b,kk){
29576                 
29577                 b.el.position('absolute');
29578                 
29579                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29580                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29581                 
29582                 if(b.size == 'md-left' || b.size == 'md-right'){
29583                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29584                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29585                 }
29586                 
29587                 b.el.setWidth(width);
29588                 b.el.setHeight(height);
29589                 // iframe?
29590                 b.el.select('iframe',true).setSize(width,height);
29591                 
29592             }, this);
29593             
29594             for (var i = 0; i < this.cols; i++){
29595                 
29596                 if(maxY[i] < maxY[col]){
29597                     col = i;
29598                     continue;
29599                 }
29600                 
29601                 col = Math.min(col, i);
29602                 
29603             }
29604             
29605             x = pos.x + col * (this.colWidth + this.padWidth);
29606             
29607             y = maxY[col];
29608             
29609             var positions = [];
29610             
29611             switch (box.length){
29612                 case 1 :
29613                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29614                     break;
29615                 case 2 :
29616                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29617                     break;
29618                 case 3 :
29619                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29620                     break;
29621                 case 4 :
29622                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29623                     break;
29624                 default :
29625                     break;
29626             }
29627             
29628             Roo.each(box, function(b,kk){
29629                 
29630                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29631                 
29632                 var sz = b.el.getSize();
29633                 
29634                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29635                 
29636             }, this);
29637             
29638         }, this);
29639         
29640         var mY = 0;
29641         
29642         for (var i = 0; i < this.cols; i++){
29643             mY = Math.max(mY, maxY[i]);
29644         }
29645         
29646         this.el.setHeight(mY - pos.y);
29647         
29648     },
29649     
29650 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29651 //    {
29652 //        var pos = this.el.getBox(true);
29653 //        var x = pos.x;
29654 //        var y = pos.y;
29655 //        var maxX = pos.right;
29656 //        
29657 //        var maxHeight = 0;
29658 //        
29659 //        Roo.each(items, function(item, k){
29660 //            
29661 //            var c = k % 2;
29662 //            
29663 //            item.el.position('absolute');
29664 //                
29665 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29666 //
29667 //            item.el.setWidth(width);
29668 //
29669 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29670 //
29671 //            item.el.setHeight(height);
29672 //            
29673 //            if(c == 0){
29674 //                item.el.setXY([x, y], isInstant ? false : true);
29675 //            } else {
29676 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29677 //            }
29678 //            
29679 //            y = y + height + this.alternativePadWidth;
29680 //            
29681 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29682 //            
29683 //        }, this);
29684 //        
29685 //        this.el.setHeight(maxHeight);
29686 //        
29687 //    },
29688     
29689     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29690     {
29691         var pos = this.el.getBox(true);
29692         
29693         var minX = pos.x;
29694         var minY = pos.y;
29695         
29696         var maxX = pos.right;
29697         
29698         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29699         
29700         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29701         
29702         Roo.each(queue, function(box, k){
29703             
29704             Roo.each(box, function(b, kk){
29705                 
29706                 b.el.position('absolute');
29707                 
29708                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29709                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29710                 
29711                 if(b.size == 'md-left' || b.size == 'md-right'){
29712                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29713                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29714                 }
29715                 
29716                 b.el.setWidth(width);
29717                 b.el.setHeight(height);
29718                 
29719             }, this);
29720             
29721             if(!box.length){
29722                 return;
29723             }
29724             
29725             var positions = [];
29726             
29727             switch (box.length){
29728                 case 1 :
29729                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29730                     break;
29731                 case 2 :
29732                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29733                     break;
29734                 case 3 :
29735                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29736                     break;
29737                 case 4 :
29738                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29739                     break;
29740                 default :
29741                     break;
29742             }
29743             
29744             Roo.each(box, function(b,kk){
29745                 
29746                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29747                 
29748                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29749                 
29750             }, this);
29751             
29752         }, this);
29753         
29754     },
29755     
29756     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29757     {
29758         Roo.each(eItems, function(b,k){
29759             
29760             b.size = (k == 0) ? 'sm' : 'xs';
29761             b.x = (k == 0) ? 2 : 1;
29762             b.y = (k == 0) ? 2 : 1;
29763             
29764             b.el.position('absolute');
29765             
29766             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29767                 
29768             b.el.setWidth(width);
29769             
29770             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29771             
29772             b.el.setHeight(height);
29773             
29774         }, this);
29775
29776         var positions = [];
29777         
29778         positions.push({
29779             x : maxX - this.unitWidth * 2 - this.gutter,
29780             y : minY
29781         });
29782         
29783         positions.push({
29784             x : maxX - this.unitWidth,
29785             y : minY + (this.unitWidth + this.gutter) * 2
29786         });
29787         
29788         positions.push({
29789             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29790             y : minY
29791         });
29792         
29793         Roo.each(eItems, function(b,k){
29794             
29795             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29796
29797         }, this);
29798         
29799     },
29800     
29801     getVerticalOneBoxColPositions : function(x, y, box)
29802     {
29803         var pos = [];
29804         
29805         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29806         
29807         if(box[0].size == 'md-left'){
29808             rand = 0;
29809         }
29810         
29811         if(box[0].size == 'md-right'){
29812             rand = 1;
29813         }
29814         
29815         pos.push({
29816             x : x + (this.unitWidth + this.gutter) * rand,
29817             y : y
29818         });
29819         
29820         return pos;
29821     },
29822     
29823     getVerticalTwoBoxColPositions : function(x, y, box)
29824     {
29825         var pos = [];
29826         
29827         if(box[0].size == 'xs'){
29828             
29829             pos.push({
29830                 x : x,
29831                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29832             });
29833
29834             pos.push({
29835                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29836                 y : y
29837             });
29838             
29839             return pos;
29840             
29841         }
29842         
29843         pos.push({
29844             x : x,
29845             y : y
29846         });
29847
29848         pos.push({
29849             x : x + (this.unitWidth + this.gutter) * 2,
29850             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29851         });
29852         
29853         return pos;
29854         
29855     },
29856     
29857     getVerticalThreeBoxColPositions : function(x, y, box)
29858     {
29859         var pos = [];
29860         
29861         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29862             
29863             pos.push({
29864                 x : x,
29865                 y : y
29866             });
29867
29868             pos.push({
29869                 x : x + (this.unitWidth + this.gutter) * 1,
29870                 y : y
29871             });
29872             
29873             pos.push({
29874                 x : x + (this.unitWidth + this.gutter) * 2,
29875                 y : y
29876             });
29877             
29878             return pos;
29879             
29880         }
29881         
29882         if(box[0].size == 'xs' && box[1].size == 'xs'){
29883             
29884             pos.push({
29885                 x : x,
29886                 y : y
29887             });
29888
29889             pos.push({
29890                 x : x,
29891                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29892             });
29893             
29894             pos.push({
29895                 x : x + (this.unitWidth + this.gutter) * 1,
29896                 y : y
29897             });
29898             
29899             return pos;
29900             
29901         }
29902         
29903         pos.push({
29904             x : x,
29905             y : y
29906         });
29907
29908         pos.push({
29909             x : x + (this.unitWidth + this.gutter) * 2,
29910             y : y
29911         });
29912
29913         pos.push({
29914             x : x + (this.unitWidth + this.gutter) * 2,
29915             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29916         });
29917             
29918         return pos;
29919         
29920     },
29921     
29922     getVerticalFourBoxColPositions : function(x, y, box)
29923     {
29924         var pos = [];
29925         
29926         if(box[0].size == 'xs'){
29927             
29928             pos.push({
29929                 x : x,
29930                 y : y
29931             });
29932
29933             pos.push({
29934                 x : x,
29935                 y : y + (this.unitHeight + this.gutter) * 1
29936             });
29937             
29938             pos.push({
29939                 x : x,
29940                 y : y + (this.unitHeight + this.gutter) * 2
29941             });
29942             
29943             pos.push({
29944                 x : x + (this.unitWidth + this.gutter) * 1,
29945                 y : y
29946             });
29947             
29948             return pos;
29949             
29950         }
29951         
29952         pos.push({
29953             x : x,
29954             y : y
29955         });
29956
29957         pos.push({
29958             x : x + (this.unitWidth + this.gutter) * 2,
29959             y : y
29960         });
29961
29962         pos.push({
29963             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29964             y : y + (this.unitHeight + this.gutter) * 1
29965         });
29966
29967         pos.push({
29968             x : x + (this.unitWidth + this.gutter) * 2,
29969             y : y + (this.unitWidth + this.gutter) * 2
29970         });
29971
29972         return pos;
29973         
29974     },
29975     
29976     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29977     {
29978         var pos = [];
29979         
29980         if(box[0].size == 'md-left'){
29981             pos.push({
29982                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29983                 y : minY
29984             });
29985             
29986             return pos;
29987         }
29988         
29989         if(box[0].size == 'md-right'){
29990             pos.push({
29991                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29992                 y : minY + (this.unitWidth + this.gutter) * 1
29993             });
29994             
29995             return pos;
29996         }
29997         
29998         var rand = Math.floor(Math.random() * (4 - box[0].y));
29999         
30000         pos.push({
30001             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30002             y : minY + (this.unitWidth + this.gutter) * rand
30003         });
30004         
30005         return pos;
30006         
30007     },
30008     
30009     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30010     {
30011         var pos = [];
30012         
30013         if(box[0].size == 'xs'){
30014             
30015             pos.push({
30016                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30017                 y : minY
30018             });
30019
30020             pos.push({
30021                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30022                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30023             });
30024             
30025             return pos;
30026             
30027         }
30028         
30029         pos.push({
30030             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30031             y : minY
30032         });
30033
30034         pos.push({
30035             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30036             y : minY + (this.unitWidth + this.gutter) * 2
30037         });
30038         
30039         return pos;
30040         
30041     },
30042     
30043     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30044     {
30045         var pos = [];
30046         
30047         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30048             
30049             pos.push({
30050                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30051                 y : minY
30052             });
30053
30054             pos.push({
30055                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30056                 y : minY + (this.unitWidth + this.gutter) * 1
30057             });
30058             
30059             pos.push({
30060                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30061                 y : minY + (this.unitWidth + this.gutter) * 2
30062             });
30063             
30064             return pos;
30065             
30066         }
30067         
30068         if(box[0].size == 'xs' && box[1].size == 'xs'){
30069             
30070             pos.push({
30071                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30072                 y : minY
30073             });
30074
30075             pos.push({
30076                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30077                 y : minY
30078             });
30079             
30080             pos.push({
30081                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30082                 y : minY + (this.unitWidth + this.gutter) * 1
30083             });
30084             
30085             return pos;
30086             
30087         }
30088         
30089         pos.push({
30090             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30091             y : minY
30092         });
30093
30094         pos.push({
30095             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30096             y : minY + (this.unitWidth + this.gutter) * 2
30097         });
30098
30099         pos.push({
30100             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30101             y : minY + (this.unitWidth + this.gutter) * 2
30102         });
30103             
30104         return pos;
30105         
30106     },
30107     
30108     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30109     {
30110         var pos = [];
30111         
30112         if(box[0].size == 'xs'){
30113             
30114             pos.push({
30115                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30116                 y : minY
30117             });
30118
30119             pos.push({
30120                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30121                 y : minY
30122             });
30123             
30124             pos.push({
30125                 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),
30126                 y : minY
30127             });
30128             
30129             pos.push({
30130                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30131                 y : minY + (this.unitWidth + this.gutter) * 1
30132             });
30133             
30134             return pos;
30135             
30136         }
30137         
30138         pos.push({
30139             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30140             y : minY
30141         });
30142         
30143         pos.push({
30144             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30145             y : minY + (this.unitWidth + this.gutter) * 2
30146         });
30147         
30148         pos.push({
30149             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30150             y : minY + (this.unitWidth + this.gutter) * 2
30151         });
30152         
30153         pos.push({
30154             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),
30155             y : minY + (this.unitWidth + this.gutter) * 2
30156         });
30157
30158         return pos;
30159         
30160     }
30161     
30162 });
30163
30164  
30165
30166  /**
30167  *
30168  * This is based on 
30169  * http://masonry.desandro.com
30170  *
30171  * The idea is to render all the bricks based on vertical width...
30172  *
30173  * The original code extends 'outlayer' - we might need to use that....
30174  * 
30175  */
30176
30177
30178 /**
30179  * @class Roo.bootstrap.LayoutMasonryAuto
30180  * @extends Roo.bootstrap.Component
30181  * Bootstrap Layout Masonry class
30182  * 
30183  * @constructor
30184  * Create a new Element
30185  * @param {Object} config The config object
30186  */
30187
30188 Roo.bootstrap.LayoutMasonryAuto = function(config){
30189     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30190 };
30191
30192 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30193     
30194       /**
30195      * @cfg {Boolean} isFitWidth  - resize the width..
30196      */   
30197     isFitWidth : false,  // options..
30198     /**
30199      * @cfg {Boolean} isOriginLeft = left align?
30200      */   
30201     isOriginLeft : true,
30202     /**
30203      * @cfg {Boolean} isOriginTop = top align?
30204      */   
30205     isOriginTop : false,
30206     /**
30207      * @cfg {Boolean} isLayoutInstant = no animation?
30208      */   
30209     isLayoutInstant : false, // needed?
30210     /**
30211      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30212      */   
30213     isResizingContainer : true,
30214     /**
30215      * @cfg {Number} columnWidth  width of the columns 
30216      */   
30217     
30218     columnWidth : 0,
30219     
30220     /**
30221      * @cfg {Number} maxCols maximum number of columns
30222      */   
30223     
30224     maxCols: 0,
30225     /**
30226      * @cfg {Number} padHeight padding below box..
30227      */   
30228     
30229     padHeight : 10, 
30230     
30231     /**
30232      * @cfg {Boolean} isAutoInitial defalut true
30233      */   
30234     
30235     isAutoInitial : true, 
30236     
30237     // private?
30238     gutter : 0,
30239     
30240     containerWidth: 0,
30241     initialColumnWidth : 0,
30242     currentSize : null,
30243     
30244     colYs : null, // array.
30245     maxY : 0,
30246     padWidth: 10,
30247     
30248     
30249     tag: 'div',
30250     cls: '',
30251     bricks: null, //CompositeElement
30252     cols : 0, // array?
30253     // element : null, // wrapped now this.el
30254     _isLayoutInited : null, 
30255     
30256     
30257     getAutoCreate : function(){
30258         
30259         var cfg = {
30260             tag: this.tag,
30261             cls: 'blog-masonary-wrapper ' + this.cls,
30262             cn : {
30263                 cls : 'mas-boxes masonary'
30264             }
30265         };
30266         
30267         return cfg;
30268     },
30269     
30270     getChildContainer: function( )
30271     {
30272         if (this.boxesEl) {
30273             return this.boxesEl;
30274         }
30275         
30276         this.boxesEl = this.el.select('.mas-boxes').first();
30277         
30278         return this.boxesEl;
30279     },
30280     
30281     
30282     initEvents : function()
30283     {
30284         var _this = this;
30285         
30286         if(this.isAutoInitial){
30287             Roo.log('hook children rendered');
30288             this.on('childrenrendered', function() {
30289                 Roo.log('children rendered');
30290                 _this.initial();
30291             } ,this);
30292         }
30293         
30294     },
30295     
30296     initial : function()
30297     {
30298         this.reloadItems();
30299
30300         this.currentSize = this.el.getBox(true);
30301
30302         /// was window resize... - let's see if this works..
30303         Roo.EventManager.onWindowResize(this.resize, this); 
30304
30305         if(!this.isAutoInitial){
30306             this.layout();
30307             return;
30308         }
30309         
30310         this.layout.defer(500,this);
30311     },
30312     
30313     reloadItems: function()
30314     {
30315         this.bricks = this.el.select('.masonry-brick', true);
30316         
30317         this.bricks.each(function(b) {
30318             //Roo.log(b.getSize());
30319             if (!b.attr('originalwidth')) {
30320                 b.attr('originalwidth',  b.getSize().width);
30321             }
30322             
30323         });
30324         
30325         Roo.log(this.bricks.elements.length);
30326     },
30327     
30328     resize : function()
30329     {
30330         Roo.log('resize');
30331         var cs = this.el.getBox(true);
30332         
30333         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30334             Roo.log("no change in with or X");
30335             return;
30336         }
30337         this.currentSize = cs;
30338         this.layout();
30339     },
30340     
30341     layout : function()
30342     {
30343          Roo.log('layout');
30344         this._resetLayout();
30345         //this._manageStamps();
30346       
30347         // don't animate first layout
30348         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30349         this.layoutItems( isInstant );
30350       
30351         // flag for initalized
30352         this._isLayoutInited = true;
30353     },
30354     
30355     layoutItems : function( isInstant )
30356     {
30357         //var items = this._getItemsForLayout( this.items );
30358         // original code supports filtering layout items.. we just ignore it..
30359         
30360         this._layoutItems( this.bricks , isInstant );
30361       
30362         this._postLayout();
30363     },
30364     _layoutItems : function ( items , isInstant)
30365     {
30366        //this.fireEvent( 'layout', this, items );
30367     
30368
30369         if ( !items || !items.elements.length ) {
30370           // no items, emit event with empty array
30371             return;
30372         }
30373
30374         var queue = [];
30375         items.each(function(item) {
30376             Roo.log("layout item");
30377             Roo.log(item);
30378             // get x/y object from method
30379             var position = this._getItemLayoutPosition( item );
30380             // enqueue
30381             position.item = item;
30382             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30383             queue.push( position );
30384         }, this);
30385       
30386         this._processLayoutQueue( queue );
30387     },
30388     /** Sets position of item in DOM
30389     * @param {Element} item
30390     * @param {Number} x - horizontal position
30391     * @param {Number} y - vertical position
30392     * @param {Boolean} isInstant - disables transitions
30393     */
30394     _processLayoutQueue : function( queue )
30395     {
30396         for ( var i=0, len = queue.length; i < len; i++ ) {
30397             var obj = queue[i];
30398             obj.item.position('absolute');
30399             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30400         }
30401     },
30402       
30403     
30404     /**
30405     * Any logic you want to do after each layout,
30406     * i.e. size the container
30407     */
30408     _postLayout : function()
30409     {
30410         this.resizeContainer();
30411     },
30412     
30413     resizeContainer : function()
30414     {
30415         if ( !this.isResizingContainer ) {
30416             return;
30417         }
30418         var size = this._getContainerSize();
30419         if ( size ) {
30420             this.el.setSize(size.width,size.height);
30421             this.boxesEl.setSize(size.width,size.height);
30422         }
30423     },
30424     
30425     
30426     
30427     _resetLayout : function()
30428     {
30429         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30430         this.colWidth = this.el.getWidth();
30431         //this.gutter = this.el.getWidth(); 
30432         
30433         this.measureColumns();
30434
30435         // reset column Y
30436         var i = this.cols;
30437         this.colYs = [];
30438         while (i--) {
30439             this.colYs.push( 0 );
30440         }
30441     
30442         this.maxY = 0;
30443     },
30444
30445     measureColumns : function()
30446     {
30447         this.getContainerWidth();
30448       // if columnWidth is 0, default to outerWidth of first item
30449         if ( !this.columnWidth ) {
30450             var firstItem = this.bricks.first();
30451             Roo.log(firstItem);
30452             this.columnWidth  = this.containerWidth;
30453             if (firstItem && firstItem.attr('originalwidth') ) {
30454                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30455             }
30456             // columnWidth fall back to item of first element
30457             Roo.log("set column width?");
30458                         this.initialColumnWidth = this.columnWidth  ;
30459
30460             // if first elem has no width, default to size of container
30461             
30462         }
30463         
30464         
30465         if (this.initialColumnWidth) {
30466             this.columnWidth = this.initialColumnWidth;
30467         }
30468         
30469         
30470             
30471         // column width is fixed at the top - however if container width get's smaller we should
30472         // reduce it...
30473         
30474         // this bit calcs how man columns..
30475             
30476         var columnWidth = this.columnWidth += this.gutter;
30477       
30478         // calculate columns
30479         var containerWidth = this.containerWidth + this.gutter;
30480         
30481         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30482         // fix rounding errors, typically with gutters
30483         var excess = columnWidth - containerWidth % columnWidth;
30484         
30485         
30486         // if overshoot is less than a pixel, round up, otherwise floor it
30487         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30488         cols = Math[ mathMethod ]( cols );
30489         this.cols = Math.max( cols, 1 );
30490         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30491         
30492          // padding positioning..
30493         var totalColWidth = this.cols * this.columnWidth;
30494         var padavail = this.containerWidth - totalColWidth;
30495         // so for 2 columns - we need 3 'pads'
30496         
30497         var padNeeded = (1+this.cols) * this.padWidth;
30498         
30499         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30500         
30501         this.columnWidth += padExtra
30502         //this.padWidth = Math.floor(padavail /  ( this.cols));
30503         
30504         // adjust colum width so that padding is fixed??
30505         
30506         // we have 3 columns ... total = width * 3
30507         // we have X left over... that should be used by 
30508         
30509         //if (this.expandC) {
30510             
30511         //}
30512         
30513         
30514         
30515     },
30516     
30517     getContainerWidth : function()
30518     {
30519        /* // container is parent if fit width
30520         var container = this.isFitWidth ? this.element.parentNode : this.element;
30521         // check that this.size and size are there
30522         // IE8 triggers resize on body size change, so they might not be
30523         
30524         var size = getSize( container );  //FIXME
30525         this.containerWidth = size && size.innerWidth; //FIXME
30526         */
30527          
30528         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30529         
30530     },
30531     
30532     _getItemLayoutPosition : function( item )  // what is item?
30533     {
30534         // we resize the item to our columnWidth..
30535       
30536         item.setWidth(this.columnWidth);
30537         item.autoBoxAdjust  = false;
30538         
30539         var sz = item.getSize();
30540  
30541         // how many columns does this brick span
30542         var remainder = this.containerWidth % this.columnWidth;
30543         
30544         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30545         // round if off by 1 pixel, otherwise use ceil
30546         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30547         colSpan = Math.min( colSpan, this.cols );
30548         
30549         // normally this should be '1' as we dont' currently allow multi width columns..
30550         
30551         var colGroup = this._getColGroup( colSpan );
30552         // get the minimum Y value from the columns
30553         var minimumY = Math.min.apply( Math, colGroup );
30554         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30555         
30556         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30557          
30558         // position the brick
30559         var position = {
30560             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30561             y: this.currentSize.y + minimumY + this.padHeight
30562         };
30563         
30564         Roo.log(position);
30565         // apply setHeight to necessary columns
30566         var setHeight = minimumY + sz.height + this.padHeight;
30567         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30568         
30569         var setSpan = this.cols + 1 - colGroup.length;
30570         for ( var i = 0; i < setSpan; i++ ) {
30571           this.colYs[ shortColIndex + i ] = setHeight ;
30572         }
30573       
30574         return position;
30575     },
30576     
30577     /**
30578      * @param {Number} colSpan - number of columns the element spans
30579      * @returns {Array} colGroup
30580      */
30581     _getColGroup : function( colSpan )
30582     {
30583         if ( colSpan < 2 ) {
30584           // if brick spans only one column, use all the column Ys
30585           return this.colYs;
30586         }
30587       
30588         var colGroup = [];
30589         // how many different places could this brick fit horizontally
30590         var groupCount = this.cols + 1 - colSpan;
30591         // for each group potential horizontal position
30592         for ( var i = 0; i < groupCount; i++ ) {
30593           // make an array of colY values for that one group
30594           var groupColYs = this.colYs.slice( i, i + colSpan );
30595           // and get the max value of the array
30596           colGroup[i] = Math.max.apply( Math, groupColYs );
30597         }
30598         return colGroup;
30599     },
30600     /*
30601     _manageStamp : function( stamp )
30602     {
30603         var stampSize =  stamp.getSize();
30604         var offset = stamp.getBox();
30605         // get the columns that this stamp affects
30606         var firstX = this.isOriginLeft ? offset.x : offset.right;
30607         var lastX = firstX + stampSize.width;
30608         var firstCol = Math.floor( firstX / this.columnWidth );
30609         firstCol = Math.max( 0, firstCol );
30610         
30611         var lastCol = Math.floor( lastX / this.columnWidth );
30612         // lastCol should not go over if multiple of columnWidth #425
30613         lastCol -= lastX % this.columnWidth ? 0 : 1;
30614         lastCol = Math.min( this.cols - 1, lastCol );
30615         
30616         // set colYs to bottom of the stamp
30617         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30618             stampSize.height;
30619             
30620         for ( var i = firstCol; i <= lastCol; i++ ) {
30621           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30622         }
30623     },
30624     */
30625     
30626     _getContainerSize : function()
30627     {
30628         this.maxY = Math.max.apply( Math, this.colYs );
30629         var size = {
30630             height: this.maxY
30631         };
30632       
30633         if ( this.isFitWidth ) {
30634             size.width = this._getContainerFitWidth();
30635         }
30636       
30637         return size;
30638     },
30639     
30640     _getContainerFitWidth : function()
30641     {
30642         var unusedCols = 0;
30643         // count unused columns
30644         var i = this.cols;
30645         while ( --i ) {
30646           if ( this.colYs[i] !== 0 ) {
30647             break;
30648           }
30649           unusedCols++;
30650         }
30651         // fit container to columns that have been used
30652         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30653     },
30654     
30655     needsResizeLayout : function()
30656     {
30657         var previousWidth = this.containerWidth;
30658         this.getContainerWidth();
30659         return previousWidth !== this.containerWidth;
30660     }
30661  
30662 });
30663
30664  
30665
30666  /*
30667  * - LGPL
30668  *
30669  * element
30670  * 
30671  */
30672
30673 /**
30674  * @class Roo.bootstrap.MasonryBrick
30675  * @extends Roo.bootstrap.Component
30676  * Bootstrap MasonryBrick class
30677  * 
30678  * @constructor
30679  * Create a new MasonryBrick
30680  * @param {Object} config The config object
30681  */
30682
30683 Roo.bootstrap.MasonryBrick = function(config){
30684     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30685     
30686     this.addEvents({
30687         // raw events
30688         /**
30689          * @event click
30690          * When a MasonryBrick is clcik
30691          * @param {Roo.bootstrap.MasonryBrick} this
30692          * @param {Roo.EventObject} e
30693          */
30694         "click" : true
30695     });
30696 };
30697
30698 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30699     
30700     /**
30701      * @cfg {String} title
30702      */   
30703     title : '',
30704     /**
30705      * @cfg {String} html
30706      */   
30707     html : '',
30708     /**
30709      * @cfg {String} bgimage
30710      */   
30711     bgimage : '',
30712     /**
30713      * @cfg {String} videourl
30714      */   
30715     videourl : '',
30716     /**
30717      * @cfg {String} cls
30718      */   
30719     cls : '',
30720     /**
30721      * @cfg {String} href
30722      */   
30723     href : '',
30724     /**
30725      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30726      */   
30727     size : 'xs',
30728     
30729     /**
30730      * @cfg {String} (center|bottom) placetitle
30731      */   
30732     placetitle : '',
30733     
30734     getAutoCreate : function()
30735     {
30736         var cls = 'masonry-brick';
30737         
30738         if(this.href.length){
30739             cls += ' masonry-brick-link';
30740         }
30741         
30742         if(this.bgimage.length){
30743             cls += ' masonry-brick-image';
30744         }
30745         
30746         if(this.size){
30747             cls += ' masonry-' + this.size + '-brick';
30748         }
30749         
30750         if(this.placetitle.length){
30751             
30752             switch (this.placetitle) {
30753                 case 'center' :
30754                     cls += ' masonry-center-title';
30755                     break;
30756                 case 'bottom' :
30757                     cls += ' masonry-bottom-title';
30758                     break;
30759                 default:
30760                     break;
30761             }
30762             
30763         } else {
30764             if(!this.html.length && !this.bgimage.length){
30765                 cls += ' masonry-center-title';
30766             }
30767
30768             if(!this.html.length && this.bgimage.length){
30769                 cls += ' masonry-bottom-title';
30770             }
30771         }
30772         
30773         if(this.cls){
30774             cls += ' ' + this.cls;
30775         }
30776         
30777         var cfg = {
30778             tag: (this.href.length) ? 'a' : 'div',
30779             cls: cls,
30780             cn: [
30781                 {
30782                     tag: 'div',
30783                     cls: 'masonry-brick-paragraph',
30784                     cn: []
30785                 }
30786             ]
30787         };
30788         
30789         if(this.href.length){
30790             cfg.href = this.href;
30791         }
30792         
30793         var cn = cfg.cn[0].cn;
30794         
30795         if(this.title.length){
30796             cn.push({
30797                 tag: 'h4',
30798                 cls: 'masonry-brick-title',
30799                 html: this.title
30800             });
30801         }
30802         
30803         if(this.html.length){
30804             cn.push({
30805                 tag: 'p',
30806                 cls: 'masonry-brick-text',
30807                 html: this.html
30808             });
30809         }  
30810         if (!this.title.length && !this.html.length) {
30811             cfg.cn[0].cls += ' hide';
30812         }
30813         
30814         if(this.bgimage.length){
30815             cfg.cn.push({
30816                 tag: 'img',
30817                 cls: 'masonry-brick-image-view',
30818                 src: this.bgimage
30819             });
30820         }
30821         if(this.videourl.length){
30822             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30823             // youtube support only?
30824             cfg.cn.push({
30825                 tag: 'iframe',
30826                 cls: 'masonry-brick-image-view',
30827                 src: vurl,
30828                 frameborder : 0,
30829                 allowfullscreen : true
30830             });
30831             
30832             
30833         }
30834         return cfg;
30835         
30836     },
30837     
30838     initEvents: function() 
30839     {
30840         switch (this.size) {
30841             case 'xs' :
30842 //                this.intSize = 1;
30843                 this.x = 1;
30844                 this.y = 1;
30845                 break;
30846             case 'sm' :
30847 //                this.intSize = 2;
30848                 this.x = 2;
30849                 this.y = 2;
30850                 break;
30851             case 'md' :
30852             case 'md-left' :
30853             case 'md-right' :
30854 //                this.intSize = 3;
30855                 this.x = 3;
30856                 this.y = 3;
30857                 break;
30858             case 'tall' :
30859 //                this.intSize = 3;
30860                 this.x = 2;
30861                 this.y = 3;
30862                 break;
30863             case 'wide' :
30864 //                this.intSize = 3;
30865                 this.x = 3;
30866                 this.y = 2;
30867                 break;
30868             case 'wide-thin' :
30869 //                this.intSize = 3;
30870                 this.x = 3;
30871                 this.y = 1;
30872                 break;
30873                         
30874             default :
30875                 break;
30876         }
30877         
30878         
30879         
30880         if(Roo.isTouch){
30881             this.el.on('touchstart', this.onTouchStart, this);
30882             this.el.on('touchmove', this.onTouchMove, this);
30883             this.el.on('touchend', this.onTouchEnd, this);
30884             this.el.on('contextmenu', this.onContextMenu, this);
30885         } else {
30886             this.el.on('mouseenter'  ,this.enter, this);
30887             this.el.on('mouseleave', this.leave, this);
30888         }
30889         
30890         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30891             this.parent().bricks.push(this);   
30892         }
30893         
30894     },
30895     
30896     onClick: function(e, el)
30897     {
30898         if(!Roo.isTouch){
30899             return;
30900         }
30901         
30902         var time = this.endTimer - this.startTimer;
30903         
30904         //alert(time);
30905         
30906         if(time < 1000){
30907             return;
30908         }
30909         
30910         e.preventDefault();
30911     },
30912     
30913     enter: function(e, el)
30914     {
30915         e.preventDefault();
30916         
30917         if(this.bgimage.length && this.html.length){
30918             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30919         }
30920     },
30921     
30922     leave: function(e, el)
30923     {
30924         e.preventDefault();
30925         
30926         if(this.bgimage.length && this.html.length){
30927             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30928         }
30929     },
30930     
30931     onTouchStart: function(e, el)
30932     {
30933 //        e.preventDefault();
30934         
30935         this.touchmoved = false;
30936         
30937         if(!this.bgimage.length || !this.html.length){
30938             return;
30939         }
30940         
30941         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30942         
30943         this.timer = new Date().getTime();
30944         
30945     },
30946     
30947     onTouchMove: function(e, el)
30948     {
30949         this.touchmoved = true;
30950     },
30951     
30952     onContextMenu : function(e,el)
30953     {
30954         e.preventDefault();
30955         e.stopPropagation();
30956         return false;
30957     },
30958     
30959     onTouchEnd: function(e, el)
30960     {
30961 //        e.preventDefault();
30962         
30963         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30964         
30965             this.leave(e,el);
30966             
30967             return;
30968         }
30969         
30970         if(!this.bgimage.length || !this.html.length){
30971             
30972             if(this.href.length){
30973                 window.location.href = this.href;
30974             }
30975             
30976             return;
30977         }
30978         
30979         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30980         
30981         window.location.href = this.href;
30982     }
30983     
30984 });
30985
30986  
30987
30988  /*
30989  * - LGPL
30990  *
30991  * element
30992  * 
30993  */
30994
30995 /**
30996  * @class Roo.bootstrap.Brick
30997  * @extends Roo.bootstrap.Component
30998  * Bootstrap Brick class
30999  * 
31000  * @constructor
31001  * Create a new Brick
31002  * @param {Object} config The config object
31003  */
31004
31005 Roo.bootstrap.Brick = function(config){
31006     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31007     
31008     this.addEvents({
31009         // raw events
31010         /**
31011          * @event click
31012          * When a Brick is click
31013          * @param {Roo.bootstrap.Brick} this
31014          * @param {Roo.EventObject} e
31015          */
31016         "click" : true
31017     });
31018 };
31019
31020 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31021     
31022     /**
31023      * @cfg {String} title
31024      */   
31025     title : '',
31026     /**
31027      * @cfg {String} html
31028      */   
31029     html : '',
31030     /**
31031      * @cfg {String} bgimage
31032      */   
31033     bgimage : '',
31034     /**
31035      * @cfg {String} cls
31036      */   
31037     cls : '',
31038     /**
31039      * @cfg {String} href
31040      */   
31041     href : '',
31042     /**
31043      * @cfg {String} video
31044      */   
31045     video : '',
31046     /**
31047      * @cfg {Boolean} square
31048      */   
31049     square : true,
31050     
31051     getAutoCreate : function()
31052     {
31053         var cls = 'roo-brick';
31054         
31055         if(this.href.length){
31056             cls += ' roo-brick-link';
31057         }
31058         
31059         if(this.bgimage.length){
31060             cls += ' roo-brick-image';
31061         }
31062         
31063         if(!this.html.length && !this.bgimage.length){
31064             cls += ' roo-brick-center-title';
31065         }
31066         
31067         if(!this.html.length && this.bgimage.length){
31068             cls += ' roo-brick-bottom-title';
31069         }
31070         
31071         if(this.cls){
31072             cls += ' ' + this.cls;
31073         }
31074         
31075         var cfg = {
31076             tag: (this.href.length) ? 'a' : 'div',
31077             cls: cls,
31078             cn: [
31079                 {
31080                     tag: 'div',
31081                     cls: 'roo-brick-paragraph',
31082                     cn: []
31083                 }
31084             ]
31085         };
31086         
31087         if(this.href.length){
31088             cfg.href = this.href;
31089         }
31090         
31091         var cn = cfg.cn[0].cn;
31092         
31093         if(this.title.length){
31094             cn.push({
31095                 tag: 'h4',
31096                 cls: 'roo-brick-title',
31097                 html: this.title
31098             });
31099         }
31100         
31101         if(this.html.length){
31102             cn.push({
31103                 tag: 'p',
31104                 cls: 'roo-brick-text',
31105                 html: this.html
31106             });
31107         } else {
31108             cn.cls += ' hide';
31109         }
31110         
31111         if(this.bgimage.length){
31112             cfg.cn.push({
31113                 tag: 'img',
31114                 cls: 'roo-brick-image-view',
31115                 src: this.bgimage
31116             });
31117         }
31118         
31119         return cfg;
31120     },
31121     
31122     initEvents: function() 
31123     {
31124         if(this.title.length || this.html.length){
31125             this.el.on('mouseenter'  ,this.enter, this);
31126             this.el.on('mouseleave', this.leave, this);
31127         }
31128         
31129         
31130         Roo.EventManager.onWindowResize(this.resize, this); 
31131         
31132         this.resize();
31133     },
31134     
31135     resize : function()
31136     {
31137         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31138         
31139         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31140 //        paragraph.setHeight(paragraph.getWidth());
31141         
31142         if(this.bgimage.length){
31143             var image = this.el.select('.roo-brick-image-view', true).first();
31144             image.setWidth(paragraph.getWidth());
31145             image.setHeight(paragraph.getWidth());
31146         }
31147         
31148     },
31149     
31150     enter: function(e, el)
31151     {
31152         e.preventDefault();
31153         
31154         if(this.bgimage.length){
31155             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31156             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31157         }
31158     },
31159     
31160     leave: function(e, el)
31161     {
31162         e.preventDefault();
31163         
31164         if(this.bgimage.length){
31165             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31166             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31167         }
31168     }
31169     
31170 });
31171
31172  
31173
31174  /*
31175  * Based on:
31176  * Ext JS Library 1.1.1
31177  * Copyright(c) 2006-2007, Ext JS, LLC.
31178  *
31179  * Originally Released Under LGPL - original licence link has changed is not relivant.
31180  *
31181  * Fork - LGPL
31182  * <script type="text/javascript">
31183  */
31184
31185
31186 /**
31187  * @class Roo.bootstrap.SplitBar
31188  * @extends Roo.util.Observable
31189  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31190  * <br><br>
31191  * Usage:
31192  * <pre><code>
31193 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31194                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31195 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31196 split.minSize = 100;
31197 split.maxSize = 600;
31198 split.animate = true;
31199 split.on('moved', splitterMoved);
31200 </code></pre>
31201  * @constructor
31202  * Create a new SplitBar
31203  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31204  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31205  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31206  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31207                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31208                         position of the SplitBar).
31209  */
31210 Roo.bootstrap.SplitBar = function(cfg){
31211     
31212     /** @private */
31213     
31214     //{
31215     //  dragElement : elm
31216     //  resizingElement: el,
31217         // optional..
31218     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31219     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31220         // existingProxy ???
31221     //}
31222     
31223     this.el = Roo.get(cfg.dragElement, true);
31224     this.el.dom.unselectable = "on";
31225     /** @private */
31226     this.resizingEl = Roo.get(cfg.resizingElement, true);
31227
31228     /**
31229      * @private
31230      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31231      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31232      * @type Number
31233      */
31234     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31235     
31236     /**
31237      * The minimum size of the resizing element. (Defaults to 0)
31238      * @type Number
31239      */
31240     this.minSize = 0;
31241     
31242     /**
31243      * The maximum size of the resizing element. (Defaults to 2000)
31244      * @type Number
31245      */
31246     this.maxSize = 2000;
31247     
31248     /**
31249      * Whether to animate the transition to the new size
31250      * @type Boolean
31251      */
31252     this.animate = false;
31253     
31254     /**
31255      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31256      * @type Boolean
31257      */
31258     this.useShim = false;
31259     
31260     /** @private */
31261     this.shim = null;
31262     
31263     if(!cfg.existingProxy){
31264         /** @private */
31265         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31266     }else{
31267         this.proxy = Roo.get(cfg.existingProxy).dom;
31268     }
31269     /** @private */
31270     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31271     
31272     /** @private */
31273     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31274     
31275     /** @private */
31276     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31277     
31278     /** @private */
31279     this.dragSpecs = {};
31280     
31281     /**
31282      * @private The adapter to use to positon and resize elements
31283      */
31284     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31285     this.adapter.init(this);
31286     
31287     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31288         /** @private */
31289         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31290         this.el.addClass("roo-splitbar-h");
31291     }else{
31292         /** @private */
31293         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31294         this.el.addClass("roo-splitbar-v");
31295     }
31296     
31297     this.addEvents({
31298         /**
31299          * @event resize
31300          * Fires when the splitter is moved (alias for {@link #event-moved})
31301          * @param {Roo.bootstrap.SplitBar} this
31302          * @param {Number} newSize the new width or height
31303          */
31304         "resize" : true,
31305         /**
31306          * @event moved
31307          * Fires when the splitter is moved
31308          * @param {Roo.bootstrap.SplitBar} this
31309          * @param {Number} newSize the new width or height
31310          */
31311         "moved" : true,
31312         /**
31313          * @event beforeresize
31314          * Fires before the splitter is dragged
31315          * @param {Roo.bootstrap.SplitBar} this
31316          */
31317         "beforeresize" : true,
31318
31319         "beforeapply" : true
31320     });
31321
31322     Roo.util.Observable.call(this);
31323 };
31324
31325 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31326     onStartProxyDrag : function(x, y){
31327         this.fireEvent("beforeresize", this);
31328         if(!this.overlay){
31329             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31330             o.unselectable();
31331             o.enableDisplayMode("block");
31332             // all splitbars share the same overlay
31333             Roo.bootstrap.SplitBar.prototype.overlay = o;
31334         }
31335         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31336         this.overlay.show();
31337         Roo.get(this.proxy).setDisplayed("block");
31338         var size = this.adapter.getElementSize(this);
31339         this.activeMinSize = this.getMinimumSize();;
31340         this.activeMaxSize = this.getMaximumSize();;
31341         var c1 = size - this.activeMinSize;
31342         var c2 = Math.max(this.activeMaxSize - size, 0);
31343         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31344             this.dd.resetConstraints();
31345             this.dd.setXConstraint(
31346                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31347                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31348             );
31349             this.dd.setYConstraint(0, 0);
31350         }else{
31351             this.dd.resetConstraints();
31352             this.dd.setXConstraint(0, 0);
31353             this.dd.setYConstraint(
31354                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31355                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31356             );
31357          }
31358         this.dragSpecs.startSize = size;
31359         this.dragSpecs.startPoint = [x, y];
31360         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31361     },
31362     
31363     /** 
31364      * @private Called after the drag operation by the DDProxy
31365      */
31366     onEndProxyDrag : function(e){
31367         Roo.get(this.proxy).setDisplayed(false);
31368         var endPoint = Roo.lib.Event.getXY(e);
31369         if(this.overlay){
31370             this.overlay.hide();
31371         }
31372         var newSize;
31373         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31374             newSize = this.dragSpecs.startSize + 
31375                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31376                     endPoint[0] - this.dragSpecs.startPoint[0] :
31377                     this.dragSpecs.startPoint[0] - endPoint[0]
31378                 );
31379         }else{
31380             newSize = this.dragSpecs.startSize + 
31381                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31382                     endPoint[1] - this.dragSpecs.startPoint[1] :
31383                     this.dragSpecs.startPoint[1] - endPoint[1]
31384                 );
31385         }
31386         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31387         if(newSize != this.dragSpecs.startSize){
31388             if(this.fireEvent('beforeapply', this, newSize) !== false){
31389                 this.adapter.setElementSize(this, newSize);
31390                 this.fireEvent("moved", this, newSize);
31391                 this.fireEvent("resize", this, newSize);
31392             }
31393         }
31394     },
31395     
31396     /**
31397      * Get the adapter this SplitBar uses
31398      * @return The adapter object
31399      */
31400     getAdapter : function(){
31401         return this.adapter;
31402     },
31403     
31404     /**
31405      * Set the adapter this SplitBar uses
31406      * @param {Object} adapter A SplitBar adapter object
31407      */
31408     setAdapter : function(adapter){
31409         this.adapter = adapter;
31410         this.adapter.init(this);
31411     },
31412     
31413     /**
31414      * Gets the minimum size for the resizing element
31415      * @return {Number} The minimum size
31416      */
31417     getMinimumSize : function(){
31418         return this.minSize;
31419     },
31420     
31421     /**
31422      * Sets the minimum size for the resizing element
31423      * @param {Number} minSize The minimum size
31424      */
31425     setMinimumSize : function(minSize){
31426         this.minSize = minSize;
31427     },
31428     
31429     /**
31430      * Gets the maximum size for the resizing element
31431      * @return {Number} The maximum size
31432      */
31433     getMaximumSize : function(){
31434         return this.maxSize;
31435     },
31436     
31437     /**
31438      * Sets the maximum size for the resizing element
31439      * @param {Number} maxSize The maximum size
31440      */
31441     setMaximumSize : function(maxSize){
31442         this.maxSize = maxSize;
31443     },
31444     
31445     /**
31446      * Sets the initialize size for the resizing element
31447      * @param {Number} size The initial size
31448      */
31449     setCurrentSize : function(size){
31450         var oldAnimate = this.animate;
31451         this.animate = false;
31452         this.adapter.setElementSize(this, size);
31453         this.animate = oldAnimate;
31454     },
31455     
31456     /**
31457      * Destroy this splitbar. 
31458      * @param {Boolean} removeEl True to remove the element
31459      */
31460     destroy : function(removeEl){
31461         if(this.shim){
31462             this.shim.remove();
31463         }
31464         this.dd.unreg();
31465         this.proxy.parentNode.removeChild(this.proxy);
31466         if(removeEl){
31467             this.el.remove();
31468         }
31469     }
31470 });
31471
31472 /**
31473  * @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.
31474  */
31475 Roo.bootstrap.SplitBar.createProxy = function(dir){
31476     var proxy = new Roo.Element(document.createElement("div"));
31477     proxy.unselectable();
31478     var cls = 'roo-splitbar-proxy';
31479     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31480     document.body.appendChild(proxy.dom);
31481     return proxy.dom;
31482 };
31483
31484 /** 
31485  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31486  * Default Adapter. It assumes the splitter and resizing element are not positioned
31487  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31488  */
31489 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31490 };
31491
31492 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31493     // do nothing for now
31494     init : function(s){
31495     
31496     },
31497     /**
31498      * Called before drag operations to get the current size of the resizing element. 
31499      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31500      */
31501      getElementSize : function(s){
31502         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31503             return s.resizingEl.getWidth();
31504         }else{
31505             return s.resizingEl.getHeight();
31506         }
31507     },
31508     
31509     /**
31510      * Called after drag operations to set the size of the resizing element.
31511      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31512      * @param {Number} newSize The new size to set
31513      * @param {Function} onComplete A function to be invoked when resizing is complete
31514      */
31515     setElementSize : function(s, newSize, onComplete){
31516         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31517             if(!s.animate){
31518                 s.resizingEl.setWidth(newSize);
31519                 if(onComplete){
31520                     onComplete(s, newSize);
31521                 }
31522             }else{
31523                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31524             }
31525         }else{
31526             
31527             if(!s.animate){
31528                 s.resizingEl.setHeight(newSize);
31529                 if(onComplete){
31530                     onComplete(s, newSize);
31531                 }
31532             }else{
31533                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31534             }
31535         }
31536     }
31537 };
31538
31539 /** 
31540  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31541  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31542  * Adapter that  moves the splitter element to align with the resized sizing element. 
31543  * Used with an absolute positioned SplitBar.
31544  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31545  * document.body, make sure you assign an id to the body element.
31546  */
31547 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31548     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31549     this.container = Roo.get(container);
31550 };
31551
31552 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31553     init : function(s){
31554         this.basic.init(s);
31555     },
31556     
31557     getElementSize : function(s){
31558         return this.basic.getElementSize(s);
31559     },
31560     
31561     setElementSize : function(s, newSize, onComplete){
31562         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31563     },
31564     
31565     moveSplitter : function(s){
31566         var yes = Roo.bootstrap.SplitBar;
31567         switch(s.placement){
31568             case yes.LEFT:
31569                 s.el.setX(s.resizingEl.getRight());
31570                 break;
31571             case yes.RIGHT:
31572                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31573                 break;
31574             case yes.TOP:
31575                 s.el.setY(s.resizingEl.getBottom());
31576                 break;
31577             case yes.BOTTOM:
31578                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31579                 break;
31580         }
31581     }
31582 };
31583
31584 /**
31585  * Orientation constant - Create a vertical SplitBar
31586  * @static
31587  * @type Number
31588  */
31589 Roo.bootstrap.SplitBar.VERTICAL = 1;
31590
31591 /**
31592  * Orientation constant - Create a horizontal SplitBar
31593  * @static
31594  * @type Number
31595  */
31596 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31597
31598 /**
31599  * Placement constant - The resizing element is to the left of the splitter element
31600  * @static
31601  * @type Number
31602  */
31603 Roo.bootstrap.SplitBar.LEFT = 1;
31604
31605 /**
31606  * Placement constant - The resizing element is to the right of the splitter element
31607  * @static
31608  * @type Number
31609  */
31610 Roo.bootstrap.SplitBar.RIGHT = 2;
31611
31612 /**
31613  * Placement constant - The resizing element is positioned above the splitter element
31614  * @static
31615  * @type Number
31616  */
31617 Roo.bootstrap.SplitBar.TOP = 3;
31618
31619 /**
31620  * Placement constant - The resizing element is positioned under splitter element
31621  * @static
31622  * @type Number
31623  */
31624 Roo.bootstrap.SplitBar.BOTTOM = 4;
31625 Roo.namespace("Roo.bootstrap.layout");/*
31626  * Based on:
31627  * Ext JS Library 1.1.1
31628  * Copyright(c) 2006-2007, Ext JS, LLC.
31629  *
31630  * Originally Released Under LGPL - original licence link has changed is not relivant.
31631  *
31632  * Fork - LGPL
31633  * <script type="text/javascript">
31634  */
31635  
31636 /**
31637  * @class Roo.bootstrap.layout.Manager
31638  * @extends Roo.bootstrap.Component
31639  * Base class for layout managers.
31640  */
31641 Roo.bootstrap.layout.Manager = function(config)
31642 {
31643     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31644     
31645     
31646      
31647     
31648     
31649     /** false to disable window resize monitoring @type Boolean */
31650     this.monitorWindowResize = true;
31651     this.regions = {};
31652     this.addEvents({
31653         /**
31654          * @event layout
31655          * Fires when a layout is performed. 
31656          * @param {Roo.LayoutManager} this
31657          */
31658         "layout" : true,
31659         /**
31660          * @event regionresized
31661          * Fires when the user resizes a region. 
31662          * @param {Roo.LayoutRegion} region The resized region
31663          * @param {Number} newSize The new size (width for east/west, height for north/south)
31664          */
31665         "regionresized" : true,
31666         /**
31667          * @event regioncollapsed
31668          * Fires when a region is collapsed. 
31669          * @param {Roo.LayoutRegion} region The collapsed region
31670          */
31671         "regioncollapsed" : true,
31672         /**
31673          * @event regionexpanded
31674          * Fires when a region is expanded.  
31675          * @param {Roo.LayoutRegion} region The expanded region
31676          */
31677         "regionexpanded" : true
31678     });
31679     this.updating = false;
31680     
31681     if (config.el) {
31682         this.el = Roo.get(config.el);
31683         this.initEvents();
31684     }
31685     
31686 };
31687
31688 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31689     
31690     
31691     regions : null,
31692     
31693     monitorWindowResize : true,
31694     
31695     
31696     updating : false,
31697     
31698     
31699     onRender : function(ct, position)
31700     {
31701         if(!this.el){
31702             this.el = Roo.get(ct);
31703             this.initEvents();
31704         }
31705     },
31706     
31707     
31708     initEvents: function()
31709     {
31710         
31711         
31712         // ie scrollbar fix
31713         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31714             document.body.scroll = "no";
31715         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31716             this.el.position('relative');
31717         }
31718         this.id = this.el.id;
31719         this.el.addClass("roo-layout-container");
31720         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31721         if(this.el.dom != document.body ) {
31722             this.el.on('resize', this.layout,this);
31723             this.el.on('show', this.layout,this);
31724         }
31725
31726     },
31727     
31728     /**
31729      * Returns true if this layout is currently being updated
31730      * @return {Boolean}
31731      */
31732     isUpdating : function(){
31733         return this.updating; 
31734     },
31735     
31736     /**
31737      * Suspend the LayoutManager from doing auto-layouts while
31738      * making multiple add or remove calls
31739      */
31740     beginUpdate : function(){
31741         this.updating = true;    
31742     },
31743     
31744     /**
31745      * Restore auto-layouts and optionally disable the manager from performing a layout
31746      * @param {Boolean} noLayout true to disable a layout update 
31747      */
31748     endUpdate : function(noLayout){
31749         this.updating = false;
31750         if(!noLayout){
31751             this.layout();
31752         }    
31753     },
31754     
31755     layout: function(){
31756         // abstract...
31757     },
31758     
31759     onRegionResized : function(region, newSize){
31760         this.fireEvent("regionresized", region, newSize);
31761         this.layout();
31762     },
31763     
31764     onRegionCollapsed : function(region){
31765         this.fireEvent("regioncollapsed", region);
31766     },
31767     
31768     onRegionExpanded : function(region){
31769         this.fireEvent("regionexpanded", region);
31770     },
31771         
31772     /**
31773      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31774      * performs box-model adjustments.
31775      * @return {Object} The size as an object {width: (the width), height: (the height)}
31776      */
31777     getViewSize : function()
31778     {
31779         var size;
31780         if(this.el.dom != document.body){
31781             size = this.el.getSize();
31782         }else{
31783             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31784         }
31785         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31786         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31787         return size;
31788     },
31789     
31790     /**
31791      * Returns the Element this layout is bound to.
31792      * @return {Roo.Element}
31793      */
31794     getEl : function(){
31795         return this.el;
31796     },
31797     
31798     /**
31799      * Returns the specified region.
31800      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31801      * @return {Roo.LayoutRegion}
31802      */
31803     getRegion : function(target){
31804         return this.regions[target.toLowerCase()];
31805     },
31806     
31807     onWindowResize : function(){
31808         if(this.monitorWindowResize){
31809             this.layout();
31810         }
31811     }
31812 });/*
31813  * Based on:
31814  * Ext JS Library 1.1.1
31815  * Copyright(c) 2006-2007, Ext JS, LLC.
31816  *
31817  * Originally Released Under LGPL - original licence link has changed is not relivant.
31818  *
31819  * Fork - LGPL
31820  * <script type="text/javascript">
31821  */
31822 /**
31823  * @class Roo.bootstrap.layout.Border
31824  * @extends Roo.bootstrap.layout.Manager
31825  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31826  * please see: examples/bootstrap/nested.html<br><br>
31827  
31828 <b>The container the layout is rendered into can be either the body element or any other element.
31829 If it is not the body element, the container needs to either be an absolute positioned element,
31830 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31831 the container size if it is not the body element.</b>
31832
31833 * @constructor
31834 * Create a new Border
31835 * @param {Object} config Configuration options
31836  */
31837 Roo.bootstrap.layout.Border = function(config){
31838     config = config || {};
31839     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31840     
31841     
31842     
31843     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31844         if(config[region]){
31845             config[region].region = region;
31846             this.addRegion(config[region]);
31847         }
31848     },this);
31849     
31850 };
31851
31852 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31853
31854 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31855     /**
31856      * Creates and adds a new region if it doesn't already exist.
31857      * @param {String} target The target region key (north, south, east, west or center).
31858      * @param {Object} config The regions config object
31859      * @return {BorderLayoutRegion} The new region
31860      */
31861     addRegion : function(config)
31862     {
31863         if(!this.regions[config.region]){
31864             var r = this.factory(config);
31865             this.bindRegion(r);
31866         }
31867         return this.regions[config.region];
31868     },
31869
31870     // private (kinda)
31871     bindRegion : function(r){
31872         this.regions[r.config.region] = r;
31873         
31874         r.on("visibilitychange",    this.layout, this);
31875         r.on("paneladded",          this.layout, this);
31876         r.on("panelremoved",        this.layout, this);
31877         r.on("invalidated",         this.layout, this);
31878         r.on("resized",             this.onRegionResized, this);
31879         r.on("collapsed",           this.onRegionCollapsed, this);
31880         r.on("expanded",            this.onRegionExpanded, this);
31881     },
31882
31883     /**
31884      * Performs a layout update.
31885      */
31886     layout : function()
31887     {
31888         if(this.updating) {
31889             return;
31890         }
31891         
31892         // render all the rebions if they have not been done alreayd?
31893         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31894             if(this.regions[region] && !this.regions[region].bodyEl){
31895                 this.regions[region].onRender(this.el)
31896             }
31897         },this);
31898         
31899         var size = this.getViewSize();
31900         var w = size.width;
31901         var h = size.height;
31902         var centerW = w;
31903         var centerH = h;
31904         var centerY = 0;
31905         var centerX = 0;
31906         //var x = 0, y = 0;
31907
31908         var rs = this.regions;
31909         var north = rs["north"];
31910         var south = rs["south"]; 
31911         var west = rs["west"];
31912         var east = rs["east"];
31913         var center = rs["center"];
31914         //if(this.hideOnLayout){ // not supported anymore
31915             //c.el.setStyle("display", "none");
31916         //}
31917         if(north && north.isVisible()){
31918             var b = north.getBox();
31919             var m = north.getMargins();
31920             b.width = w - (m.left+m.right);
31921             b.x = m.left;
31922             b.y = m.top;
31923             centerY = b.height + b.y + m.bottom;
31924             centerH -= centerY;
31925             north.updateBox(this.safeBox(b));
31926         }
31927         if(south && south.isVisible()){
31928             var b = south.getBox();
31929             var m = south.getMargins();
31930             b.width = w - (m.left+m.right);
31931             b.x = m.left;
31932             var totalHeight = (b.height + m.top + m.bottom);
31933             b.y = h - totalHeight + m.top;
31934             centerH -= totalHeight;
31935             south.updateBox(this.safeBox(b));
31936         }
31937         if(west && west.isVisible()){
31938             var b = west.getBox();
31939             var m = west.getMargins();
31940             b.height = centerH - (m.top+m.bottom);
31941             b.x = m.left;
31942             b.y = centerY + m.top;
31943             var totalWidth = (b.width + m.left + m.right);
31944             centerX += totalWidth;
31945             centerW -= totalWidth;
31946             west.updateBox(this.safeBox(b));
31947         }
31948         if(east && east.isVisible()){
31949             var b = east.getBox();
31950             var m = east.getMargins();
31951             b.height = centerH - (m.top+m.bottom);
31952             var totalWidth = (b.width + m.left + m.right);
31953             b.x = w - totalWidth + m.left;
31954             b.y = centerY + m.top;
31955             centerW -= totalWidth;
31956             east.updateBox(this.safeBox(b));
31957         }
31958         if(center){
31959             var m = center.getMargins();
31960             var centerBox = {
31961                 x: centerX + m.left,
31962                 y: centerY + m.top,
31963                 width: centerW - (m.left+m.right),
31964                 height: centerH - (m.top+m.bottom)
31965             };
31966             //if(this.hideOnLayout){
31967                 //center.el.setStyle("display", "block");
31968             //}
31969             center.updateBox(this.safeBox(centerBox));
31970         }
31971         this.el.repaint();
31972         this.fireEvent("layout", this);
31973     },
31974
31975     // private
31976     safeBox : function(box){
31977         box.width = Math.max(0, box.width);
31978         box.height = Math.max(0, box.height);
31979         return box;
31980     },
31981
31982     /**
31983      * Adds a ContentPanel (or subclass) to this layout.
31984      * @param {String} target The target region key (north, south, east, west or center).
31985      * @param {Roo.ContentPanel} panel The panel to add
31986      * @return {Roo.ContentPanel} The added panel
31987      */
31988     add : function(target, panel){
31989          
31990         target = target.toLowerCase();
31991         return this.regions[target].add(panel);
31992     },
31993
31994     /**
31995      * Remove a ContentPanel (or subclass) to this layout.
31996      * @param {String} target The target region key (north, south, east, west or center).
31997      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31998      * @return {Roo.ContentPanel} The removed panel
31999      */
32000     remove : function(target, panel){
32001         target = target.toLowerCase();
32002         return this.regions[target].remove(panel);
32003     },
32004
32005     /**
32006      * Searches all regions for a panel with the specified id
32007      * @param {String} panelId
32008      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32009      */
32010     findPanel : function(panelId){
32011         var rs = this.regions;
32012         for(var target in rs){
32013             if(typeof rs[target] != "function"){
32014                 var p = rs[target].getPanel(panelId);
32015                 if(p){
32016                     return p;
32017                 }
32018             }
32019         }
32020         return null;
32021     },
32022
32023     /**
32024      * Searches all regions for a panel with the specified id and activates (shows) it.
32025      * @param {String/ContentPanel} panelId The panels id or the panel itself
32026      * @return {Roo.ContentPanel} The shown panel or null
32027      */
32028     showPanel : function(panelId) {
32029       var rs = this.regions;
32030       for(var target in rs){
32031          var r = rs[target];
32032          if(typeof r != "function"){
32033             if(r.hasPanel(panelId)){
32034                return r.showPanel(panelId);
32035             }
32036          }
32037       }
32038       return null;
32039    },
32040
32041    /**
32042      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32043      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32044      */
32045    /*
32046     restoreState : function(provider){
32047         if(!provider){
32048             provider = Roo.state.Manager;
32049         }
32050         var sm = new Roo.LayoutStateManager();
32051         sm.init(this, provider);
32052     },
32053 */
32054  
32055  
32056     /**
32057      * Adds a xtype elements to the layout.
32058      * <pre><code>
32059
32060 layout.addxtype({
32061        xtype : 'ContentPanel',
32062        region: 'west',
32063        items: [ .... ]
32064    }
32065 );
32066
32067 layout.addxtype({
32068         xtype : 'NestedLayoutPanel',
32069         region: 'west',
32070         layout: {
32071            center: { },
32072            west: { }   
32073         },
32074         items : [ ... list of content panels or nested layout panels.. ]
32075    }
32076 );
32077 </code></pre>
32078      * @param {Object} cfg Xtype definition of item to add.
32079      */
32080     addxtype : function(cfg)
32081     {
32082         // basically accepts a pannel...
32083         // can accept a layout region..!?!?
32084         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32085         
32086         
32087         // theory?  children can only be panels??
32088         
32089         //if (!cfg.xtype.match(/Panel$/)) {
32090         //    return false;
32091         //}
32092         var ret = false;
32093         
32094         if (typeof(cfg.region) == 'undefined') {
32095             Roo.log("Failed to add Panel, region was not set");
32096             Roo.log(cfg);
32097             return false;
32098         }
32099         var region = cfg.region;
32100         delete cfg.region;
32101         
32102           
32103         var xitems = [];
32104         if (cfg.items) {
32105             xitems = cfg.items;
32106             delete cfg.items;
32107         }
32108         var nb = false;
32109         
32110         switch(cfg.xtype) 
32111         {
32112             case 'Content':  // ContentPanel (el, cfg)
32113             case 'Scroll':  // ContentPanel (el, cfg)
32114             case 'View': 
32115                 cfg.autoCreate = true;
32116                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32117                 //} else {
32118                 //    var el = this.el.createChild();
32119                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32120                 //}
32121                 
32122                 this.add(region, ret);
32123                 break;
32124             
32125             /*
32126             case 'TreePanel': // our new panel!
32127                 cfg.el = this.el.createChild();
32128                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32129                 this.add(region, ret);
32130                 break;
32131             */
32132             
32133             case 'Nest': 
32134                 // create a new Layout (which is  a Border Layout...
32135                 
32136                 var clayout = cfg.layout;
32137                 clayout.el  = this.el.createChild();
32138                 clayout.items   = clayout.items  || [];
32139                 
32140                 delete cfg.layout;
32141                 
32142                 // replace this exitems with the clayout ones..
32143                 xitems = clayout.items;
32144                  
32145                 // force background off if it's in center...
32146                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32147                     cfg.background = false;
32148                 }
32149                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
32150                 
32151                 
32152                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32153                 //console.log('adding nested layout panel '  + cfg.toSource());
32154                 this.add(region, ret);
32155                 nb = {}; /// find first...
32156                 break;
32157             
32158             case 'Grid':
32159                 
32160                 // needs grid and region
32161                 
32162                 //var el = this.getRegion(region).el.createChild();
32163                 /*
32164                  *var el = this.el.createChild();
32165                 // create the grid first...
32166                 cfg.grid.container = el;
32167                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32168                 */
32169                 
32170                 if (region == 'center' && this.active ) {
32171                     cfg.background = false;
32172                 }
32173                 
32174                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32175                 
32176                 this.add(region, ret);
32177                 /*
32178                 if (cfg.background) {
32179                     // render grid on panel activation (if panel background)
32180                     ret.on('activate', function(gp) {
32181                         if (!gp.grid.rendered) {
32182                     //        gp.grid.render(el);
32183                         }
32184                     });
32185                 } else {
32186                   //  cfg.grid.render(el);
32187                 }
32188                 */
32189                 break;
32190            
32191            
32192             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32193                 // it was the old xcomponent building that caused this before.
32194                 // espeically if border is the top element in the tree.
32195                 ret = this;
32196                 break; 
32197                 
32198                     
32199                 
32200                 
32201                 
32202             default:
32203                 /*
32204                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32205                     
32206                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32207                     this.add(region, ret);
32208                 } else {
32209                 */
32210                     Roo.log(cfg);
32211                     throw "Can not add '" + cfg.xtype + "' to Border";
32212                     return null;
32213              
32214                                 
32215              
32216         }
32217         this.beginUpdate();
32218         // add children..
32219         var region = '';
32220         var abn = {};
32221         Roo.each(xitems, function(i)  {
32222             region = nb && i.region ? i.region : false;
32223             
32224             var add = ret.addxtype(i);
32225            
32226             if (region) {
32227                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32228                 if (!i.background) {
32229                     abn[region] = nb[region] ;
32230                 }
32231             }
32232             
32233         });
32234         this.endUpdate();
32235
32236         // make the last non-background panel active..
32237         //if (nb) { Roo.log(abn); }
32238         if (nb) {
32239             
32240             for(var r in abn) {
32241                 region = this.getRegion(r);
32242                 if (region) {
32243                     // tried using nb[r], but it does not work..
32244                      
32245                     region.showPanel(abn[r]);
32246                    
32247                 }
32248             }
32249         }
32250         return ret;
32251         
32252     },
32253     
32254     
32255 // private
32256     factory : function(cfg)
32257     {
32258         
32259         var validRegions = Roo.bootstrap.layout.Border.regions;
32260
32261         var target = cfg.region;
32262         cfg.mgr = this;
32263         
32264         var r = Roo.bootstrap.layout;
32265         Roo.log(target);
32266         switch(target){
32267             case "north":
32268                 return new r.North(cfg);
32269             case "south":
32270                 return new r.South(cfg);
32271             case "east":
32272                 return new r.East(cfg);
32273             case "west":
32274                 return new r.West(cfg);
32275             case "center":
32276                 return new r.Center(cfg);
32277         }
32278         throw 'Layout region "'+target+'" not supported.';
32279     }
32280     
32281     
32282 });
32283  /*
32284  * Based on:
32285  * Ext JS Library 1.1.1
32286  * Copyright(c) 2006-2007, Ext JS, LLC.
32287  *
32288  * Originally Released Under LGPL - original licence link has changed is not relivant.
32289  *
32290  * Fork - LGPL
32291  * <script type="text/javascript">
32292  */
32293  
32294 /**
32295  * @class Roo.bootstrap.layout.Basic
32296  * @extends Roo.util.Observable
32297  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32298  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32299  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32300  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32301  * @cfg {string}   region  the region that it inhabits..
32302  * @cfg {bool}   skipConfig skip config?
32303  * 
32304
32305  */
32306 Roo.bootstrap.layout.Basic = function(config){
32307     
32308     this.mgr = config.mgr;
32309     
32310     this.position = config.region;
32311     
32312     var skipConfig = config.skipConfig;
32313     
32314     this.events = {
32315         /**
32316          * @scope Roo.BasicLayoutRegion
32317          */
32318         
32319         /**
32320          * @event beforeremove
32321          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32322          * @param {Roo.LayoutRegion} this
32323          * @param {Roo.ContentPanel} panel The panel
32324          * @param {Object} e The cancel event object
32325          */
32326         "beforeremove" : true,
32327         /**
32328          * @event invalidated
32329          * Fires when the layout for this region is changed.
32330          * @param {Roo.LayoutRegion} this
32331          */
32332         "invalidated" : true,
32333         /**
32334          * @event visibilitychange
32335          * Fires when this region is shown or hidden 
32336          * @param {Roo.LayoutRegion} this
32337          * @param {Boolean} visibility true or false
32338          */
32339         "visibilitychange" : true,
32340         /**
32341          * @event paneladded
32342          * Fires when a panel is added. 
32343          * @param {Roo.LayoutRegion} this
32344          * @param {Roo.ContentPanel} panel The panel
32345          */
32346         "paneladded" : true,
32347         /**
32348          * @event panelremoved
32349          * Fires when a panel is removed. 
32350          * @param {Roo.LayoutRegion} this
32351          * @param {Roo.ContentPanel} panel The panel
32352          */
32353         "panelremoved" : true,
32354         /**
32355          * @event beforecollapse
32356          * Fires when this region before collapse.
32357          * @param {Roo.LayoutRegion} this
32358          */
32359         "beforecollapse" : true,
32360         /**
32361          * @event collapsed
32362          * Fires when this region is collapsed.
32363          * @param {Roo.LayoutRegion} this
32364          */
32365         "collapsed" : true,
32366         /**
32367          * @event expanded
32368          * Fires when this region is expanded.
32369          * @param {Roo.LayoutRegion} this
32370          */
32371         "expanded" : true,
32372         /**
32373          * @event slideshow
32374          * Fires when this region is slid into view.
32375          * @param {Roo.LayoutRegion} this
32376          */
32377         "slideshow" : true,
32378         /**
32379          * @event slidehide
32380          * Fires when this region slides out of view. 
32381          * @param {Roo.LayoutRegion} this
32382          */
32383         "slidehide" : true,
32384         /**
32385          * @event panelactivated
32386          * Fires when a panel is activated. 
32387          * @param {Roo.LayoutRegion} this
32388          * @param {Roo.ContentPanel} panel The activated panel
32389          */
32390         "panelactivated" : true,
32391         /**
32392          * @event resized
32393          * Fires when the user resizes this region. 
32394          * @param {Roo.LayoutRegion} this
32395          * @param {Number} newSize The new size (width for east/west, height for north/south)
32396          */
32397         "resized" : true
32398     };
32399     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32400     this.panels = new Roo.util.MixedCollection();
32401     this.panels.getKey = this.getPanelId.createDelegate(this);
32402     this.box = null;
32403     this.activePanel = null;
32404     // ensure listeners are added...
32405     
32406     if (config.listeners || config.events) {
32407         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32408             listeners : config.listeners || {},
32409             events : config.events || {}
32410         });
32411     }
32412     
32413     if(skipConfig !== true){
32414         this.applyConfig(config);
32415     }
32416 };
32417
32418 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32419 {
32420     getPanelId : function(p){
32421         return p.getId();
32422     },
32423     
32424     applyConfig : function(config){
32425         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32426         this.config = config;
32427         
32428     },
32429     
32430     /**
32431      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32432      * the width, for horizontal (north, south) the height.
32433      * @param {Number} newSize The new width or height
32434      */
32435     resizeTo : function(newSize){
32436         var el = this.el ? this.el :
32437                  (this.activePanel ? this.activePanel.getEl() : null);
32438         if(el){
32439             switch(this.position){
32440                 case "east":
32441                 case "west":
32442                     el.setWidth(newSize);
32443                     this.fireEvent("resized", this, newSize);
32444                 break;
32445                 case "north":
32446                 case "south":
32447                     el.setHeight(newSize);
32448                     this.fireEvent("resized", this, newSize);
32449                 break;                
32450             }
32451         }
32452     },
32453     
32454     getBox : function(){
32455         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32456     },
32457     
32458     getMargins : function(){
32459         return this.margins;
32460     },
32461     
32462     updateBox : function(box){
32463         this.box = box;
32464         var el = this.activePanel.getEl();
32465         el.dom.style.left = box.x + "px";
32466         el.dom.style.top = box.y + "px";
32467         this.activePanel.setSize(box.width, box.height);
32468     },
32469     
32470     /**
32471      * Returns the container element for this region.
32472      * @return {Roo.Element}
32473      */
32474     getEl : function(){
32475         return this.activePanel;
32476     },
32477     
32478     /**
32479      * Returns true if this region is currently visible.
32480      * @return {Boolean}
32481      */
32482     isVisible : function(){
32483         return this.activePanel ? true : false;
32484     },
32485     
32486     setActivePanel : function(panel){
32487         panel = this.getPanel(panel);
32488         if(this.activePanel && this.activePanel != panel){
32489             this.activePanel.setActiveState(false);
32490             this.activePanel.getEl().setLeftTop(-10000,-10000);
32491         }
32492         this.activePanel = panel;
32493         panel.setActiveState(true);
32494         if(this.box){
32495             panel.setSize(this.box.width, this.box.height);
32496         }
32497         this.fireEvent("panelactivated", this, panel);
32498         this.fireEvent("invalidated");
32499     },
32500     
32501     /**
32502      * Show the specified panel.
32503      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32504      * @return {Roo.ContentPanel} The shown panel or null
32505      */
32506     showPanel : function(panel){
32507         panel = this.getPanel(panel);
32508         if(panel){
32509             this.setActivePanel(panel);
32510         }
32511         return panel;
32512     },
32513     
32514     /**
32515      * Get the active panel for this region.
32516      * @return {Roo.ContentPanel} The active panel or null
32517      */
32518     getActivePanel : function(){
32519         return this.activePanel;
32520     },
32521     
32522     /**
32523      * Add the passed ContentPanel(s)
32524      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32525      * @return {Roo.ContentPanel} The panel added (if only one was added)
32526      */
32527     add : function(panel){
32528         if(arguments.length > 1){
32529             for(var i = 0, len = arguments.length; i < len; i++) {
32530                 this.add(arguments[i]);
32531             }
32532             return null;
32533         }
32534         if(this.hasPanel(panel)){
32535             this.showPanel(panel);
32536             return panel;
32537         }
32538         var el = panel.getEl();
32539         if(el.dom.parentNode != this.mgr.el.dom){
32540             this.mgr.el.dom.appendChild(el.dom);
32541         }
32542         if(panel.setRegion){
32543             panel.setRegion(this);
32544         }
32545         this.panels.add(panel);
32546         el.setStyle("position", "absolute");
32547         if(!panel.background){
32548             this.setActivePanel(panel);
32549             if(this.config.initialSize && this.panels.getCount()==1){
32550                 this.resizeTo(this.config.initialSize);
32551             }
32552         }
32553         this.fireEvent("paneladded", this, panel);
32554         return panel;
32555     },
32556     
32557     /**
32558      * Returns true if the panel is in this region.
32559      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32560      * @return {Boolean}
32561      */
32562     hasPanel : function(panel){
32563         if(typeof panel == "object"){ // must be panel obj
32564             panel = panel.getId();
32565         }
32566         return this.getPanel(panel) ? true : false;
32567     },
32568     
32569     /**
32570      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32571      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32572      * @param {Boolean} preservePanel Overrides the config preservePanel option
32573      * @return {Roo.ContentPanel} The panel that was removed
32574      */
32575     remove : function(panel, preservePanel){
32576         panel = this.getPanel(panel);
32577         if(!panel){
32578             return null;
32579         }
32580         var e = {};
32581         this.fireEvent("beforeremove", this, panel, e);
32582         if(e.cancel === true){
32583             return null;
32584         }
32585         var panelId = panel.getId();
32586         this.panels.removeKey(panelId);
32587         return panel;
32588     },
32589     
32590     /**
32591      * Returns the panel specified or null if it's not in this region.
32592      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32593      * @return {Roo.ContentPanel}
32594      */
32595     getPanel : function(id){
32596         if(typeof id == "object"){ // must be panel obj
32597             return id;
32598         }
32599         return this.panels.get(id);
32600     },
32601     
32602     /**
32603      * Returns this regions position (north/south/east/west/center).
32604      * @return {String} 
32605      */
32606     getPosition: function(){
32607         return this.position;    
32608     }
32609 });/*
32610  * Based on:
32611  * Ext JS Library 1.1.1
32612  * Copyright(c) 2006-2007, Ext JS, LLC.
32613  *
32614  * Originally Released Under LGPL - original licence link has changed is not relivant.
32615  *
32616  * Fork - LGPL
32617  * <script type="text/javascript">
32618  */
32619  
32620 /**
32621  * @class Roo.bootstrap.layout.Region
32622  * @extends Roo.bootstrap.layout.Basic
32623  * This class represents a region in a layout manager.
32624  
32625  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32626  * @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})
32627  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32628  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32629  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32630  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32631  * @cfg {String}    title           The title for the region (overrides panel titles)
32632  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32633  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32634  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32635  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32636  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32637  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32638  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32639  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32640  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32641  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32642
32643  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32644  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32645  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32646  * @cfg {Number}    width           For East/West panels
32647  * @cfg {Number}    height          For North/South panels
32648  * @cfg {Boolean}   split           To show the splitter
32649  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32650  * 
32651  * @cfg {string}   cls             Extra CSS classes to add to region
32652  * 
32653  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32654  * @cfg {string}   region  the region that it inhabits..
32655  *
32656
32657  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32658  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32659
32660  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32661  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32662  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32663  */
32664 Roo.bootstrap.layout.Region = function(config)
32665 {
32666     this.applyConfig(config);
32667
32668     var mgr = config.mgr;
32669     var pos = config.region;
32670     config.skipConfig = true;
32671     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32672     
32673     if (mgr.el) {
32674         this.onRender(mgr.el);   
32675     }
32676      
32677     this.visible = true;
32678     this.collapsed = false;
32679     this.unrendered_panels = [];
32680 };
32681
32682 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32683
32684     position: '', // set by wrapper (eg. north/south etc..)
32685     unrendered_panels : null,  // unrendered panels.
32686     createBody : function(){
32687         /** This region's body element 
32688         * @type Roo.Element */
32689         this.bodyEl = this.el.createChild({
32690                 tag: "div",
32691                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32692         });
32693     },
32694
32695     onRender: function(ctr, pos)
32696     {
32697         var dh = Roo.DomHelper;
32698         /** This region's container element 
32699         * @type Roo.Element */
32700         this.el = dh.append(ctr.dom, {
32701                 tag: "div",
32702                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32703             }, true);
32704         /** This region's title element 
32705         * @type Roo.Element */
32706     
32707         this.titleEl = dh.append(this.el.dom,
32708             {
32709                     tag: "div",
32710                     unselectable: "on",
32711                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32712                     children:[
32713                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32714                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32715                     ]}, true);
32716         
32717         this.titleEl.enableDisplayMode();
32718         /** This region's title text element 
32719         * @type HTMLElement */
32720         this.titleTextEl = this.titleEl.dom.firstChild;
32721         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32722         /*
32723         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32724         this.closeBtn.enableDisplayMode();
32725         this.closeBtn.on("click", this.closeClicked, this);
32726         this.closeBtn.hide();
32727     */
32728         this.createBody(this.config);
32729         if(this.config.hideWhenEmpty){
32730             this.hide();
32731             this.on("paneladded", this.validateVisibility, this);
32732             this.on("panelremoved", this.validateVisibility, this);
32733         }
32734         if(this.autoScroll){
32735             this.bodyEl.setStyle("overflow", "auto");
32736         }else{
32737             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32738         }
32739         //if(c.titlebar !== false){
32740             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32741                 this.titleEl.hide();
32742             }else{
32743                 this.titleEl.show();
32744                 if(this.config.title){
32745                     this.titleTextEl.innerHTML = this.config.title;
32746                 }
32747             }
32748         //}
32749         if(this.config.collapsed){
32750             this.collapse(true);
32751         }
32752         if(this.config.hidden){
32753             this.hide();
32754         }
32755         
32756         if (this.unrendered_panels && this.unrendered_panels.length) {
32757             for (var i =0;i< this.unrendered_panels.length; i++) {
32758                 this.add(this.unrendered_panels[i]);
32759             }
32760             this.unrendered_panels = null;
32761             
32762         }
32763         
32764     },
32765     
32766     applyConfig : function(c)
32767     {
32768         /*
32769          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32770             var dh = Roo.DomHelper;
32771             if(c.titlebar !== false){
32772                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32773                 this.collapseBtn.on("click", this.collapse, this);
32774                 this.collapseBtn.enableDisplayMode();
32775                 /*
32776                 if(c.showPin === true || this.showPin){
32777                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32778                     this.stickBtn.enableDisplayMode();
32779                     this.stickBtn.on("click", this.expand, this);
32780                     this.stickBtn.hide();
32781                 }
32782                 
32783             }
32784             */
32785             /** This region's collapsed element
32786             * @type Roo.Element */
32787             /*
32788              *
32789             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32790                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32791             ]}, true);
32792             
32793             if(c.floatable !== false){
32794                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32795                this.collapsedEl.on("click", this.collapseClick, this);
32796             }
32797
32798             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32799                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32800                    id: "message", unselectable: "on", style:{"float":"left"}});
32801                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32802              }
32803             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32804             this.expandBtn.on("click", this.expand, this);
32805             
32806         }
32807         
32808         if(this.collapseBtn){
32809             this.collapseBtn.setVisible(c.collapsible == true);
32810         }
32811         
32812         this.cmargins = c.cmargins || this.cmargins ||
32813                          (this.position == "west" || this.position == "east" ?
32814                              {top: 0, left: 2, right:2, bottom: 0} :
32815                              {top: 2, left: 0, right:0, bottom: 2});
32816         */
32817         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32818         
32819         
32820         this.bottomTabs = c.tabPosition != "top";
32821         
32822         this.autoScroll = c.autoScroll || false;
32823         
32824         
32825        
32826         
32827         this.duration = c.duration || .30;
32828         this.slideDuration = c.slideDuration || .45;
32829         this.config = c;
32830        
32831     },
32832     /**
32833      * Returns true if this region is currently visible.
32834      * @return {Boolean}
32835      */
32836     isVisible : function(){
32837         return this.visible;
32838     },
32839
32840     /**
32841      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32842      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32843      */
32844     //setCollapsedTitle : function(title){
32845     //    title = title || "&#160;";
32846      //   if(this.collapsedTitleTextEl){
32847       //      this.collapsedTitleTextEl.innerHTML = title;
32848        // }
32849     //},
32850
32851     getBox : function(){
32852         var b;
32853       //  if(!this.collapsed){
32854             b = this.el.getBox(false, true);
32855        // }else{
32856           //  b = this.collapsedEl.getBox(false, true);
32857         //}
32858         return b;
32859     },
32860
32861     getMargins : function(){
32862         return this.margins;
32863         //return this.collapsed ? this.cmargins : this.margins;
32864     },
32865 /*
32866     highlight : function(){
32867         this.el.addClass("x-layout-panel-dragover");
32868     },
32869
32870     unhighlight : function(){
32871         this.el.removeClass("x-layout-panel-dragover");
32872     },
32873 */
32874     updateBox : function(box)
32875     {
32876         if (!this.bodyEl) {
32877             return; // not rendered yet..
32878         }
32879         
32880         this.box = box;
32881         if(!this.collapsed){
32882             this.el.dom.style.left = box.x + "px";
32883             this.el.dom.style.top = box.y + "px";
32884             this.updateBody(box.width, box.height);
32885         }else{
32886             this.collapsedEl.dom.style.left = box.x + "px";
32887             this.collapsedEl.dom.style.top = box.y + "px";
32888             this.collapsedEl.setSize(box.width, box.height);
32889         }
32890         if(this.tabs){
32891             this.tabs.autoSizeTabs();
32892         }
32893     },
32894
32895     updateBody : function(w, h)
32896     {
32897         if(w !== null){
32898             this.el.setWidth(w);
32899             w -= this.el.getBorderWidth("rl");
32900             if(this.config.adjustments){
32901                 w += this.config.adjustments[0];
32902             }
32903         }
32904         if(h !== null && h > 0){
32905             this.el.setHeight(h);
32906             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32907             h -= this.el.getBorderWidth("tb");
32908             if(this.config.adjustments){
32909                 h += this.config.adjustments[1];
32910             }
32911             this.bodyEl.setHeight(h);
32912             if(this.tabs){
32913                 h = this.tabs.syncHeight(h);
32914             }
32915         }
32916         if(this.panelSize){
32917             w = w !== null ? w : this.panelSize.width;
32918             h = h !== null ? h : this.panelSize.height;
32919         }
32920         if(this.activePanel){
32921             var el = this.activePanel.getEl();
32922             w = w !== null ? w : el.getWidth();
32923             h = h !== null ? h : el.getHeight();
32924             this.panelSize = {width: w, height: h};
32925             this.activePanel.setSize(w, h);
32926         }
32927         if(Roo.isIE && this.tabs){
32928             this.tabs.el.repaint();
32929         }
32930     },
32931
32932     /**
32933      * Returns the container element for this region.
32934      * @return {Roo.Element}
32935      */
32936     getEl : function(){
32937         return this.el;
32938     },
32939
32940     /**
32941      * Hides this region.
32942      */
32943     hide : function(){
32944         //if(!this.collapsed){
32945             this.el.dom.style.left = "-2000px";
32946             this.el.hide();
32947         //}else{
32948          //   this.collapsedEl.dom.style.left = "-2000px";
32949          //   this.collapsedEl.hide();
32950        // }
32951         this.visible = false;
32952         this.fireEvent("visibilitychange", this, false);
32953     },
32954
32955     /**
32956      * Shows this region if it was previously hidden.
32957      */
32958     show : function(){
32959         //if(!this.collapsed){
32960             this.el.show();
32961         //}else{
32962         //    this.collapsedEl.show();
32963        // }
32964         this.visible = true;
32965         this.fireEvent("visibilitychange", this, true);
32966     },
32967 /*
32968     closeClicked : function(){
32969         if(this.activePanel){
32970             this.remove(this.activePanel);
32971         }
32972     },
32973
32974     collapseClick : function(e){
32975         if(this.isSlid){
32976            e.stopPropagation();
32977            this.slideIn();
32978         }else{
32979            e.stopPropagation();
32980            this.slideOut();
32981         }
32982     },
32983 */
32984     /**
32985      * Collapses this region.
32986      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32987      */
32988     /*
32989     collapse : function(skipAnim, skipCheck = false){
32990         if(this.collapsed) {
32991             return;
32992         }
32993         
32994         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32995             
32996             this.collapsed = true;
32997             if(this.split){
32998                 this.split.el.hide();
32999             }
33000             if(this.config.animate && skipAnim !== true){
33001                 this.fireEvent("invalidated", this);
33002                 this.animateCollapse();
33003             }else{
33004                 this.el.setLocation(-20000,-20000);
33005                 this.el.hide();
33006                 this.collapsedEl.show();
33007                 this.fireEvent("collapsed", this);
33008                 this.fireEvent("invalidated", this);
33009             }
33010         }
33011         
33012     },
33013 */
33014     animateCollapse : function(){
33015         // overridden
33016     },
33017
33018     /**
33019      * Expands this region if it was previously collapsed.
33020      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33021      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33022      */
33023     /*
33024     expand : function(e, skipAnim){
33025         if(e) {
33026             e.stopPropagation();
33027         }
33028         if(!this.collapsed || this.el.hasActiveFx()) {
33029             return;
33030         }
33031         if(this.isSlid){
33032             this.afterSlideIn();
33033             skipAnim = true;
33034         }
33035         this.collapsed = false;
33036         if(this.config.animate && skipAnim !== true){
33037             this.animateExpand();
33038         }else{
33039             this.el.show();
33040             if(this.split){
33041                 this.split.el.show();
33042             }
33043             this.collapsedEl.setLocation(-2000,-2000);
33044             this.collapsedEl.hide();
33045             this.fireEvent("invalidated", this);
33046             this.fireEvent("expanded", this);
33047         }
33048     },
33049 */
33050     animateExpand : function(){
33051         // overridden
33052     },
33053
33054     initTabs : function()
33055     {
33056         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33057         
33058         var ts = new Roo.bootstrap.panel.Tabs({
33059                 el: this.bodyEl.dom,
33060                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33061                 disableTooltips: this.config.disableTabTips,
33062                 toolbar : this.config.toolbar
33063             });
33064         
33065         if(this.config.hideTabs){
33066             ts.stripWrap.setDisplayed(false);
33067         }
33068         this.tabs = ts;
33069         ts.resizeTabs = this.config.resizeTabs === true;
33070         ts.minTabWidth = this.config.minTabWidth || 40;
33071         ts.maxTabWidth = this.config.maxTabWidth || 250;
33072         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33073         ts.monitorResize = false;
33074         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33075         ts.bodyEl.addClass('roo-layout-tabs-body');
33076         this.panels.each(this.initPanelAsTab, this);
33077     },
33078
33079     initPanelAsTab : function(panel){
33080         var ti = this.tabs.addTab(
33081                     panel.getEl().id,
33082                     panel.getTitle(),
33083                     null,
33084                     this.config.closeOnTab && panel.isClosable()
33085             );
33086         if(panel.tabTip !== undefined){
33087             ti.setTooltip(panel.tabTip);
33088         }
33089         ti.on("activate", function(){
33090               this.setActivePanel(panel);
33091         }, this);
33092         
33093         if(this.config.closeOnTab){
33094             ti.on("beforeclose", function(t, e){
33095                 e.cancel = true;
33096                 this.remove(panel);
33097             }, this);
33098         }
33099         return ti;
33100     },
33101
33102     updatePanelTitle : function(panel, title)
33103     {
33104         if(this.activePanel == panel){
33105             this.updateTitle(title);
33106         }
33107         if(this.tabs){
33108             var ti = this.tabs.getTab(panel.getEl().id);
33109             ti.setText(title);
33110             if(panel.tabTip !== undefined){
33111                 ti.setTooltip(panel.tabTip);
33112             }
33113         }
33114     },
33115
33116     updateTitle : function(title){
33117         if(this.titleTextEl && !this.config.title){
33118             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33119         }
33120     },
33121
33122     setActivePanel : function(panel)
33123     {
33124         panel = this.getPanel(panel);
33125         if(this.activePanel && this.activePanel != panel){
33126             this.activePanel.setActiveState(false);
33127         }
33128         this.activePanel = panel;
33129         panel.setActiveState(true);
33130         if(this.panelSize){
33131             panel.setSize(this.panelSize.width, this.panelSize.height);
33132         }
33133         if(this.closeBtn){
33134             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33135         }
33136         this.updateTitle(panel.getTitle());
33137         if(this.tabs){
33138             this.fireEvent("invalidated", this);
33139         }
33140         this.fireEvent("panelactivated", this, panel);
33141     },
33142
33143     /**
33144      * Shows the specified panel.
33145      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33146      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33147      */
33148     showPanel : function(panel)
33149     {
33150         panel = this.getPanel(panel);
33151         if(panel){
33152             if(this.tabs){
33153                 var tab = this.tabs.getTab(panel.getEl().id);
33154                 if(tab.isHidden()){
33155                     this.tabs.unhideTab(tab.id);
33156                 }
33157                 tab.activate();
33158             }else{
33159                 this.setActivePanel(panel);
33160             }
33161         }
33162         return panel;
33163     },
33164
33165     /**
33166      * Get the active panel for this region.
33167      * @return {Roo.ContentPanel} The active panel or null
33168      */
33169     getActivePanel : function(){
33170         return this.activePanel;
33171     },
33172
33173     validateVisibility : function(){
33174         if(this.panels.getCount() < 1){
33175             this.updateTitle("&#160;");
33176             this.closeBtn.hide();
33177             this.hide();
33178         }else{
33179             if(!this.isVisible()){
33180                 this.show();
33181             }
33182         }
33183     },
33184
33185     /**
33186      * Adds the passed ContentPanel(s) to this region.
33187      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33188      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33189      */
33190     add : function(panel)
33191     {
33192         if(arguments.length > 1){
33193             for(var i = 0, len = arguments.length; i < len; i++) {
33194                 this.add(arguments[i]);
33195             }
33196             return null;
33197         }
33198         
33199         // if we have not been rendered yet, then we can not really do much of this..
33200         if (!this.bodyEl) {
33201             this.unrendered_panels.push(panel);
33202             return panel;
33203         }
33204         
33205         
33206         
33207         
33208         if(this.hasPanel(panel)){
33209             this.showPanel(panel);
33210             return panel;
33211         }
33212         panel.setRegion(this);
33213         this.panels.add(panel);
33214        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33215             // sinle panel - no tab...?? would it not be better to render it with the tabs,
33216             // and hide them... ???
33217             this.bodyEl.dom.appendChild(panel.getEl().dom);
33218             if(panel.background !== true){
33219                 this.setActivePanel(panel);
33220             }
33221             this.fireEvent("paneladded", this, panel);
33222             return panel;
33223         }
33224         */
33225         if(!this.tabs){
33226             this.initTabs();
33227         }else{
33228             this.initPanelAsTab(panel);
33229         }
33230         
33231         
33232         if(panel.background !== true){
33233             this.tabs.activate(panel.getEl().id);
33234         }
33235         this.fireEvent("paneladded", this, panel);
33236         return panel;
33237     },
33238
33239     /**
33240      * Hides the tab for the specified panel.
33241      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33242      */
33243     hidePanel : function(panel){
33244         if(this.tabs && (panel = this.getPanel(panel))){
33245             this.tabs.hideTab(panel.getEl().id);
33246         }
33247     },
33248
33249     /**
33250      * Unhides the tab for a previously hidden panel.
33251      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33252      */
33253     unhidePanel : function(panel){
33254         if(this.tabs && (panel = this.getPanel(panel))){
33255             this.tabs.unhideTab(panel.getEl().id);
33256         }
33257     },
33258
33259     clearPanels : function(){
33260         while(this.panels.getCount() > 0){
33261              this.remove(this.panels.first());
33262         }
33263     },
33264
33265     /**
33266      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33267      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33268      * @param {Boolean} preservePanel Overrides the config preservePanel option
33269      * @return {Roo.ContentPanel} The panel that was removed
33270      */
33271     remove : function(panel, preservePanel)
33272     {
33273         panel = this.getPanel(panel);
33274         if(!panel){
33275             return null;
33276         }
33277         var e = {};
33278         this.fireEvent("beforeremove", this, panel, e);
33279         if(e.cancel === true){
33280             return null;
33281         }
33282         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33283         var panelId = panel.getId();
33284         this.panels.removeKey(panelId);
33285         if(preservePanel){
33286             document.body.appendChild(panel.getEl().dom);
33287         }
33288         if(this.tabs){
33289             this.tabs.removeTab(panel.getEl().id);
33290         }else if (!preservePanel){
33291             this.bodyEl.dom.removeChild(panel.getEl().dom);
33292         }
33293         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33294             var p = this.panels.first();
33295             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33296             tempEl.appendChild(p.getEl().dom);
33297             this.bodyEl.update("");
33298             this.bodyEl.dom.appendChild(p.getEl().dom);
33299             tempEl = null;
33300             this.updateTitle(p.getTitle());
33301             this.tabs = null;
33302             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33303             this.setActivePanel(p);
33304         }
33305         panel.setRegion(null);
33306         if(this.activePanel == panel){
33307             this.activePanel = null;
33308         }
33309         if(this.config.autoDestroy !== false && preservePanel !== true){
33310             try{panel.destroy();}catch(e){}
33311         }
33312         this.fireEvent("panelremoved", this, panel);
33313         return panel;
33314     },
33315
33316     /**
33317      * Returns the TabPanel component used by this region
33318      * @return {Roo.TabPanel}
33319      */
33320     getTabs : function(){
33321         return this.tabs;
33322     },
33323
33324     createTool : function(parentEl, className){
33325         var btn = Roo.DomHelper.append(parentEl, {
33326             tag: "div",
33327             cls: "x-layout-tools-button",
33328             children: [ {
33329                 tag: "div",
33330                 cls: "roo-layout-tools-button-inner " + className,
33331                 html: "&#160;"
33332             }]
33333         }, true);
33334         btn.addClassOnOver("roo-layout-tools-button-over");
33335         return btn;
33336     }
33337 });/*
33338  * Based on:
33339  * Ext JS Library 1.1.1
33340  * Copyright(c) 2006-2007, Ext JS, LLC.
33341  *
33342  * Originally Released Under LGPL - original licence link has changed is not relivant.
33343  *
33344  * Fork - LGPL
33345  * <script type="text/javascript">
33346  */
33347  
33348
33349
33350 /**
33351  * @class Roo.SplitLayoutRegion
33352  * @extends Roo.LayoutRegion
33353  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33354  */
33355 Roo.bootstrap.layout.Split = function(config){
33356     this.cursor = config.cursor;
33357     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33358 };
33359
33360 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33361 {
33362     splitTip : "Drag to resize.",
33363     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33364     useSplitTips : false,
33365
33366     applyConfig : function(config){
33367         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33368     },
33369     
33370     onRender : function(ctr,pos) {
33371         
33372         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33373         if(!this.config.split){
33374             return;
33375         }
33376         if(!this.split){
33377             
33378             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33379                             tag: "div",
33380                             id: this.el.id + "-split",
33381                             cls: "roo-layout-split roo-layout-split-"+this.position,
33382                             html: "&#160;"
33383             });
33384             /** The SplitBar for this region 
33385             * @type Roo.SplitBar */
33386             // does not exist yet...
33387             Roo.log([this.position, this.orientation]);
33388             
33389             this.split = new Roo.bootstrap.SplitBar({
33390                 dragElement : splitEl,
33391                 resizingElement: this.el,
33392                 orientation : this.orientation
33393             });
33394             
33395             this.split.on("moved", this.onSplitMove, this);
33396             this.split.useShim = this.config.useShim === true;
33397             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33398             if(this.useSplitTips){
33399                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33400             }
33401             //if(config.collapsible){
33402             //    this.split.el.on("dblclick", this.collapse,  this);
33403             //}
33404         }
33405         if(typeof this.config.minSize != "undefined"){
33406             this.split.minSize = this.config.minSize;
33407         }
33408         if(typeof this.config.maxSize != "undefined"){
33409             this.split.maxSize = this.config.maxSize;
33410         }
33411         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33412             this.hideSplitter();
33413         }
33414         
33415     },
33416
33417     getHMaxSize : function(){
33418          var cmax = this.config.maxSize || 10000;
33419          var center = this.mgr.getRegion("center");
33420          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33421     },
33422
33423     getVMaxSize : function(){
33424          var cmax = this.config.maxSize || 10000;
33425          var center = this.mgr.getRegion("center");
33426          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33427     },
33428
33429     onSplitMove : function(split, newSize){
33430         this.fireEvent("resized", this, newSize);
33431     },
33432     
33433     /** 
33434      * Returns the {@link Roo.SplitBar} for this region.
33435      * @return {Roo.SplitBar}
33436      */
33437     getSplitBar : function(){
33438         return this.split;
33439     },
33440     
33441     hide : function(){
33442         this.hideSplitter();
33443         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33444     },
33445
33446     hideSplitter : function(){
33447         if(this.split){
33448             this.split.el.setLocation(-2000,-2000);
33449             this.split.el.hide();
33450         }
33451     },
33452
33453     show : function(){
33454         if(this.split){
33455             this.split.el.show();
33456         }
33457         Roo.bootstrap.layout.Split.superclass.show.call(this);
33458     },
33459     
33460     beforeSlide: function(){
33461         if(Roo.isGecko){// firefox overflow auto bug workaround
33462             this.bodyEl.clip();
33463             if(this.tabs) {
33464                 this.tabs.bodyEl.clip();
33465             }
33466             if(this.activePanel){
33467                 this.activePanel.getEl().clip();
33468                 
33469                 if(this.activePanel.beforeSlide){
33470                     this.activePanel.beforeSlide();
33471                 }
33472             }
33473         }
33474     },
33475     
33476     afterSlide : function(){
33477         if(Roo.isGecko){// firefox overflow auto bug workaround
33478             this.bodyEl.unclip();
33479             if(this.tabs) {
33480                 this.tabs.bodyEl.unclip();
33481             }
33482             if(this.activePanel){
33483                 this.activePanel.getEl().unclip();
33484                 if(this.activePanel.afterSlide){
33485                     this.activePanel.afterSlide();
33486                 }
33487             }
33488         }
33489     },
33490
33491     initAutoHide : function(){
33492         if(this.autoHide !== false){
33493             if(!this.autoHideHd){
33494                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33495                 this.autoHideHd = {
33496                     "mouseout": function(e){
33497                         if(!e.within(this.el, true)){
33498                             st.delay(500);
33499                         }
33500                     },
33501                     "mouseover" : function(e){
33502                         st.cancel();
33503                     },
33504                     scope : this
33505                 };
33506             }
33507             this.el.on(this.autoHideHd);
33508         }
33509     },
33510
33511     clearAutoHide : function(){
33512         if(this.autoHide !== false){
33513             this.el.un("mouseout", this.autoHideHd.mouseout);
33514             this.el.un("mouseover", this.autoHideHd.mouseover);
33515         }
33516     },
33517
33518     clearMonitor : function(){
33519         Roo.get(document).un("click", this.slideInIf, this);
33520     },
33521
33522     // these names are backwards but not changed for compat
33523     slideOut : function(){
33524         if(this.isSlid || this.el.hasActiveFx()){
33525             return;
33526         }
33527         this.isSlid = true;
33528         if(this.collapseBtn){
33529             this.collapseBtn.hide();
33530         }
33531         this.closeBtnState = this.closeBtn.getStyle('display');
33532         this.closeBtn.hide();
33533         if(this.stickBtn){
33534             this.stickBtn.show();
33535         }
33536         this.el.show();
33537         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33538         this.beforeSlide();
33539         this.el.setStyle("z-index", 10001);
33540         this.el.slideIn(this.getSlideAnchor(), {
33541             callback: function(){
33542                 this.afterSlide();
33543                 this.initAutoHide();
33544                 Roo.get(document).on("click", this.slideInIf, this);
33545                 this.fireEvent("slideshow", this);
33546             },
33547             scope: this,
33548             block: true
33549         });
33550     },
33551
33552     afterSlideIn : function(){
33553         this.clearAutoHide();
33554         this.isSlid = false;
33555         this.clearMonitor();
33556         this.el.setStyle("z-index", "");
33557         if(this.collapseBtn){
33558             this.collapseBtn.show();
33559         }
33560         this.closeBtn.setStyle('display', this.closeBtnState);
33561         if(this.stickBtn){
33562             this.stickBtn.hide();
33563         }
33564         this.fireEvent("slidehide", this);
33565     },
33566
33567     slideIn : function(cb){
33568         if(!this.isSlid || this.el.hasActiveFx()){
33569             Roo.callback(cb);
33570             return;
33571         }
33572         this.isSlid = false;
33573         this.beforeSlide();
33574         this.el.slideOut(this.getSlideAnchor(), {
33575             callback: function(){
33576                 this.el.setLeftTop(-10000, -10000);
33577                 this.afterSlide();
33578                 this.afterSlideIn();
33579                 Roo.callback(cb);
33580             },
33581             scope: this,
33582             block: true
33583         });
33584     },
33585     
33586     slideInIf : function(e){
33587         if(!e.within(this.el)){
33588             this.slideIn();
33589         }
33590     },
33591
33592     animateCollapse : function(){
33593         this.beforeSlide();
33594         this.el.setStyle("z-index", 20000);
33595         var anchor = this.getSlideAnchor();
33596         this.el.slideOut(anchor, {
33597             callback : function(){
33598                 this.el.setStyle("z-index", "");
33599                 this.collapsedEl.slideIn(anchor, {duration:.3});
33600                 this.afterSlide();
33601                 this.el.setLocation(-10000,-10000);
33602                 this.el.hide();
33603                 this.fireEvent("collapsed", this);
33604             },
33605             scope: this,
33606             block: true
33607         });
33608     },
33609
33610     animateExpand : function(){
33611         this.beforeSlide();
33612         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33613         this.el.setStyle("z-index", 20000);
33614         this.collapsedEl.hide({
33615             duration:.1
33616         });
33617         this.el.slideIn(this.getSlideAnchor(), {
33618             callback : function(){
33619                 this.el.setStyle("z-index", "");
33620                 this.afterSlide();
33621                 if(this.split){
33622                     this.split.el.show();
33623                 }
33624                 this.fireEvent("invalidated", this);
33625                 this.fireEvent("expanded", this);
33626             },
33627             scope: this,
33628             block: true
33629         });
33630     },
33631
33632     anchors : {
33633         "west" : "left",
33634         "east" : "right",
33635         "north" : "top",
33636         "south" : "bottom"
33637     },
33638
33639     sanchors : {
33640         "west" : "l",
33641         "east" : "r",
33642         "north" : "t",
33643         "south" : "b"
33644     },
33645
33646     canchors : {
33647         "west" : "tl-tr",
33648         "east" : "tr-tl",
33649         "north" : "tl-bl",
33650         "south" : "bl-tl"
33651     },
33652
33653     getAnchor : function(){
33654         return this.anchors[this.position];
33655     },
33656
33657     getCollapseAnchor : function(){
33658         return this.canchors[this.position];
33659     },
33660
33661     getSlideAnchor : function(){
33662         return this.sanchors[this.position];
33663     },
33664
33665     getAlignAdj : function(){
33666         var cm = this.cmargins;
33667         switch(this.position){
33668             case "west":
33669                 return [0, 0];
33670             break;
33671             case "east":
33672                 return [0, 0];
33673             break;
33674             case "north":
33675                 return [0, 0];
33676             break;
33677             case "south":
33678                 return [0, 0];
33679             break;
33680         }
33681     },
33682
33683     getExpandAdj : function(){
33684         var c = this.collapsedEl, cm = this.cmargins;
33685         switch(this.position){
33686             case "west":
33687                 return [-(cm.right+c.getWidth()+cm.left), 0];
33688             break;
33689             case "east":
33690                 return [cm.right+c.getWidth()+cm.left, 0];
33691             break;
33692             case "north":
33693                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33694             break;
33695             case "south":
33696                 return [0, cm.top+cm.bottom+c.getHeight()];
33697             break;
33698         }
33699     }
33700 });/*
33701  * Based on:
33702  * Ext JS Library 1.1.1
33703  * Copyright(c) 2006-2007, Ext JS, LLC.
33704  *
33705  * Originally Released Under LGPL - original licence link has changed is not relivant.
33706  *
33707  * Fork - LGPL
33708  * <script type="text/javascript">
33709  */
33710 /*
33711  * These classes are private internal classes
33712  */
33713 Roo.bootstrap.layout.Center = function(config){
33714     config.region = "center";
33715     Roo.bootstrap.layout.Region.call(this, config);
33716     this.visible = true;
33717     this.minWidth = config.minWidth || 20;
33718     this.minHeight = config.minHeight || 20;
33719 };
33720
33721 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33722     hide : function(){
33723         // center panel can't be hidden
33724     },
33725     
33726     show : function(){
33727         // center panel can't be hidden
33728     },
33729     
33730     getMinWidth: function(){
33731         return this.minWidth;
33732     },
33733     
33734     getMinHeight: function(){
33735         return this.minHeight;
33736     }
33737 });
33738
33739
33740
33741
33742  
33743
33744
33745
33746
33747
33748 Roo.bootstrap.layout.North = function(config)
33749 {
33750     config.region = 'north';
33751     config.cursor = 'n-resize';
33752     
33753     Roo.bootstrap.layout.Split.call(this, config);
33754     
33755     
33756     if(this.split){
33757         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33758         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33759         this.split.el.addClass("roo-layout-split-v");
33760     }
33761     var size = config.initialSize || config.height;
33762     if(typeof size != "undefined"){
33763         this.el.setHeight(size);
33764     }
33765 };
33766 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33767 {
33768     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33769     
33770     
33771     
33772     getBox : function(){
33773         if(this.collapsed){
33774             return this.collapsedEl.getBox();
33775         }
33776         var box = this.el.getBox();
33777         if(this.split){
33778             box.height += this.split.el.getHeight();
33779         }
33780         return box;
33781     },
33782     
33783     updateBox : function(box){
33784         if(this.split && !this.collapsed){
33785             box.height -= this.split.el.getHeight();
33786             this.split.el.setLeft(box.x);
33787             this.split.el.setTop(box.y+box.height);
33788             this.split.el.setWidth(box.width);
33789         }
33790         if(this.collapsed){
33791             this.updateBody(box.width, null);
33792         }
33793         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33794     }
33795 });
33796
33797
33798
33799
33800
33801 Roo.bootstrap.layout.South = function(config){
33802     config.region = 'south';
33803     config.cursor = 's-resize';
33804     Roo.bootstrap.layout.Split.call(this, config);
33805     if(this.split){
33806         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33807         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33808         this.split.el.addClass("roo-layout-split-v");
33809     }
33810     var size = config.initialSize || config.height;
33811     if(typeof size != "undefined"){
33812         this.el.setHeight(size);
33813     }
33814 };
33815
33816 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33817     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33818     getBox : function(){
33819         if(this.collapsed){
33820             return this.collapsedEl.getBox();
33821         }
33822         var box = this.el.getBox();
33823         if(this.split){
33824             var sh = this.split.el.getHeight();
33825             box.height += sh;
33826             box.y -= sh;
33827         }
33828         return box;
33829     },
33830     
33831     updateBox : function(box){
33832         if(this.split && !this.collapsed){
33833             var sh = this.split.el.getHeight();
33834             box.height -= sh;
33835             box.y += sh;
33836             this.split.el.setLeft(box.x);
33837             this.split.el.setTop(box.y-sh);
33838             this.split.el.setWidth(box.width);
33839         }
33840         if(this.collapsed){
33841             this.updateBody(box.width, null);
33842         }
33843         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33844     }
33845 });
33846
33847 Roo.bootstrap.layout.East = function(config){
33848     config.region = "east";
33849     config.cursor = "e-resize";
33850     Roo.bootstrap.layout.Split.call(this, config);
33851     if(this.split){
33852         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33853         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33854         this.split.el.addClass("roo-layout-split-h");
33855     }
33856     var size = config.initialSize || config.width;
33857     if(typeof size != "undefined"){
33858         this.el.setWidth(size);
33859     }
33860 };
33861 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33862     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33863     getBox : function(){
33864         if(this.collapsed){
33865             return this.collapsedEl.getBox();
33866         }
33867         var box = this.el.getBox();
33868         if(this.split){
33869             var sw = this.split.el.getWidth();
33870             box.width += sw;
33871             box.x -= sw;
33872         }
33873         return box;
33874     },
33875
33876     updateBox : function(box){
33877         if(this.split && !this.collapsed){
33878             var sw = this.split.el.getWidth();
33879             box.width -= sw;
33880             this.split.el.setLeft(box.x);
33881             this.split.el.setTop(box.y);
33882             this.split.el.setHeight(box.height);
33883             box.x += sw;
33884         }
33885         if(this.collapsed){
33886             this.updateBody(null, box.height);
33887         }
33888         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33889     }
33890 });
33891
33892 Roo.bootstrap.layout.West = function(config){
33893     config.region = "west";
33894     config.cursor = "w-resize";
33895     
33896     Roo.bootstrap.layout.Split.call(this, config);
33897     if(this.split){
33898         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33899         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33900         this.split.el.addClass("roo-layout-split-h");
33901     }
33902     
33903 };
33904 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33905     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33906     
33907     onRender: function(ctr, pos)
33908     {
33909         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33910         var size = this.config.initialSize || this.config.width;
33911         if(typeof size != "undefined"){
33912             this.el.setWidth(size);
33913         }
33914     },
33915     
33916     getBox : function(){
33917         if(this.collapsed){
33918             return this.collapsedEl.getBox();
33919         }
33920         var box = this.el.getBox();
33921         if(this.split){
33922             box.width += this.split.el.getWidth();
33923         }
33924         return box;
33925     },
33926     
33927     updateBox : function(box){
33928         if(this.split && !this.collapsed){
33929             var sw = this.split.el.getWidth();
33930             box.width -= sw;
33931             this.split.el.setLeft(box.x+box.width);
33932             this.split.el.setTop(box.y);
33933             this.split.el.setHeight(box.height);
33934         }
33935         if(this.collapsed){
33936             this.updateBody(null, box.height);
33937         }
33938         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33939     }
33940 });
33941 Roo.namespace("Roo.bootstrap.panel");/*
33942  * Based on:
33943  * Ext JS Library 1.1.1
33944  * Copyright(c) 2006-2007, Ext JS, LLC.
33945  *
33946  * Originally Released Under LGPL - original licence link has changed is not relivant.
33947  *
33948  * Fork - LGPL
33949  * <script type="text/javascript">
33950  */
33951 /**
33952  * @class Roo.ContentPanel
33953  * @extends Roo.util.Observable
33954  * A basic ContentPanel element.
33955  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33956  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33957  * @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
33958  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33959  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33960  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33961  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33962  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33963  * @cfg {String} title          The title for this panel
33964  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33965  * @cfg {String} url            Calls {@link #setUrl} with this value
33966  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33967  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33968  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33969  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33970
33971  * @constructor
33972  * Create a new ContentPanel.
33973  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33974  * @param {String/Object} config A string to set only the title or a config object
33975  * @param {String} content (optional) Set the HTML content for this panel
33976  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33977  */
33978 Roo.bootstrap.panel.Content = function( config){
33979     
33980     var el = config.el;
33981     var content = config.content;
33982
33983     if(config.autoCreate){ // xtype is available if this is called from factory
33984         el = Roo.id();
33985     }
33986     this.el = Roo.get(el);
33987     if(!this.el && config && config.autoCreate){
33988         if(typeof config.autoCreate == "object"){
33989             if(!config.autoCreate.id){
33990                 config.autoCreate.id = config.id||el;
33991             }
33992             this.el = Roo.DomHelper.append(document.body,
33993                         config.autoCreate, true);
33994         }else{
33995             var elcfg =  {   tag: "div",
33996                             cls: "roo-layout-inactive-content",
33997                             id: config.id||el
33998                             };
33999             if (config.html) {
34000                 elcfg.html = config.html;
34001                 
34002             }
34003                         
34004             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34005         }
34006     } 
34007     this.closable = false;
34008     this.loaded = false;
34009     this.active = false;
34010    
34011       
34012     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34013         
34014         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34015         
34016         this.wrapEl = this.el.wrap();
34017         var ti = [];
34018         if (config.toolbar.items) {
34019             ti = config.toolbar.items ;
34020             delete config.toolbar.items ;
34021         }
34022         
34023         var nitems = [];
34024         this.toolbar.render(this.wrapEl, 'before');
34025         for(var i =0;i < ti.length;i++) {
34026           //  Roo.log(['add child', items[i]]);
34027             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34028         }
34029         this.toolbar.items = nitems;
34030         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34031         delete config.toolbar;
34032         
34033     }
34034     /*
34035     // xtype created footer. - not sure if will work as we normally have to render first..
34036     if (this.footer && !this.footer.el && this.footer.xtype) {
34037         if (!this.wrapEl) {
34038             this.wrapEl = this.el.wrap();
34039         }
34040     
34041         this.footer.container = this.wrapEl.createChild();
34042          
34043         this.footer = Roo.factory(this.footer, Roo);
34044         
34045     }
34046     */
34047     
34048      if(typeof config == "string"){
34049         this.title = config;
34050     }else{
34051         Roo.apply(this, config);
34052     }
34053     
34054     if(this.resizeEl){
34055         this.resizeEl = Roo.get(this.resizeEl, true);
34056     }else{
34057         this.resizeEl = this.el;
34058     }
34059     // handle view.xtype
34060     
34061  
34062     
34063     
34064     this.addEvents({
34065         /**
34066          * @event activate
34067          * Fires when this panel is activated. 
34068          * @param {Roo.ContentPanel} this
34069          */
34070         "activate" : true,
34071         /**
34072          * @event deactivate
34073          * Fires when this panel is activated. 
34074          * @param {Roo.ContentPanel} this
34075          */
34076         "deactivate" : true,
34077
34078         /**
34079          * @event resize
34080          * Fires when this panel is resized if fitToFrame is true.
34081          * @param {Roo.ContentPanel} this
34082          * @param {Number} width The width after any component adjustments
34083          * @param {Number} height The height after any component adjustments
34084          */
34085         "resize" : true,
34086         
34087          /**
34088          * @event render
34089          * Fires when this tab is created
34090          * @param {Roo.ContentPanel} this
34091          */
34092         "render" : true
34093         
34094         
34095         
34096     });
34097     
34098
34099     
34100     
34101     if(this.autoScroll){
34102         this.resizeEl.setStyle("overflow", "auto");
34103     } else {
34104         // fix randome scrolling
34105         //this.el.on('scroll', function() {
34106         //    Roo.log('fix random scolling');
34107         //    this.scrollTo('top',0); 
34108         //});
34109     }
34110     content = content || this.content;
34111     if(content){
34112         this.setContent(content);
34113     }
34114     if(config && config.url){
34115         this.setUrl(this.url, this.params, this.loadOnce);
34116     }
34117     
34118     
34119     
34120     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34121     
34122     if (this.view && typeof(this.view.xtype) != 'undefined') {
34123         this.view.el = this.el.appendChild(document.createElement("div"));
34124         this.view = Roo.factory(this.view); 
34125         this.view.render  &&  this.view.render(false, '');  
34126     }
34127     
34128     
34129     this.fireEvent('render', this);
34130 };
34131
34132 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34133     tabTip:'',
34134     setRegion : function(region){
34135         this.region = region;
34136         if(region){
34137            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34138         }else{
34139            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34140         } 
34141     },
34142     
34143     /**
34144      * Returns the toolbar for this Panel if one was configured. 
34145      * @return {Roo.Toolbar} 
34146      */
34147     getToolbar : function(){
34148         return this.toolbar;
34149     },
34150     
34151     setActiveState : function(active){
34152         this.active = active;
34153         if(!active){
34154             this.fireEvent("deactivate", this);
34155         }else{
34156             this.fireEvent("activate", this);
34157         }
34158     },
34159     /**
34160      * Updates this panel's element
34161      * @param {String} content The new content
34162      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34163     */
34164     setContent : function(content, loadScripts){
34165         this.el.update(content, loadScripts);
34166     },
34167
34168     ignoreResize : function(w, h){
34169         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34170             return true;
34171         }else{
34172             this.lastSize = {width: w, height: h};
34173             return false;
34174         }
34175     },
34176     /**
34177      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34178      * @return {Roo.UpdateManager} The UpdateManager
34179      */
34180     getUpdateManager : function(){
34181         return this.el.getUpdateManager();
34182     },
34183      /**
34184      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34185      * @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:
34186 <pre><code>
34187 panel.load({
34188     url: "your-url.php",
34189     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34190     callback: yourFunction,
34191     scope: yourObject, //(optional scope)
34192     discardUrl: false,
34193     nocache: false,
34194     text: "Loading...",
34195     timeout: 30,
34196     scripts: false
34197 });
34198 </code></pre>
34199      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34200      * 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.
34201      * @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}
34202      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34203      * @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.
34204      * @return {Roo.ContentPanel} this
34205      */
34206     load : function(){
34207         var um = this.el.getUpdateManager();
34208         um.update.apply(um, arguments);
34209         return this;
34210     },
34211
34212
34213     /**
34214      * 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.
34215      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34216      * @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)
34217      * @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)
34218      * @return {Roo.UpdateManager} The UpdateManager
34219      */
34220     setUrl : function(url, params, loadOnce){
34221         if(this.refreshDelegate){
34222             this.removeListener("activate", this.refreshDelegate);
34223         }
34224         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34225         this.on("activate", this.refreshDelegate);
34226         return this.el.getUpdateManager();
34227     },
34228     
34229     _handleRefresh : function(url, params, loadOnce){
34230         if(!loadOnce || !this.loaded){
34231             var updater = this.el.getUpdateManager();
34232             updater.update(url, params, this._setLoaded.createDelegate(this));
34233         }
34234     },
34235     
34236     _setLoaded : function(){
34237         this.loaded = true;
34238     }, 
34239     
34240     /**
34241      * Returns this panel's id
34242      * @return {String} 
34243      */
34244     getId : function(){
34245         return this.el.id;
34246     },
34247     
34248     /** 
34249      * Returns this panel's element - used by regiosn to add.
34250      * @return {Roo.Element} 
34251      */
34252     getEl : function(){
34253         return this.wrapEl || this.el;
34254     },
34255     
34256    
34257     
34258     adjustForComponents : function(width, height)
34259     {
34260         //Roo.log('adjustForComponents ');
34261         if(this.resizeEl != this.el){
34262             width -= this.el.getFrameWidth('lr');
34263             height -= this.el.getFrameWidth('tb');
34264         }
34265         if(this.toolbar){
34266             var te = this.toolbar.getEl();
34267             height -= te.getHeight();
34268             te.setWidth(width);
34269         }
34270         if(this.footer){
34271             var te = this.footer.getEl();
34272             Roo.log("footer:" + te.getHeight());
34273             
34274             height -= te.getHeight();
34275             te.setWidth(width);
34276         }
34277         
34278         
34279         if(this.adjustments){
34280             width += this.adjustments[0];
34281             height += this.adjustments[1];
34282         }
34283         return {"width": width, "height": height};
34284     },
34285     
34286     setSize : function(width, height){
34287         if(this.fitToFrame && !this.ignoreResize(width, height)){
34288             if(this.fitContainer && this.resizeEl != this.el){
34289                 this.el.setSize(width, height);
34290             }
34291             var size = this.adjustForComponents(width, height);
34292             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34293             this.fireEvent('resize', this, size.width, size.height);
34294         }
34295     },
34296     
34297     /**
34298      * Returns this panel's title
34299      * @return {String} 
34300      */
34301     getTitle : function(){
34302         return this.title;
34303     },
34304     
34305     /**
34306      * Set this panel's title
34307      * @param {String} title
34308      */
34309     setTitle : function(title){
34310         this.title = title;
34311         if(this.region){
34312             this.region.updatePanelTitle(this, title);
34313         }
34314     },
34315     
34316     /**
34317      * Returns true is this panel was configured to be closable
34318      * @return {Boolean} 
34319      */
34320     isClosable : function(){
34321         return this.closable;
34322     },
34323     
34324     beforeSlide : function(){
34325         this.el.clip();
34326         this.resizeEl.clip();
34327     },
34328     
34329     afterSlide : function(){
34330         this.el.unclip();
34331         this.resizeEl.unclip();
34332     },
34333     
34334     /**
34335      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34336      *   Will fail silently if the {@link #setUrl} method has not been called.
34337      *   This does not activate the panel, just updates its content.
34338      */
34339     refresh : function(){
34340         if(this.refreshDelegate){
34341            this.loaded = false;
34342            this.refreshDelegate();
34343         }
34344     },
34345     
34346     /**
34347      * Destroys this panel
34348      */
34349     destroy : function(){
34350         this.el.removeAllListeners();
34351         var tempEl = document.createElement("span");
34352         tempEl.appendChild(this.el.dom);
34353         tempEl.innerHTML = "";
34354         this.el.remove();
34355         this.el = null;
34356     },
34357     
34358     /**
34359      * form - if the content panel contains a form - this is a reference to it.
34360      * @type {Roo.form.Form}
34361      */
34362     form : false,
34363     /**
34364      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34365      *    This contains a reference to it.
34366      * @type {Roo.View}
34367      */
34368     view : false,
34369     
34370       /**
34371      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34372      * <pre><code>
34373
34374 layout.addxtype({
34375        xtype : 'Form',
34376        items: [ .... ]
34377    }
34378 );
34379
34380 </code></pre>
34381      * @param {Object} cfg Xtype definition of item to add.
34382      */
34383     
34384     
34385     getChildContainer: function () {
34386         return this.getEl();
34387     }
34388     
34389     
34390     /*
34391         var  ret = new Roo.factory(cfg);
34392         return ret;
34393         
34394         
34395         // add form..
34396         if (cfg.xtype.match(/^Form$/)) {
34397             
34398             var el;
34399             //if (this.footer) {
34400             //    el = this.footer.container.insertSibling(false, 'before');
34401             //} else {
34402                 el = this.el.createChild();
34403             //}
34404
34405             this.form = new  Roo.form.Form(cfg);
34406             
34407             
34408             if ( this.form.allItems.length) {
34409                 this.form.render(el.dom);
34410             }
34411             return this.form;
34412         }
34413         // should only have one of theses..
34414         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34415             // views.. should not be just added - used named prop 'view''
34416             
34417             cfg.el = this.el.appendChild(document.createElement("div"));
34418             // factory?
34419             
34420             var ret = new Roo.factory(cfg);
34421              
34422              ret.render && ret.render(false, ''); // render blank..
34423             this.view = ret;
34424             return ret;
34425         }
34426         return false;
34427     }
34428     \*/
34429 });
34430  
34431 /**
34432  * @class Roo.bootstrap.panel.Grid
34433  * @extends Roo.bootstrap.panel.Content
34434  * @constructor
34435  * Create a new GridPanel.
34436  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34437  * @param {Object} config A the config object
34438   
34439  */
34440
34441
34442
34443 Roo.bootstrap.panel.Grid = function(config)
34444 {
34445     
34446       
34447     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34448         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34449
34450     config.el = this.wrapper;
34451     //this.el = this.wrapper;
34452     
34453       if (config.container) {
34454         // ctor'ed from a Border/panel.grid
34455         
34456         
34457         this.wrapper.setStyle("overflow", "hidden");
34458         this.wrapper.addClass('roo-grid-container');
34459
34460     }
34461     
34462     
34463     if(config.toolbar){
34464         var tool_el = this.wrapper.createChild();    
34465         this.toolbar = Roo.factory(config.toolbar);
34466         var ti = [];
34467         if (config.toolbar.items) {
34468             ti = config.toolbar.items ;
34469             delete config.toolbar.items ;
34470         }
34471         
34472         var nitems = [];
34473         this.toolbar.render(tool_el);
34474         for(var i =0;i < ti.length;i++) {
34475           //  Roo.log(['add child', items[i]]);
34476             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34477         }
34478         this.toolbar.items = nitems;
34479         
34480         delete config.toolbar;
34481     }
34482     
34483     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34484     config.grid.scrollBody = true;;
34485     config.grid.monitorWindowResize = false; // turn off autosizing
34486     config.grid.autoHeight = false;
34487     config.grid.autoWidth = false;
34488     
34489     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34490     
34491     if (config.background) {
34492         // render grid on panel activation (if panel background)
34493         this.on('activate', function(gp) {
34494             if (!gp.grid.rendered) {
34495                 gp.grid.render(el);
34496                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34497
34498             }
34499         });
34500             
34501     } else {
34502         this.grid.render(this.wrapper);
34503         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34504
34505     }
34506     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34507     // ??? needed ??? config.el = this.wrapper;
34508     
34509     
34510     
34511   
34512     // xtype created footer. - not sure if will work as we normally have to render first..
34513     if (this.footer && !this.footer.el && this.footer.xtype) {
34514         
34515         var ctr = this.grid.getView().getFooterPanel(true);
34516         this.footer.dataSource = this.grid.dataSource;
34517         this.footer = Roo.factory(this.footer, Roo);
34518         this.footer.render(ctr);
34519         
34520     }
34521     
34522     
34523     
34524     
34525      
34526 };
34527
34528 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34529     getId : function(){
34530         return this.grid.id;
34531     },
34532     
34533     /**
34534      * Returns the grid for this panel
34535      * @return {Roo.bootstrap.Table} 
34536      */
34537     getGrid : function(){
34538         return this.grid;    
34539     },
34540     
34541     setSize : function(width, height){
34542         if(!this.ignoreResize(width, height)){
34543             var grid = this.grid;
34544             var size = this.adjustForComponents(width, height);
34545             var gridel = grid.getGridEl();
34546             gridel.setSize(size.width, size.height);
34547             /*
34548             var thd = grid.getGridEl().select('thead',true).first();
34549             var tbd = grid.getGridEl().select('tbody', true).first();
34550             if (tbd) {
34551                 tbd.setSize(width, height - thd.getHeight());
34552             }
34553             */
34554             grid.autoSize();
34555         }
34556     },
34557      
34558     
34559     
34560     beforeSlide : function(){
34561         this.grid.getView().scroller.clip();
34562     },
34563     
34564     afterSlide : function(){
34565         this.grid.getView().scroller.unclip();
34566     },
34567     
34568     destroy : function(){
34569         this.grid.destroy();
34570         delete this.grid;
34571         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34572     }
34573 });
34574
34575 /**
34576  * @class Roo.bootstrap.panel.Nest
34577  * @extends Roo.bootstrap.panel.Content
34578  * @constructor
34579  * Create a new Panel, that can contain a layout.Border.
34580  * 
34581  * 
34582  * @param {Roo.BorderLayout} layout The layout for this panel
34583  * @param {String/Object} config A string to set only the title or a config object
34584  */
34585 Roo.bootstrap.panel.Nest = function(config)
34586 {
34587     // construct with only one argument..
34588     /* FIXME - implement nicer consturctors
34589     if (layout.layout) {
34590         config = layout;
34591         layout = config.layout;
34592         delete config.layout;
34593     }
34594     if (layout.xtype && !layout.getEl) {
34595         // then layout needs constructing..
34596         layout = Roo.factory(layout, Roo);
34597     }
34598     */
34599     
34600     config.el =  config.layout.getEl();
34601     
34602     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34603     
34604     config.layout.monitorWindowResize = false; // turn off autosizing
34605     this.layout = config.layout;
34606     this.layout.getEl().addClass("roo-layout-nested-layout");
34607     
34608     
34609     
34610     
34611 };
34612
34613 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34614
34615     setSize : function(width, height){
34616         if(!this.ignoreResize(width, height)){
34617             var size = this.adjustForComponents(width, height);
34618             var el = this.layout.getEl();
34619             if (size.height < 1) {
34620                 el.setWidth(size.width);   
34621             } else {
34622                 el.setSize(size.width, size.height);
34623             }
34624             var touch = el.dom.offsetWidth;
34625             this.layout.layout();
34626             // ie requires a double layout on the first pass
34627             if(Roo.isIE && !this.initialized){
34628                 this.initialized = true;
34629                 this.layout.layout();
34630             }
34631         }
34632     },
34633     
34634     // activate all subpanels if not currently active..
34635     
34636     setActiveState : function(active){
34637         this.active = active;
34638         if(!active){
34639             this.fireEvent("deactivate", this);
34640             return;
34641         }
34642         
34643         this.fireEvent("activate", this);
34644         // not sure if this should happen before or after..
34645         if (!this.layout) {
34646             return; // should not happen..
34647         }
34648         var reg = false;
34649         for (var r in this.layout.regions) {
34650             reg = this.layout.getRegion(r);
34651             if (reg.getActivePanel()) {
34652                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34653                 reg.setActivePanel(reg.getActivePanel());
34654                 continue;
34655             }
34656             if (!reg.panels.length) {
34657                 continue;
34658             }
34659             reg.showPanel(reg.getPanel(0));
34660         }
34661         
34662         
34663         
34664         
34665     },
34666     
34667     /**
34668      * Returns the nested BorderLayout for this panel
34669      * @return {Roo.BorderLayout} 
34670      */
34671     getLayout : function(){
34672         return this.layout;
34673     },
34674     
34675      /**
34676      * Adds a xtype elements to the layout of the nested panel
34677      * <pre><code>
34678
34679 panel.addxtype({
34680        xtype : 'ContentPanel',
34681        region: 'west',
34682        items: [ .... ]
34683    }
34684 );
34685
34686 panel.addxtype({
34687         xtype : 'NestedLayoutPanel',
34688         region: 'west',
34689         layout: {
34690            center: { },
34691            west: { }   
34692         },
34693         items : [ ... list of content panels or nested layout panels.. ]
34694    }
34695 );
34696 </code></pre>
34697      * @param {Object} cfg Xtype definition of item to add.
34698      */
34699     addxtype : function(cfg) {
34700         return this.layout.addxtype(cfg);
34701     
34702     }
34703 });        /*
34704  * Based on:
34705  * Ext JS Library 1.1.1
34706  * Copyright(c) 2006-2007, Ext JS, LLC.
34707  *
34708  * Originally Released Under LGPL - original licence link has changed is not relivant.
34709  *
34710  * Fork - LGPL
34711  * <script type="text/javascript">
34712  */
34713 /**
34714  * @class Roo.TabPanel
34715  * @extends Roo.util.Observable
34716  * A lightweight tab container.
34717  * <br><br>
34718  * Usage:
34719  * <pre><code>
34720 // basic tabs 1, built from existing content
34721 var tabs = new Roo.TabPanel("tabs1");
34722 tabs.addTab("script", "View Script");
34723 tabs.addTab("markup", "View Markup");
34724 tabs.activate("script");
34725
34726 // more advanced tabs, built from javascript
34727 var jtabs = new Roo.TabPanel("jtabs");
34728 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34729
34730 // set up the UpdateManager
34731 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34732 var updater = tab2.getUpdateManager();
34733 updater.setDefaultUrl("ajax1.htm");
34734 tab2.on('activate', updater.refresh, updater, true);
34735
34736 // Use setUrl for Ajax loading
34737 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34738 tab3.setUrl("ajax2.htm", null, true);
34739
34740 // Disabled tab
34741 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34742 tab4.disable();
34743
34744 jtabs.activate("jtabs-1");
34745  * </code></pre>
34746  * @constructor
34747  * Create a new TabPanel.
34748  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34749  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34750  */
34751 Roo.bootstrap.panel.Tabs = function(config){
34752     /**
34753     * The container element for this TabPanel.
34754     * @type Roo.Element
34755     */
34756     this.el = Roo.get(config.el);
34757     delete config.el;
34758     if(config){
34759         if(typeof config == "boolean"){
34760             this.tabPosition = config ? "bottom" : "top";
34761         }else{
34762             Roo.apply(this, config);
34763         }
34764     }
34765     
34766     if(this.tabPosition == "bottom"){
34767         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34768         this.el.addClass("roo-tabs-bottom");
34769     }
34770     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34771     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34772     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34773     if(Roo.isIE){
34774         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34775     }
34776     if(this.tabPosition != "bottom"){
34777         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34778          * @type Roo.Element
34779          */
34780         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34781         this.el.addClass("roo-tabs-top");
34782     }
34783     this.items = [];
34784
34785     this.bodyEl.setStyle("position", "relative");
34786
34787     this.active = null;
34788     this.activateDelegate = this.activate.createDelegate(this);
34789
34790     this.addEvents({
34791         /**
34792          * @event tabchange
34793          * Fires when the active tab changes
34794          * @param {Roo.TabPanel} this
34795          * @param {Roo.TabPanelItem} activePanel The new active tab
34796          */
34797         "tabchange": true,
34798         /**
34799          * @event beforetabchange
34800          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34801          * @param {Roo.TabPanel} this
34802          * @param {Object} e Set cancel to true on this object to cancel the tab change
34803          * @param {Roo.TabPanelItem} tab The tab being changed to
34804          */
34805         "beforetabchange" : true
34806     });
34807
34808     Roo.EventManager.onWindowResize(this.onResize, this);
34809     this.cpad = this.el.getPadding("lr");
34810     this.hiddenCount = 0;
34811
34812
34813     // toolbar on the tabbar support...
34814     if (this.toolbar) {
34815         alert("no toolbar support yet");
34816         this.toolbar  = false;
34817         /*
34818         var tcfg = this.toolbar;
34819         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34820         this.toolbar = new Roo.Toolbar(tcfg);
34821         if (Roo.isSafari) {
34822             var tbl = tcfg.container.child('table', true);
34823             tbl.setAttribute('width', '100%');
34824         }
34825         */
34826         
34827     }
34828    
34829
34830
34831     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34832 };
34833
34834 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34835     /*
34836      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34837      */
34838     tabPosition : "top",
34839     /*
34840      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34841      */
34842     currentTabWidth : 0,
34843     /*
34844      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34845      */
34846     minTabWidth : 40,
34847     /*
34848      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34849      */
34850     maxTabWidth : 250,
34851     /*
34852      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34853      */
34854     preferredTabWidth : 175,
34855     /*
34856      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34857      */
34858     resizeTabs : false,
34859     /*
34860      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34861      */
34862     monitorResize : true,
34863     /*
34864      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34865      */
34866     toolbar : false,
34867
34868     /**
34869      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34870      * @param {String} id The id of the div to use <b>or create</b>
34871      * @param {String} text The text for the tab
34872      * @param {String} content (optional) Content to put in the TabPanelItem body
34873      * @param {Boolean} closable (optional) True to create a close icon on the tab
34874      * @return {Roo.TabPanelItem} The created TabPanelItem
34875      */
34876     addTab : function(id, text, content, closable)
34877     {
34878         var item = new Roo.bootstrap.panel.TabItem({
34879             panel: this,
34880             id : id,
34881             text : text,
34882             closable : closable
34883         });
34884         this.addTabItem(item);
34885         if(content){
34886             item.setContent(content);
34887         }
34888         return item;
34889     },
34890
34891     /**
34892      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34893      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34894      * @return {Roo.TabPanelItem}
34895      */
34896     getTab : function(id){
34897         return this.items[id];
34898     },
34899
34900     /**
34901      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34902      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34903      */
34904     hideTab : function(id){
34905         var t = this.items[id];
34906         if(!t.isHidden()){
34907            t.setHidden(true);
34908            this.hiddenCount++;
34909            this.autoSizeTabs();
34910         }
34911     },
34912
34913     /**
34914      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34915      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34916      */
34917     unhideTab : function(id){
34918         var t = this.items[id];
34919         if(t.isHidden()){
34920            t.setHidden(false);
34921            this.hiddenCount--;
34922            this.autoSizeTabs();
34923         }
34924     },
34925
34926     /**
34927      * Adds an existing {@link Roo.TabPanelItem}.
34928      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34929      */
34930     addTabItem : function(item){
34931         this.items[item.id] = item;
34932         this.items.push(item);
34933       //  if(this.resizeTabs){
34934     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34935   //         this.autoSizeTabs();
34936 //        }else{
34937 //            item.autoSize();
34938        // }
34939     },
34940
34941     /**
34942      * Removes a {@link Roo.TabPanelItem}.
34943      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34944      */
34945     removeTab : function(id){
34946         var items = this.items;
34947         var tab = items[id];
34948         if(!tab) { return; }
34949         var index = items.indexOf(tab);
34950         if(this.active == tab && items.length > 1){
34951             var newTab = this.getNextAvailable(index);
34952             if(newTab) {
34953                 newTab.activate();
34954             }
34955         }
34956         this.stripEl.dom.removeChild(tab.pnode.dom);
34957         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34958             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34959         }
34960         items.splice(index, 1);
34961         delete this.items[tab.id];
34962         tab.fireEvent("close", tab);
34963         tab.purgeListeners();
34964         this.autoSizeTabs();
34965     },
34966
34967     getNextAvailable : function(start){
34968         var items = this.items;
34969         var index = start;
34970         // look for a next tab that will slide over to
34971         // replace the one being removed
34972         while(index < items.length){
34973             var item = items[++index];
34974             if(item && !item.isHidden()){
34975                 return item;
34976             }
34977         }
34978         // if one isn't found select the previous tab (on the left)
34979         index = start;
34980         while(index >= 0){
34981             var item = items[--index];
34982             if(item && !item.isHidden()){
34983                 return item;
34984             }
34985         }
34986         return null;
34987     },
34988
34989     /**
34990      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34991      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34992      */
34993     disableTab : function(id){
34994         var tab = this.items[id];
34995         if(tab && this.active != tab){
34996             tab.disable();
34997         }
34998     },
34999
35000     /**
35001      * Enables a {@link Roo.TabPanelItem} that is disabled.
35002      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35003      */
35004     enableTab : function(id){
35005         var tab = this.items[id];
35006         tab.enable();
35007     },
35008
35009     /**
35010      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35011      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35012      * @return {Roo.TabPanelItem} The TabPanelItem.
35013      */
35014     activate : function(id){
35015         var tab = this.items[id];
35016         if(!tab){
35017             return null;
35018         }
35019         if(tab == this.active || tab.disabled){
35020             return tab;
35021         }
35022         var e = {};
35023         this.fireEvent("beforetabchange", this, e, tab);
35024         if(e.cancel !== true && !tab.disabled){
35025             if(this.active){
35026                 this.active.hide();
35027             }
35028             this.active = this.items[id];
35029             this.active.show();
35030             this.fireEvent("tabchange", this, this.active);
35031         }
35032         return tab;
35033     },
35034
35035     /**
35036      * Gets the active {@link Roo.TabPanelItem}.
35037      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35038      */
35039     getActiveTab : function(){
35040         return this.active;
35041     },
35042
35043     /**
35044      * Updates the tab body element to fit the height of the container element
35045      * for overflow scrolling
35046      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35047      */
35048     syncHeight : function(targetHeight){
35049         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35050         var bm = this.bodyEl.getMargins();
35051         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35052         this.bodyEl.setHeight(newHeight);
35053         return newHeight;
35054     },
35055
35056     onResize : function(){
35057         if(this.monitorResize){
35058             this.autoSizeTabs();
35059         }
35060     },
35061
35062     /**
35063      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35064      */
35065     beginUpdate : function(){
35066         this.updating = true;
35067     },
35068
35069     /**
35070      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35071      */
35072     endUpdate : function(){
35073         this.updating = false;
35074         this.autoSizeTabs();
35075     },
35076
35077     /**
35078      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35079      */
35080     autoSizeTabs : function(){
35081         var count = this.items.length;
35082         var vcount = count - this.hiddenCount;
35083         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35084             return;
35085         }
35086         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35087         var availWidth = Math.floor(w / vcount);
35088         var b = this.stripBody;
35089         if(b.getWidth() > w){
35090             var tabs = this.items;
35091             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35092             if(availWidth < this.minTabWidth){
35093                 /*if(!this.sleft){    // incomplete scrolling code
35094                     this.createScrollButtons();
35095                 }
35096                 this.showScroll();
35097                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35098             }
35099         }else{
35100             if(this.currentTabWidth < this.preferredTabWidth){
35101                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35102             }
35103         }
35104     },
35105
35106     /**
35107      * Returns the number of tabs in this TabPanel.
35108      * @return {Number}
35109      */
35110      getCount : function(){
35111          return this.items.length;
35112      },
35113
35114     /**
35115      * Resizes all the tabs to the passed width
35116      * @param {Number} The new width
35117      */
35118     setTabWidth : function(width){
35119         this.currentTabWidth = width;
35120         for(var i = 0, len = this.items.length; i < len; i++) {
35121                 if(!this.items[i].isHidden()) {
35122                 this.items[i].setWidth(width);
35123             }
35124         }
35125     },
35126
35127     /**
35128      * Destroys this TabPanel
35129      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35130      */
35131     destroy : function(removeEl){
35132         Roo.EventManager.removeResizeListener(this.onResize, this);
35133         for(var i = 0, len = this.items.length; i < len; i++){
35134             this.items[i].purgeListeners();
35135         }
35136         if(removeEl === true){
35137             this.el.update("");
35138             this.el.remove();
35139         }
35140     },
35141     
35142     createStrip : function(container)
35143     {
35144         var strip = document.createElement("nav");
35145         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35146         container.appendChild(strip);
35147         return strip;
35148     },
35149     
35150     createStripList : function(strip)
35151     {
35152         // div wrapper for retard IE
35153         // returns the "tr" element.
35154         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35155         //'<div class="x-tabs-strip-wrap">'+
35156           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35157           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35158         return strip.firstChild; //.firstChild.firstChild.firstChild;
35159     },
35160     createBody : function(container)
35161     {
35162         var body = document.createElement("div");
35163         Roo.id(body, "tab-body");
35164         //Roo.fly(body).addClass("x-tabs-body");
35165         Roo.fly(body).addClass("tab-content");
35166         container.appendChild(body);
35167         return body;
35168     },
35169     createItemBody :function(bodyEl, id){
35170         var body = Roo.getDom(id);
35171         if(!body){
35172             body = document.createElement("div");
35173             body.id = id;
35174         }
35175         //Roo.fly(body).addClass("x-tabs-item-body");
35176         Roo.fly(body).addClass("tab-pane");
35177          bodyEl.insertBefore(body, bodyEl.firstChild);
35178         return body;
35179     },
35180     /** @private */
35181     createStripElements :  function(stripEl, text, closable)
35182     {
35183         var td = document.createElement("li"); // was td..
35184         
35185         
35186         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35187         
35188         
35189         stripEl.appendChild(td);
35190         /*if(closable){
35191             td.className = "x-tabs-closable";
35192             if(!this.closeTpl){
35193                 this.closeTpl = new Roo.Template(
35194                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35195                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35196                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
35197                 );
35198             }
35199             var el = this.closeTpl.overwrite(td, {"text": text});
35200             var close = el.getElementsByTagName("div")[0];
35201             var inner = el.getElementsByTagName("em")[0];
35202             return {"el": el, "close": close, "inner": inner};
35203         } else {
35204         */
35205         // not sure what this is..
35206             if(!this.tabTpl){
35207                 //this.tabTpl = new Roo.Template(
35208                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35209                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35210                 //);
35211                 this.tabTpl = new Roo.Template(
35212                    '<a href="#">' +
35213                    '<span unselectable="on"' +
35214                             (this.disableTooltips ? '' : ' title="{text}"') +
35215                             ' >{text}</span></span></a>'
35216                 );
35217                 
35218             }
35219             var el = this.tabTpl.overwrite(td, {"text": text});
35220             var inner = el.getElementsByTagName("span")[0];
35221             return {"el": el, "inner": inner};
35222         //}
35223     }
35224         
35225     
35226 });
35227
35228 /**
35229  * @class Roo.TabPanelItem
35230  * @extends Roo.util.Observable
35231  * Represents an individual item (tab plus body) in a TabPanel.
35232  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35233  * @param {String} id The id of this TabPanelItem
35234  * @param {String} text The text for the tab of this TabPanelItem
35235  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35236  */
35237 Roo.bootstrap.panel.TabItem = function(config){
35238     /**
35239      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35240      * @type Roo.TabPanel
35241      */
35242     this.tabPanel = config.panel;
35243     /**
35244      * The id for this TabPanelItem
35245      * @type String
35246      */
35247     this.id = config.id;
35248     /** @private */
35249     this.disabled = false;
35250     /** @private */
35251     this.text = config.text;
35252     /** @private */
35253     this.loaded = false;
35254     this.closable = config.closable;
35255
35256     /**
35257      * The body element for this TabPanelItem.
35258      * @type Roo.Element
35259      */
35260     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35261     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35262     this.bodyEl.setStyle("display", "block");
35263     this.bodyEl.setStyle("zoom", "1");
35264     //this.hideAction();
35265
35266     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35267     /** @private */
35268     this.el = Roo.get(els.el);
35269     this.inner = Roo.get(els.inner, true);
35270     this.textEl = Roo.get(this.el.dom.firstChild, true);
35271     this.pnode = Roo.get(els.el.parentNode, true);
35272     this.el.on("mousedown", this.onTabMouseDown, this);
35273     this.el.on("click", this.onTabClick, this);
35274     /** @private */
35275     if(config.closable){
35276         var c = Roo.get(els.close, true);
35277         c.dom.title = this.closeText;
35278         c.addClassOnOver("close-over");
35279         c.on("click", this.closeClick, this);
35280      }
35281
35282     this.addEvents({
35283          /**
35284          * @event activate
35285          * Fires when this tab becomes the active tab.
35286          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35287          * @param {Roo.TabPanelItem} this
35288          */
35289         "activate": true,
35290         /**
35291          * @event beforeclose
35292          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35293          * @param {Roo.TabPanelItem} this
35294          * @param {Object} e Set cancel to true on this object to cancel the close.
35295          */
35296         "beforeclose": true,
35297         /**
35298          * @event close
35299          * Fires when this tab is closed.
35300          * @param {Roo.TabPanelItem} this
35301          */
35302          "close": true,
35303         /**
35304          * @event deactivate
35305          * Fires when this tab is no longer the active tab.
35306          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35307          * @param {Roo.TabPanelItem} this
35308          */
35309          "deactivate" : true
35310     });
35311     this.hidden = false;
35312
35313     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35314 };
35315
35316 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35317            {
35318     purgeListeners : function(){
35319        Roo.util.Observable.prototype.purgeListeners.call(this);
35320        this.el.removeAllListeners();
35321     },
35322     /**
35323      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35324      */
35325     show : function(){
35326         this.pnode.addClass("active");
35327         this.showAction();
35328         if(Roo.isOpera){
35329             this.tabPanel.stripWrap.repaint();
35330         }
35331         this.fireEvent("activate", this.tabPanel, this);
35332     },
35333
35334     /**
35335      * Returns true if this tab is the active tab.
35336      * @return {Boolean}
35337      */
35338     isActive : function(){
35339         return this.tabPanel.getActiveTab() == this;
35340     },
35341
35342     /**
35343      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35344      */
35345     hide : function(){
35346         this.pnode.removeClass("active");
35347         this.hideAction();
35348         this.fireEvent("deactivate", this.tabPanel, this);
35349     },
35350
35351     hideAction : function(){
35352         this.bodyEl.hide();
35353         this.bodyEl.setStyle("position", "absolute");
35354         this.bodyEl.setLeft("-20000px");
35355         this.bodyEl.setTop("-20000px");
35356     },
35357
35358     showAction : function(){
35359         this.bodyEl.setStyle("position", "relative");
35360         this.bodyEl.setTop("");
35361         this.bodyEl.setLeft("");
35362         this.bodyEl.show();
35363     },
35364
35365     /**
35366      * Set the tooltip for the tab.
35367      * @param {String} tooltip The tab's tooltip
35368      */
35369     setTooltip : function(text){
35370         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35371             this.textEl.dom.qtip = text;
35372             this.textEl.dom.removeAttribute('title');
35373         }else{
35374             this.textEl.dom.title = text;
35375         }
35376     },
35377
35378     onTabClick : function(e){
35379         e.preventDefault();
35380         this.tabPanel.activate(this.id);
35381     },
35382
35383     onTabMouseDown : function(e){
35384         e.preventDefault();
35385         this.tabPanel.activate(this.id);
35386     },
35387 /*
35388     getWidth : function(){
35389         return this.inner.getWidth();
35390     },
35391
35392     setWidth : function(width){
35393         var iwidth = width - this.pnode.getPadding("lr");
35394         this.inner.setWidth(iwidth);
35395         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35396         this.pnode.setWidth(width);
35397     },
35398 */
35399     /**
35400      * Show or hide the tab
35401      * @param {Boolean} hidden True to hide or false to show.
35402      */
35403     setHidden : function(hidden){
35404         this.hidden = hidden;
35405         this.pnode.setStyle("display", hidden ? "none" : "");
35406     },
35407
35408     /**
35409      * Returns true if this tab is "hidden"
35410      * @return {Boolean}
35411      */
35412     isHidden : function(){
35413         return this.hidden;
35414     },
35415
35416     /**
35417      * Returns the text for this tab
35418      * @return {String}
35419      */
35420     getText : function(){
35421         return this.text;
35422     },
35423     /*
35424     autoSize : function(){
35425         //this.el.beginMeasure();
35426         this.textEl.setWidth(1);
35427         /*
35428          *  #2804 [new] Tabs in Roojs
35429          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35430          */
35431         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35432         //this.el.endMeasure();
35433     //},
35434
35435     /**
35436      * Sets the text for the tab (Note: this also sets the tooltip text)
35437      * @param {String} text The tab's text and tooltip
35438      */
35439     setText : function(text){
35440         this.text = text;
35441         this.textEl.update(text);
35442         this.setTooltip(text);
35443         //if(!this.tabPanel.resizeTabs){
35444         //    this.autoSize();
35445         //}
35446     },
35447     /**
35448      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35449      */
35450     activate : function(){
35451         this.tabPanel.activate(this.id);
35452     },
35453
35454     /**
35455      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35456      */
35457     disable : function(){
35458         if(this.tabPanel.active != this){
35459             this.disabled = true;
35460             this.pnode.addClass("disabled");
35461         }
35462     },
35463
35464     /**
35465      * Enables this TabPanelItem if it was previously disabled.
35466      */
35467     enable : function(){
35468         this.disabled = false;
35469         this.pnode.removeClass("disabled");
35470     },
35471
35472     /**
35473      * Sets the content for this TabPanelItem.
35474      * @param {String} content The content
35475      * @param {Boolean} loadScripts true to look for and load scripts
35476      */
35477     setContent : function(content, loadScripts){
35478         this.bodyEl.update(content, loadScripts);
35479     },
35480
35481     /**
35482      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35483      * @return {Roo.UpdateManager} The UpdateManager
35484      */
35485     getUpdateManager : function(){
35486         return this.bodyEl.getUpdateManager();
35487     },
35488
35489     /**
35490      * Set a URL to be used to load the content for this TabPanelItem.
35491      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35492      * @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)
35493      * @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)
35494      * @return {Roo.UpdateManager} The UpdateManager
35495      */
35496     setUrl : function(url, params, loadOnce){
35497         if(this.refreshDelegate){
35498             this.un('activate', this.refreshDelegate);
35499         }
35500         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35501         this.on("activate", this.refreshDelegate);
35502         return this.bodyEl.getUpdateManager();
35503     },
35504
35505     /** @private */
35506     _handleRefresh : function(url, params, loadOnce){
35507         if(!loadOnce || !this.loaded){
35508             var updater = this.bodyEl.getUpdateManager();
35509             updater.update(url, params, this._setLoaded.createDelegate(this));
35510         }
35511     },
35512
35513     /**
35514      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35515      *   Will fail silently if the setUrl method has not been called.
35516      *   This does not activate the panel, just updates its content.
35517      */
35518     refresh : function(){
35519         if(this.refreshDelegate){
35520            this.loaded = false;
35521            this.refreshDelegate();
35522         }
35523     },
35524
35525     /** @private */
35526     _setLoaded : function(){
35527         this.loaded = true;
35528     },
35529
35530     /** @private */
35531     closeClick : function(e){
35532         var o = {};
35533         e.stopEvent();
35534         this.fireEvent("beforeclose", this, o);
35535         if(o.cancel !== true){
35536             this.tabPanel.removeTab(this.id);
35537         }
35538     },
35539     /**
35540      * The text displayed in the tooltip for the close icon.
35541      * @type String
35542      */
35543     closeText : "Close this tab"
35544 });