fa89ad21a4986be8568aa7ce4a0e86a38b4b578c
[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.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19302             cls : 'roo-' + this.inputType, //'form-box',
19303             placeholder : this.placeholder || ''
19304             
19305         };
19306         
19307         if (this.weight) { // Validity check?
19308             cfg.cls += " " + this.inputType + "-" + this.weight;
19309         }
19310         
19311         if (this.disabled) {
19312             input.disabled=true;
19313         }
19314         
19315         if(this.checked){
19316             input.checked = this.checked;
19317         }
19318         
19319         if (this.name) {
19320             input.name = this.name;
19321         }
19322         
19323         if (this.size) {
19324             input.cls += ' input-' + this.size;
19325         }
19326         
19327         var settings=this;
19328         
19329         ['xs','sm','md','lg'].map(function(size){
19330             if (settings[size]) {
19331                 cfg.cls += ' col-' + size + '-' + settings[size];
19332             }
19333         });
19334         
19335         var inputblock = input;
19336          
19337         if (this.before || this.after) {
19338             
19339             inputblock = {
19340                 cls : 'input-group',
19341                 cn :  [] 
19342             };
19343             
19344             if (this.before) {
19345                 inputblock.cn.push({
19346                     tag :'span',
19347                     cls : 'input-group-addon',
19348                     html : this.before
19349                 });
19350             }
19351             
19352             inputblock.cn.push(input);
19353             
19354             if (this.after) {
19355                 inputblock.cn.push({
19356                     tag :'span',
19357                     cls : 'input-group-addon',
19358                     html : this.after
19359                 });
19360             }
19361             
19362         }
19363         
19364         if (align ==='left' && this.fieldLabel.length) {
19365 //                Roo.log("left and has label");
19366                 cfg.cn = [
19367                     
19368                     {
19369                         tag: 'label',
19370                         'for' :  id,
19371                         cls : 'control-label col-md-' + this.labelWidth,
19372                         html : this.fieldLabel
19373                         
19374                     },
19375                     {
19376                         cls : "col-md-" + (12 - this.labelWidth), 
19377                         cn: [
19378                             inputblock
19379                         ]
19380                     }
19381                     
19382                 ];
19383         } else if ( this.fieldLabel.length) {
19384 //                Roo.log(" label");
19385                 cfg.cn = [
19386                    
19387                     {
19388                         tag: this.boxLabel ? 'span' : 'label',
19389                         'for': id,
19390                         cls: 'control-label box-input-label',
19391                         //cls : 'input-group-addon',
19392                         html : this.fieldLabel
19393                         
19394                     },
19395                     
19396                     inputblock
19397                     
19398                 ];
19399
19400         } else {
19401             
19402 //                Roo.log(" no label && no align");
19403                 cfg.cn = [  inputblock ] ;
19404                 
19405                 
19406         }
19407         
19408         if(this.boxLabel){
19409              var boxLabelCfg = {
19410                 tag: 'label',
19411                 //'for': id, // box label is handled by onclick - so no for...
19412                 cls: 'box-label',
19413                 html: this.boxLabel
19414             };
19415             
19416             if(this.tooltip){
19417                 boxLabelCfg.tooltip = this.tooltip;
19418             }
19419              
19420             cfg.cn.push(boxLabelCfg);
19421         }
19422         
19423         
19424        
19425         return cfg;
19426         
19427     },
19428     
19429     /**
19430      * return the real input element.
19431      */
19432     inputEl: function ()
19433     {
19434         return this.el.select('input.roo-' + this.inputType,true).first();
19435     },
19436     
19437     labelEl: function()
19438     {
19439         return this.el.select('label.control-label',true).first();
19440     },
19441     /* depricated... */
19442     
19443     label: function()
19444     {
19445         return this.labelEl();
19446     },
19447     
19448     boxLabelEl: function()
19449     {
19450         return this.el.select('label.box-label',true).first();
19451     },
19452     
19453     initEvents : function()
19454     {
19455 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19456         
19457         this.inputEl().on('click', this.onClick,  this);
19458         
19459         if (this.boxLabel) { 
19460             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19461         }
19462         
19463         this.startValue = this.getValue();
19464         
19465         if(this.groupId){
19466             Roo.bootstrap.CheckBox.register(this);
19467         }
19468     },
19469     
19470     onClick : function()
19471     {   
19472         this.setChecked(!this.checked);
19473     },
19474     
19475     setChecked : function(state,suppressEvent)
19476     {
19477         this.startValue = this.getValue();
19478         
19479         if(this.inputType == 'radio'){
19480             
19481             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19482                 e.dom.checked = false;
19483             });
19484             
19485             this.inputEl().dom.checked = true;
19486             
19487             this.inputEl().dom.value = this.inputValue;
19488             
19489             if(suppressEvent !== true){
19490                 this.fireEvent('check', this, true);
19491             }
19492             
19493             this.validate();
19494             
19495             return;
19496         }
19497         
19498         this.checked = state;
19499         
19500         this.inputEl().dom.checked = state;
19501         
19502         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19503         
19504         if(suppressEvent !== true){
19505             this.fireEvent('check', this, state);
19506         }
19507         
19508         this.validate();
19509     },
19510     
19511     getValue : function()
19512     {
19513         if(this.inputType == 'radio'){
19514             return this.getGroupValue();
19515         }
19516         
19517         return this.inputEl().getValue();
19518         
19519     },
19520     
19521     getGroupValue : function()
19522     {
19523         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19524             return '';
19525         }
19526         
19527         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19528     },
19529     
19530     setValue : function(v,suppressEvent)
19531     {
19532         if(this.inputType == 'radio'){
19533             this.setGroupValue(v, suppressEvent);
19534             return;
19535         }
19536         
19537         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19538         
19539         this.validate();
19540     },
19541     
19542     setGroupValue : function(v, suppressEvent)
19543     {
19544         this.startValue = this.getValue();
19545         
19546         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19547             e.dom.checked = false;
19548             
19549             if(e.dom.value == v){
19550                 e.dom.checked = true;
19551             }
19552         });
19553         
19554         if(suppressEvent !== true){
19555             this.fireEvent('check', this, true);
19556         }
19557
19558         this.validate();
19559         
19560         return;
19561     },
19562     
19563     validate : function()
19564     {
19565         if(
19566                 this.disabled || 
19567                 (this.inputType == 'radio' && this.validateRadio()) ||
19568                 (this.inputType == 'checkbox' && this.validateCheckbox())
19569         ){
19570             this.markValid();
19571             return true;
19572         }
19573         
19574         this.markInvalid();
19575         return false;
19576     },
19577     
19578     validateRadio : function()
19579     {
19580         if(this.allowBlank){
19581             return true;
19582         }
19583         
19584         var valid = false;
19585         
19586         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19587             if(!e.dom.checked){
19588                 return;
19589             }
19590             
19591             valid = true;
19592             
19593             return false;
19594         });
19595         
19596         return valid;
19597     },
19598     
19599     validateCheckbox : function()
19600     {
19601         if(!this.groupId){
19602             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19603         }
19604         
19605         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19606         
19607         if(!group){
19608             return false;
19609         }
19610         
19611         var r = false;
19612         
19613         for(var i in group){
19614             if(r){
19615                 break;
19616             }
19617             
19618             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19619         }
19620         
19621         return r;
19622     },
19623     
19624     /**
19625      * Mark this field as valid
19626      */
19627     markValid : function()
19628     {
19629         if(this.allowBlank){
19630             return;
19631         }
19632         
19633         var _this = this;
19634         
19635         this.fireEvent('valid', this);
19636         
19637         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19638         
19639         if(this.groupId){
19640             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19641         }
19642         
19643         if(label){
19644             label.markValid();
19645         }
19646         
19647         if(this.inputType == 'radio'){
19648             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19649                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19650                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19651             });
19652             
19653             return;
19654         }
19655         
19656         if(!this.groupId){
19657             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19658             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19659             return;
19660         }
19661         
19662         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19663             
19664         if(!group){
19665             return;
19666         }
19667         
19668         for(var i in group){
19669             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19670             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19671         }
19672     },
19673     
19674      /**
19675      * Mark this field as invalid
19676      * @param {String} msg The validation message
19677      */
19678     markInvalid : function(msg)
19679     {
19680         if(this.allowBlank){
19681             return;
19682         }
19683         
19684         var _this = this;
19685         
19686         this.fireEvent('invalid', this, msg);
19687         
19688         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19689         
19690         if(this.groupId){
19691             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19692         }
19693         
19694         if(label){
19695             label.markInvalid();
19696         }
19697             
19698         if(this.inputType == 'radio'){
19699             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19700                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19701                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19702             });
19703             
19704             return;
19705         }
19706         
19707         if(!this.groupId){
19708             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19709             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19710             return;
19711         }
19712         
19713         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19714         
19715         if(!group){
19716             return;
19717         }
19718         
19719         for(var i in group){
19720             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19721             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19722         }
19723         
19724     },
19725     
19726     disable : function()
19727     {
19728         if(this.inputType != 'radio'){
19729             Roo.bootstrap.CheckBox.superclass.disable.call(this);
19730             return;
19731         }
19732         
19733         var _this = this;
19734         
19735         if(this.rendered){
19736             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19737                 _this.getActionEl().addClass(this.disabledClass);
19738                 e.dom.disabled = true;
19739             });
19740         }
19741         
19742         this.disabled = true;
19743         this.fireEvent("disable", this);
19744         return this;
19745     },
19746
19747     enable : function()
19748     {
19749         if(this.inputType != 'radio'){
19750             Roo.bootstrap.CheckBox.superclass.enable.call(this);
19751             return;
19752         }
19753         
19754         var _this = this;
19755         
19756         if(this.rendered){
19757             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19758                 _this.getActionEl().removeClass(this.disabledClass);
19759                 e.dom.disabled = false;
19760             });
19761         }
19762         
19763         this.disabled = false;
19764         this.fireEvent("enable", this);
19765         return this;
19766     }
19767     
19768
19769 });
19770
19771 Roo.apply(Roo.bootstrap.CheckBox, {
19772     
19773     groups: {},
19774     
19775      /**
19776     * register a CheckBox Group
19777     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19778     */
19779     register : function(checkbox)
19780     {
19781         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19782             this.groups[checkbox.groupId] = {};
19783         }
19784         
19785         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19786             return;
19787         }
19788         
19789         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19790         
19791     },
19792     /**
19793     * fetch a CheckBox Group based on the group ID
19794     * @param {string} the group ID
19795     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19796     */
19797     get: function(groupId) {
19798         if (typeof(this.groups[groupId]) == 'undefined') {
19799             return false;
19800         }
19801         
19802         return this.groups[groupId] ;
19803     }
19804     
19805     
19806 });
19807 /*
19808  * - LGPL
19809  *
19810  * Radio
19811  *
19812  *
19813  * not inline
19814  *<div class="radio">
19815   <label>
19816     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19817     Option one is this and that&mdash;be sure to include why it's great
19818   </label>
19819 </div>
19820  *
19821  *
19822  *inline
19823  *<span>
19824  *<label class="radio-inline">fieldLabel</label>
19825  *<label class="radio-inline">
19826   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19827 </label>
19828 <span>
19829  * 
19830  * 
19831  */
19832
19833 /**
19834  * @class Roo.bootstrap.Radio
19835  * @extends Roo.bootstrap.CheckBox
19836  * Bootstrap Radio class
19837
19838  * @constructor
19839  * Create a new Radio
19840  * @param {Object} config The config object
19841  */
19842
19843 Roo.bootstrap.Radio = function(config){
19844     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19845    
19846 };
19847
19848 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19849     
19850     inputType: 'radio',
19851     inputValue: '',
19852     valueOff: '',
19853     
19854     getAutoCreate : function()
19855     {
19856         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19857         align = align || 'left'; // default...
19858         
19859         
19860         
19861         var id = Roo.id();
19862         
19863         var cfg = {
19864                 tag : this.inline ? 'span' : 'div',
19865                 cls : '',
19866                 cn : []
19867         };
19868         
19869         var inline = this.inline ? ' radio-inline' : '';
19870         
19871         var lbl = {
19872                 tag: 'label' ,
19873                 // does not need for, as we wrap the input with it..
19874                 'for' : id,
19875                 cls : 'control-label box-label' + inline,
19876                 cn : []
19877         };
19878         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19879         
19880         var fieldLabel = {
19881             tag: 'label' ,
19882             //cls : 'control-label' + inline,
19883             html : this.fieldLabel,
19884             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19885         };
19886         
19887  
19888         
19889         
19890         var input =  {
19891             tag: 'input',
19892             id : id,
19893             type : this.inputType,
19894             //value : (!this.checked) ? this.valueOff : this.inputValue,
19895             value : this.inputValue,
19896             cls : 'roo-radio',
19897             placeholder : this.placeholder || '' // ?? needed????
19898             
19899         };
19900         if (this.weight) { // Validity check?
19901             input.cls += " radio-" + this.weight;
19902         }
19903         if (this.disabled) {
19904             input.disabled=true;
19905         }
19906         
19907         if(this.checked){
19908             input.checked = this.checked;
19909         }
19910         
19911         if (this.name) {
19912             input.name = this.name;
19913         }
19914         
19915         if (this.size) {
19916             input.cls += ' input-' + this.size;
19917         }
19918         
19919         //?? can span's inline have a width??
19920         
19921         var settings=this;
19922         ['xs','sm','md','lg'].map(function(size){
19923             if (settings[size]) {
19924                 cfg.cls += ' col-' + size + '-' + settings[size];
19925             }
19926         });
19927         
19928         var inputblock = input;
19929         
19930         if (this.before || this.after) {
19931             
19932             inputblock = {
19933                 cls : 'input-group',
19934                 tag : 'span',
19935                 cn :  [] 
19936             };
19937             if (this.before) {
19938                 inputblock.cn.push({
19939                     tag :'span',
19940                     cls : 'input-group-addon',
19941                     html : this.before
19942                 });
19943             }
19944             inputblock.cn.push(input);
19945             if (this.after) {
19946                 inputblock.cn.push({
19947                     tag :'span',
19948                     cls : 'input-group-addon',
19949                     html : this.after
19950                 });
19951             }
19952             
19953         };
19954         
19955         
19956         if (this.fieldLabel && this.fieldLabel.length) {
19957             cfg.cn.push(fieldLabel);
19958         }
19959        
19960         // normal bootstrap puts the input inside the label.
19961         // however with our styled version - it has to go after the input.
19962        
19963         //lbl.cn.push(inputblock);
19964         
19965         var lblwrap =  {
19966             tag: 'span',
19967             cls: 'radio' + inline,
19968             cn: [
19969                 inputblock,
19970                 lbl
19971             ]
19972         };
19973         
19974         cfg.cn.push( lblwrap);
19975         
19976         if(this.boxLabel){
19977             lbl.cn.push({
19978                 tag: 'span',
19979                 html: this.boxLabel
19980             })
19981         }
19982          
19983         
19984         return cfg;
19985         
19986     },
19987     
19988     initEvents : function()
19989     {
19990 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19991         
19992         this.inputEl().on('click', this.onClick,  this);
19993         if (this.boxLabel) {
19994             //Roo.log('find label');
19995             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19996         }
19997         
19998     },
19999     
20000     inputEl: function ()
20001     {
20002         return this.el.select('input.roo-radio',true).first();
20003     },
20004     onClick : function()
20005     {   
20006         Roo.log("click");
20007         this.setChecked(true);
20008     },
20009     
20010     setChecked : function(state,suppressEvent)
20011     {
20012         if(state){
20013             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20014                 v.dom.checked = false;
20015             });
20016         }
20017         Roo.log(this.inputEl().dom);
20018         this.checked = state;
20019         this.inputEl().dom.checked = state;
20020         
20021         if(suppressEvent !== true){
20022             this.fireEvent('check', this, state);
20023         }
20024         
20025         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20026         
20027     },
20028     
20029     getGroupValue : function()
20030     {
20031         var value = '';
20032         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20033             if(v.dom.checked == true){
20034                 value = v.dom.value;
20035             }
20036         });
20037         
20038         return value;
20039     },
20040     
20041     /**
20042      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20043      * @return {Mixed} value The field value
20044      */
20045     getValue : function(){
20046         return this.getGroupValue();
20047     }
20048     
20049 });
20050
20051  
20052 //<script type="text/javascript">
20053
20054 /*
20055  * Based  Ext JS Library 1.1.1
20056  * Copyright(c) 2006-2007, Ext JS, LLC.
20057  * LGPL
20058  *
20059  */
20060  
20061 /**
20062  * @class Roo.HtmlEditorCore
20063  * @extends Roo.Component
20064  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20065  *
20066  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20067  */
20068
20069 Roo.HtmlEditorCore = function(config){
20070     
20071     
20072     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20073     
20074     
20075     this.addEvents({
20076         /**
20077          * @event initialize
20078          * Fires when the editor is fully initialized (including the iframe)
20079          * @param {Roo.HtmlEditorCore} this
20080          */
20081         initialize: true,
20082         /**
20083          * @event activate
20084          * Fires when the editor is first receives the focus. Any insertion must wait
20085          * until after this event.
20086          * @param {Roo.HtmlEditorCore} this
20087          */
20088         activate: true,
20089          /**
20090          * @event beforesync
20091          * Fires before the textarea is updated with content from the editor iframe. Return false
20092          * to cancel the sync.
20093          * @param {Roo.HtmlEditorCore} this
20094          * @param {String} html
20095          */
20096         beforesync: true,
20097          /**
20098          * @event beforepush
20099          * Fires before the iframe editor is updated with content from the textarea. Return false
20100          * to cancel the push.
20101          * @param {Roo.HtmlEditorCore} this
20102          * @param {String} html
20103          */
20104         beforepush: true,
20105          /**
20106          * @event sync
20107          * Fires when the textarea is updated with content from the editor iframe.
20108          * @param {Roo.HtmlEditorCore} this
20109          * @param {String} html
20110          */
20111         sync: true,
20112          /**
20113          * @event push
20114          * Fires when the iframe editor is updated with content from the textarea.
20115          * @param {Roo.HtmlEditorCore} this
20116          * @param {String} html
20117          */
20118         push: true,
20119         
20120         /**
20121          * @event editorevent
20122          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20123          * @param {Roo.HtmlEditorCore} this
20124          */
20125         editorevent: true
20126         
20127     });
20128     
20129     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20130     
20131     // defaults : white / black...
20132     this.applyBlacklists();
20133     
20134     
20135     
20136 };
20137
20138
20139 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20140
20141
20142      /**
20143      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20144      */
20145     
20146     owner : false,
20147     
20148      /**
20149      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20150      *                        Roo.resizable.
20151      */
20152     resizable : false,
20153      /**
20154      * @cfg {Number} height (in pixels)
20155      */   
20156     height: 300,
20157    /**
20158      * @cfg {Number} width (in pixels)
20159      */   
20160     width: 500,
20161     
20162     /**
20163      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20164      * 
20165      */
20166     stylesheets: false,
20167     
20168     // id of frame..
20169     frameId: false,
20170     
20171     // private properties
20172     validationEvent : false,
20173     deferHeight: true,
20174     initialized : false,
20175     activated : false,
20176     sourceEditMode : false,
20177     onFocus : Roo.emptyFn,
20178     iframePad:3,
20179     hideMode:'offsets',
20180     
20181     clearUp: true,
20182     
20183     // blacklist + whitelisted elements..
20184     black: false,
20185     white: false,
20186      
20187     
20188
20189     /**
20190      * Protected method that will not generally be called directly. It
20191      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20192      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20193      */
20194     getDocMarkup : function(){
20195         // body styles..
20196         var st = '';
20197         
20198         // inherit styels from page...?? 
20199         if (this.stylesheets === false) {
20200             
20201             Roo.get(document.head).select('style').each(function(node) {
20202                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20203             });
20204             
20205             Roo.get(document.head).select('link').each(function(node) { 
20206                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20207             });
20208             
20209         } else if (!this.stylesheets.length) {
20210                 // simple..
20211                 st = '<style type="text/css">' +
20212                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20213                    '</style>';
20214         } else { 
20215             
20216         }
20217         
20218         st +=  '<style type="text/css">' +
20219             'IMG { cursor: pointer } ' +
20220         '</style>';
20221
20222         
20223         return '<html><head>' + st  +
20224             //<style type="text/css">' +
20225             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20226             //'</style>' +
20227             ' </head><body class="roo-htmleditor-body"></body></html>';
20228     },
20229
20230     // private
20231     onRender : function(ct, position)
20232     {
20233         var _t = this;
20234         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20235         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20236         
20237         
20238         this.el.dom.style.border = '0 none';
20239         this.el.dom.setAttribute('tabIndex', -1);
20240         this.el.addClass('x-hidden hide');
20241         
20242         
20243         
20244         if(Roo.isIE){ // fix IE 1px bogus margin
20245             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20246         }
20247        
20248         
20249         this.frameId = Roo.id();
20250         
20251          
20252         
20253         var iframe = this.owner.wrap.createChild({
20254             tag: 'iframe',
20255             cls: 'form-control', // bootstrap..
20256             id: this.frameId,
20257             name: this.frameId,
20258             frameBorder : 'no',
20259             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20260         }, this.el
20261         );
20262         
20263         
20264         this.iframe = iframe.dom;
20265
20266          this.assignDocWin();
20267         
20268         this.doc.designMode = 'on';
20269        
20270         this.doc.open();
20271         this.doc.write(this.getDocMarkup());
20272         this.doc.close();
20273
20274         
20275         var task = { // must defer to wait for browser to be ready
20276             run : function(){
20277                 //console.log("run task?" + this.doc.readyState);
20278                 this.assignDocWin();
20279                 if(this.doc.body || this.doc.readyState == 'complete'){
20280                     try {
20281                         this.doc.designMode="on";
20282                     } catch (e) {
20283                         return;
20284                     }
20285                     Roo.TaskMgr.stop(task);
20286                     this.initEditor.defer(10, this);
20287                 }
20288             },
20289             interval : 10,
20290             duration: 10000,
20291             scope: this
20292         };
20293         Roo.TaskMgr.start(task);
20294
20295     },
20296
20297     // private
20298     onResize : function(w, h)
20299     {
20300          Roo.log('resize: ' +w + ',' + h );
20301         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20302         if(!this.iframe){
20303             return;
20304         }
20305         if(typeof w == 'number'){
20306             
20307             this.iframe.style.width = w + 'px';
20308         }
20309         if(typeof h == 'number'){
20310             
20311             this.iframe.style.height = h + 'px';
20312             if(this.doc){
20313                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20314             }
20315         }
20316         
20317     },
20318
20319     /**
20320      * Toggles the editor between standard and source edit mode.
20321      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20322      */
20323     toggleSourceEdit : function(sourceEditMode){
20324         
20325         this.sourceEditMode = sourceEditMode === true;
20326         
20327         if(this.sourceEditMode){
20328  
20329             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20330             
20331         }else{
20332             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20333             //this.iframe.className = '';
20334             this.deferFocus();
20335         }
20336         //this.setSize(this.owner.wrap.getSize());
20337         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20338     },
20339
20340     
20341   
20342
20343     /**
20344      * Protected method that will not generally be called directly. If you need/want
20345      * custom HTML cleanup, this is the method you should override.
20346      * @param {String} html The HTML to be cleaned
20347      * return {String} The cleaned HTML
20348      */
20349     cleanHtml : function(html){
20350         html = String(html);
20351         if(html.length > 5){
20352             if(Roo.isSafari){ // strip safari nonsense
20353                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20354             }
20355         }
20356         if(html == '&nbsp;'){
20357             html = '';
20358         }
20359         return html;
20360     },
20361
20362     /**
20363      * HTML Editor -> Textarea
20364      * Protected method that will not generally be called directly. Syncs the contents
20365      * of the editor iframe with the textarea.
20366      */
20367     syncValue : function(){
20368         if(this.initialized){
20369             var bd = (this.doc.body || this.doc.documentElement);
20370             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20371             var html = bd.innerHTML;
20372             if(Roo.isSafari){
20373                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20374                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20375                 if(m && m[1]){
20376                     html = '<div style="'+m[0]+'">' + html + '</div>';
20377                 }
20378             }
20379             html = this.cleanHtml(html);
20380             // fix up the special chars.. normaly like back quotes in word...
20381             // however we do not want to do this with chinese..
20382             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20383                 var cc = b.charCodeAt();
20384                 if (
20385                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20386                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20387                     (cc >= 0xf900 && cc < 0xfb00 )
20388                 ) {
20389                         return b;
20390                 }
20391                 return "&#"+cc+";" 
20392             });
20393             if(this.owner.fireEvent('beforesync', this, html) !== false){
20394                 this.el.dom.value = html;
20395                 this.owner.fireEvent('sync', this, html);
20396             }
20397         }
20398     },
20399
20400     /**
20401      * Protected method that will not generally be called directly. Pushes the value of the textarea
20402      * into the iframe editor.
20403      */
20404     pushValue : function(){
20405         if(this.initialized){
20406             var v = this.el.dom.value.trim();
20407             
20408 //            if(v.length < 1){
20409 //                v = '&#160;';
20410 //            }
20411             
20412             if(this.owner.fireEvent('beforepush', this, v) !== false){
20413                 var d = (this.doc.body || this.doc.documentElement);
20414                 d.innerHTML = v;
20415                 this.cleanUpPaste();
20416                 this.el.dom.value = d.innerHTML;
20417                 this.owner.fireEvent('push', this, v);
20418             }
20419         }
20420     },
20421
20422     // private
20423     deferFocus : function(){
20424         this.focus.defer(10, this);
20425     },
20426
20427     // doc'ed in Field
20428     focus : function(){
20429         if(this.win && !this.sourceEditMode){
20430             this.win.focus();
20431         }else{
20432             this.el.focus();
20433         }
20434     },
20435     
20436     assignDocWin: function()
20437     {
20438         var iframe = this.iframe;
20439         
20440          if(Roo.isIE){
20441             this.doc = iframe.contentWindow.document;
20442             this.win = iframe.contentWindow;
20443         } else {
20444 //            if (!Roo.get(this.frameId)) {
20445 //                return;
20446 //            }
20447 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20448 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20449             
20450             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20451                 return;
20452             }
20453             
20454             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20455             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20456         }
20457     },
20458     
20459     // private
20460     initEditor : function(){
20461         //console.log("INIT EDITOR");
20462         this.assignDocWin();
20463         
20464         
20465         
20466         this.doc.designMode="on";
20467         this.doc.open();
20468         this.doc.write(this.getDocMarkup());
20469         this.doc.close();
20470         
20471         var dbody = (this.doc.body || this.doc.documentElement);
20472         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20473         // this copies styles from the containing element into thsi one..
20474         // not sure why we need all of this..
20475         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20476         
20477         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20478         //ss['background-attachment'] = 'fixed'; // w3c
20479         dbody.bgProperties = 'fixed'; // ie
20480         //Roo.DomHelper.applyStyles(dbody, ss);
20481         Roo.EventManager.on(this.doc, {
20482             //'mousedown': this.onEditorEvent,
20483             'mouseup': this.onEditorEvent,
20484             'dblclick': this.onEditorEvent,
20485             'click': this.onEditorEvent,
20486             'keyup': this.onEditorEvent,
20487             buffer:100,
20488             scope: this
20489         });
20490         if(Roo.isGecko){
20491             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20492         }
20493         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20494             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20495         }
20496         this.initialized = true;
20497
20498         this.owner.fireEvent('initialize', this);
20499         this.pushValue();
20500     },
20501
20502     // private
20503     onDestroy : function(){
20504         
20505         
20506         
20507         if(this.rendered){
20508             
20509             //for (var i =0; i < this.toolbars.length;i++) {
20510             //    // fixme - ask toolbars for heights?
20511             //    this.toolbars[i].onDestroy();
20512            // }
20513             
20514             //this.wrap.dom.innerHTML = '';
20515             //this.wrap.remove();
20516         }
20517     },
20518
20519     // private
20520     onFirstFocus : function(){
20521         
20522         this.assignDocWin();
20523         
20524         
20525         this.activated = true;
20526          
20527     
20528         if(Roo.isGecko){ // prevent silly gecko errors
20529             this.win.focus();
20530             var s = this.win.getSelection();
20531             if(!s.focusNode || s.focusNode.nodeType != 3){
20532                 var r = s.getRangeAt(0);
20533                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20534                 r.collapse(true);
20535                 this.deferFocus();
20536             }
20537             try{
20538                 this.execCmd('useCSS', true);
20539                 this.execCmd('styleWithCSS', false);
20540             }catch(e){}
20541         }
20542         this.owner.fireEvent('activate', this);
20543     },
20544
20545     // private
20546     adjustFont: function(btn){
20547         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20548         //if(Roo.isSafari){ // safari
20549         //    adjust *= 2;
20550        // }
20551         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20552         if(Roo.isSafari){ // safari
20553             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20554             v =  (v < 10) ? 10 : v;
20555             v =  (v > 48) ? 48 : v;
20556             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20557             
20558         }
20559         
20560         
20561         v = Math.max(1, v+adjust);
20562         
20563         this.execCmd('FontSize', v  );
20564     },
20565
20566     onEditorEvent : function(e)
20567     {
20568         this.owner.fireEvent('editorevent', this, e);
20569       //  this.updateToolbar();
20570         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20571     },
20572
20573     insertTag : function(tg)
20574     {
20575         // could be a bit smarter... -> wrap the current selected tRoo..
20576         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20577             
20578             range = this.createRange(this.getSelection());
20579             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20580             wrappingNode.appendChild(range.extractContents());
20581             range.insertNode(wrappingNode);
20582
20583             return;
20584             
20585             
20586             
20587         }
20588         this.execCmd("formatblock",   tg);
20589         
20590     },
20591     
20592     insertText : function(txt)
20593     {
20594         
20595         
20596         var range = this.createRange();
20597         range.deleteContents();
20598                //alert(Sender.getAttribute('label'));
20599                
20600         range.insertNode(this.doc.createTextNode(txt));
20601     } ,
20602     
20603      
20604
20605     /**
20606      * Executes a Midas editor command on the editor document and performs necessary focus and
20607      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20608      * @param {String} cmd The Midas command
20609      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20610      */
20611     relayCmd : function(cmd, value){
20612         this.win.focus();
20613         this.execCmd(cmd, value);
20614         this.owner.fireEvent('editorevent', this);
20615         //this.updateToolbar();
20616         this.owner.deferFocus();
20617     },
20618
20619     /**
20620      * Executes a Midas editor command directly on the editor document.
20621      * For visual commands, you should use {@link #relayCmd} instead.
20622      * <b>This should only be called after the editor is initialized.</b>
20623      * @param {String} cmd The Midas command
20624      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20625      */
20626     execCmd : function(cmd, value){
20627         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20628         this.syncValue();
20629     },
20630  
20631  
20632    
20633     /**
20634      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20635      * to insert tRoo.
20636      * @param {String} text | dom node.. 
20637      */
20638     insertAtCursor : function(text)
20639     {
20640         
20641         
20642         
20643         if(!this.activated){
20644             return;
20645         }
20646         /*
20647         if(Roo.isIE){
20648             this.win.focus();
20649             var r = this.doc.selection.createRange();
20650             if(r){
20651                 r.collapse(true);
20652                 r.pasteHTML(text);
20653                 this.syncValue();
20654                 this.deferFocus();
20655             
20656             }
20657             return;
20658         }
20659         */
20660         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20661             this.win.focus();
20662             
20663             
20664             // from jquery ui (MIT licenced)
20665             var range, node;
20666             var win = this.win;
20667             
20668             if (win.getSelection && win.getSelection().getRangeAt) {
20669                 range = win.getSelection().getRangeAt(0);
20670                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20671                 range.insertNode(node);
20672             } else if (win.document.selection && win.document.selection.createRange) {
20673                 // no firefox support
20674                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20675                 win.document.selection.createRange().pasteHTML(txt);
20676             } else {
20677                 // no firefox support
20678                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20679                 this.execCmd('InsertHTML', txt);
20680             } 
20681             
20682             this.syncValue();
20683             
20684             this.deferFocus();
20685         }
20686     },
20687  // private
20688     mozKeyPress : function(e){
20689         if(e.ctrlKey){
20690             var c = e.getCharCode(), cmd;
20691           
20692             if(c > 0){
20693                 c = String.fromCharCode(c).toLowerCase();
20694                 switch(c){
20695                     case 'b':
20696                         cmd = 'bold';
20697                         break;
20698                     case 'i':
20699                         cmd = 'italic';
20700                         break;
20701                     
20702                     case 'u':
20703                         cmd = 'underline';
20704                         break;
20705                     
20706                     case 'v':
20707                         this.cleanUpPaste.defer(100, this);
20708                         return;
20709                         
20710                 }
20711                 if(cmd){
20712                     this.win.focus();
20713                     this.execCmd(cmd);
20714                     this.deferFocus();
20715                     e.preventDefault();
20716                 }
20717                 
20718             }
20719         }
20720     },
20721
20722     // private
20723     fixKeys : function(){ // load time branching for fastest keydown performance
20724         if(Roo.isIE){
20725             return function(e){
20726                 var k = e.getKey(), r;
20727                 if(k == e.TAB){
20728                     e.stopEvent();
20729                     r = this.doc.selection.createRange();
20730                     if(r){
20731                         r.collapse(true);
20732                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20733                         this.deferFocus();
20734                     }
20735                     return;
20736                 }
20737                 
20738                 if(k == e.ENTER){
20739                     r = this.doc.selection.createRange();
20740                     if(r){
20741                         var target = r.parentElement();
20742                         if(!target || target.tagName.toLowerCase() != 'li'){
20743                             e.stopEvent();
20744                             r.pasteHTML('<br />');
20745                             r.collapse(false);
20746                             r.select();
20747                         }
20748                     }
20749                 }
20750                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20751                     this.cleanUpPaste.defer(100, this);
20752                     return;
20753                 }
20754                 
20755                 
20756             };
20757         }else if(Roo.isOpera){
20758             return function(e){
20759                 var k = e.getKey();
20760                 if(k == e.TAB){
20761                     e.stopEvent();
20762                     this.win.focus();
20763                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20764                     this.deferFocus();
20765                 }
20766                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20767                     this.cleanUpPaste.defer(100, this);
20768                     return;
20769                 }
20770                 
20771             };
20772         }else if(Roo.isSafari){
20773             return function(e){
20774                 var k = e.getKey();
20775                 
20776                 if(k == e.TAB){
20777                     e.stopEvent();
20778                     this.execCmd('InsertText','\t');
20779                     this.deferFocus();
20780                     return;
20781                 }
20782                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20783                     this.cleanUpPaste.defer(100, this);
20784                     return;
20785                 }
20786                 
20787              };
20788         }
20789     }(),
20790     
20791     getAllAncestors: function()
20792     {
20793         var p = this.getSelectedNode();
20794         var a = [];
20795         if (!p) {
20796             a.push(p); // push blank onto stack..
20797             p = this.getParentElement();
20798         }
20799         
20800         
20801         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20802             a.push(p);
20803             p = p.parentNode;
20804         }
20805         a.push(this.doc.body);
20806         return a;
20807     },
20808     lastSel : false,
20809     lastSelNode : false,
20810     
20811     
20812     getSelection : function() 
20813     {
20814         this.assignDocWin();
20815         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20816     },
20817     
20818     getSelectedNode: function() 
20819     {
20820         // this may only work on Gecko!!!
20821         
20822         // should we cache this!!!!
20823         
20824         
20825         
20826          
20827         var range = this.createRange(this.getSelection()).cloneRange();
20828         
20829         if (Roo.isIE) {
20830             var parent = range.parentElement();
20831             while (true) {
20832                 var testRange = range.duplicate();
20833                 testRange.moveToElementText(parent);
20834                 if (testRange.inRange(range)) {
20835                     break;
20836                 }
20837                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20838                     break;
20839                 }
20840                 parent = parent.parentElement;
20841             }
20842             return parent;
20843         }
20844         
20845         // is ancestor a text element.
20846         var ac =  range.commonAncestorContainer;
20847         if (ac.nodeType == 3) {
20848             ac = ac.parentNode;
20849         }
20850         
20851         var ar = ac.childNodes;
20852          
20853         var nodes = [];
20854         var other_nodes = [];
20855         var has_other_nodes = false;
20856         for (var i=0;i<ar.length;i++) {
20857             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20858                 continue;
20859             }
20860             // fullly contained node.
20861             
20862             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20863                 nodes.push(ar[i]);
20864                 continue;
20865             }
20866             
20867             // probably selected..
20868             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20869                 other_nodes.push(ar[i]);
20870                 continue;
20871             }
20872             // outer..
20873             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20874                 continue;
20875             }
20876             
20877             
20878             has_other_nodes = true;
20879         }
20880         if (!nodes.length && other_nodes.length) {
20881             nodes= other_nodes;
20882         }
20883         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20884             return false;
20885         }
20886         
20887         return nodes[0];
20888     },
20889     createRange: function(sel)
20890     {
20891         // this has strange effects when using with 
20892         // top toolbar - not sure if it's a great idea.
20893         //this.editor.contentWindow.focus();
20894         if (typeof sel != "undefined") {
20895             try {
20896                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20897             } catch(e) {
20898                 return this.doc.createRange();
20899             }
20900         } else {
20901             return this.doc.createRange();
20902         }
20903     },
20904     getParentElement: function()
20905     {
20906         
20907         this.assignDocWin();
20908         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20909         
20910         var range = this.createRange(sel);
20911          
20912         try {
20913             var p = range.commonAncestorContainer;
20914             while (p.nodeType == 3) { // text node
20915                 p = p.parentNode;
20916             }
20917             return p;
20918         } catch (e) {
20919             return null;
20920         }
20921     
20922     },
20923     /***
20924      *
20925      * Range intersection.. the hard stuff...
20926      *  '-1' = before
20927      *  '0' = hits..
20928      *  '1' = after.
20929      *         [ -- selected range --- ]
20930      *   [fail]                        [fail]
20931      *
20932      *    basically..
20933      *      if end is before start or  hits it. fail.
20934      *      if start is after end or hits it fail.
20935      *
20936      *   if either hits (but other is outside. - then it's not 
20937      *   
20938      *    
20939      **/
20940     
20941     
20942     // @see http://www.thismuchiknow.co.uk/?p=64.
20943     rangeIntersectsNode : function(range, node)
20944     {
20945         var nodeRange = node.ownerDocument.createRange();
20946         try {
20947             nodeRange.selectNode(node);
20948         } catch (e) {
20949             nodeRange.selectNodeContents(node);
20950         }
20951     
20952         var rangeStartRange = range.cloneRange();
20953         rangeStartRange.collapse(true);
20954     
20955         var rangeEndRange = range.cloneRange();
20956         rangeEndRange.collapse(false);
20957     
20958         var nodeStartRange = nodeRange.cloneRange();
20959         nodeStartRange.collapse(true);
20960     
20961         var nodeEndRange = nodeRange.cloneRange();
20962         nodeEndRange.collapse(false);
20963     
20964         return rangeStartRange.compareBoundaryPoints(
20965                  Range.START_TO_START, nodeEndRange) == -1 &&
20966                rangeEndRange.compareBoundaryPoints(
20967                  Range.START_TO_START, nodeStartRange) == 1;
20968         
20969          
20970     },
20971     rangeCompareNode : function(range, node)
20972     {
20973         var nodeRange = node.ownerDocument.createRange();
20974         try {
20975             nodeRange.selectNode(node);
20976         } catch (e) {
20977             nodeRange.selectNodeContents(node);
20978         }
20979         
20980         
20981         range.collapse(true);
20982     
20983         nodeRange.collapse(true);
20984      
20985         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20986         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20987          
20988         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20989         
20990         var nodeIsBefore   =  ss == 1;
20991         var nodeIsAfter    = ee == -1;
20992         
20993         if (nodeIsBefore && nodeIsAfter) {
20994             return 0; // outer
20995         }
20996         if (!nodeIsBefore && nodeIsAfter) {
20997             return 1; //right trailed.
20998         }
20999         
21000         if (nodeIsBefore && !nodeIsAfter) {
21001             return 2;  // left trailed.
21002         }
21003         // fully contined.
21004         return 3;
21005     },
21006
21007     // private? - in a new class?
21008     cleanUpPaste :  function()
21009     {
21010         // cleans up the whole document..
21011         Roo.log('cleanuppaste');
21012         
21013         this.cleanUpChildren(this.doc.body);
21014         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21015         if (clean != this.doc.body.innerHTML) {
21016             this.doc.body.innerHTML = clean;
21017         }
21018         
21019     },
21020     
21021     cleanWordChars : function(input) {// change the chars to hex code
21022         var he = Roo.HtmlEditorCore;
21023         
21024         var output = input;
21025         Roo.each(he.swapCodes, function(sw) { 
21026             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21027             
21028             output = output.replace(swapper, sw[1]);
21029         });
21030         
21031         return output;
21032     },
21033     
21034     
21035     cleanUpChildren : function (n)
21036     {
21037         if (!n.childNodes.length) {
21038             return;
21039         }
21040         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21041            this.cleanUpChild(n.childNodes[i]);
21042         }
21043     },
21044     
21045     
21046         
21047     
21048     cleanUpChild : function (node)
21049     {
21050         var ed = this;
21051         //console.log(node);
21052         if (node.nodeName == "#text") {
21053             // clean up silly Windows -- stuff?
21054             return; 
21055         }
21056         if (node.nodeName == "#comment") {
21057             node.parentNode.removeChild(node);
21058             // clean up silly Windows -- stuff?
21059             return; 
21060         }
21061         var lcname = node.tagName.toLowerCase();
21062         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21063         // whitelist of tags..
21064         
21065         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21066             // remove node.
21067             node.parentNode.removeChild(node);
21068             return;
21069             
21070         }
21071         
21072         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21073         
21074         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21075         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21076         
21077         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21078         //    remove_keep_children = true;
21079         //}
21080         
21081         if (remove_keep_children) {
21082             this.cleanUpChildren(node);
21083             // inserts everything just before this node...
21084             while (node.childNodes.length) {
21085                 var cn = node.childNodes[0];
21086                 node.removeChild(cn);
21087                 node.parentNode.insertBefore(cn, node);
21088             }
21089             node.parentNode.removeChild(node);
21090             return;
21091         }
21092         
21093         if (!node.attributes || !node.attributes.length) {
21094             this.cleanUpChildren(node);
21095             return;
21096         }
21097         
21098         function cleanAttr(n,v)
21099         {
21100             
21101             if (v.match(/^\./) || v.match(/^\//)) {
21102                 return;
21103             }
21104             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21105                 return;
21106             }
21107             if (v.match(/^#/)) {
21108                 return;
21109             }
21110 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21111             node.removeAttribute(n);
21112             
21113         }
21114         
21115         var cwhite = this.cwhite;
21116         var cblack = this.cblack;
21117             
21118         function cleanStyle(n,v)
21119         {
21120             if (v.match(/expression/)) { //XSS?? should we even bother..
21121                 node.removeAttribute(n);
21122                 return;
21123             }
21124             
21125             var parts = v.split(/;/);
21126             var clean = [];
21127             
21128             Roo.each(parts, function(p) {
21129                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21130                 if (!p.length) {
21131                     return true;
21132                 }
21133                 var l = p.split(':').shift().replace(/\s+/g,'');
21134                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21135                 
21136                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21137 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21138                     //node.removeAttribute(n);
21139                     return true;
21140                 }
21141                 //Roo.log()
21142                 // only allow 'c whitelisted system attributes'
21143                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21144 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21145                     //node.removeAttribute(n);
21146                     return true;
21147                 }
21148                 
21149                 
21150                  
21151                 
21152                 clean.push(p);
21153                 return true;
21154             });
21155             if (clean.length) { 
21156                 node.setAttribute(n, clean.join(';'));
21157             } else {
21158                 node.removeAttribute(n);
21159             }
21160             
21161         }
21162         
21163         
21164         for (var i = node.attributes.length-1; i > -1 ; i--) {
21165             var a = node.attributes[i];
21166             //console.log(a);
21167             
21168             if (a.name.toLowerCase().substr(0,2)=='on')  {
21169                 node.removeAttribute(a.name);
21170                 continue;
21171             }
21172             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21173                 node.removeAttribute(a.name);
21174                 continue;
21175             }
21176             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21177                 cleanAttr(a.name,a.value); // fixme..
21178                 continue;
21179             }
21180             if (a.name == 'style') {
21181                 cleanStyle(a.name,a.value);
21182                 continue;
21183             }
21184             /// clean up MS crap..
21185             // tecnically this should be a list of valid class'es..
21186             
21187             
21188             if (a.name == 'class') {
21189                 if (a.value.match(/^Mso/)) {
21190                     node.className = '';
21191                 }
21192                 
21193                 if (a.value.match(/body/)) {
21194                     node.className = '';
21195                 }
21196                 continue;
21197             }
21198             
21199             // style cleanup!?
21200             // class cleanup?
21201             
21202         }
21203         
21204         
21205         this.cleanUpChildren(node);
21206         
21207         
21208     },
21209     
21210     /**
21211      * Clean up MS wordisms...
21212      */
21213     cleanWord : function(node)
21214     {
21215         
21216         
21217         if (!node) {
21218             this.cleanWord(this.doc.body);
21219             return;
21220         }
21221         if (node.nodeName == "#text") {
21222             // clean up silly Windows -- stuff?
21223             return; 
21224         }
21225         if (node.nodeName == "#comment") {
21226             node.parentNode.removeChild(node);
21227             // clean up silly Windows -- stuff?
21228             return; 
21229         }
21230         
21231         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21232             node.parentNode.removeChild(node);
21233             return;
21234         }
21235         
21236         // remove - but keep children..
21237         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21238             while (node.childNodes.length) {
21239                 var cn = node.childNodes[0];
21240                 node.removeChild(cn);
21241                 node.parentNode.insertBefore(cn, node);
21242             }
21243             node.parentNode.removeChild(node);
21244             this.iterateChildren(node, this.cleanWord);
21245             return;
21246         }
21247         // clean styles
21248         if (node.className.length) {
21249             
21250             var cn = node.className.split(/\W+/);
21251             var cna = [];
21252             Roo.each(cn, function(cls) {
21253                 if (cls.match(/Mso[a-zA-Z]+/)) {
21254                     return;
21255                 }
21256                 cna.push(cls);
21257             });
21258             node.className = cna.length ? cna.join(' ') : '';
21259             if (!cna.length) {
21260                 node.removeAttribute("class");
21261             }
21262         }
21263         
21264         if (node.hasAttribute("lang")) {
21265             node.removeAttribute("lang");
21266         }
21267         
21268         if (node.hasAttribute("style")) {
21269             
21270             var styles = node.getAttribute("style").split(";");
21271             var nstyle = [];
21272             Roo.each(styles, function(s) {
21273                 if (!s.match(/:/)) {
21274                     return;
21275                 }
21276                 var kv = s.split(":");
21277                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21278                     return;
21279                 }
21280                 // what ever is left... we allow.
21281                 nstyle.push(s);
21282             });
21283             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21284             if (!nstyle.length) {
21285                 node.removeAttribute('style');
21286             }
21287         }
21288         this.iterateChildren(node, this.cleanWord);
21289         
21290         
21291         
21292     },
21293     /**
21294      * iterateChildren of a Node, calling fn each time, using this as the scole..
21295      * @param {DomNode} node node to iterate children of.
21296      * @param {Function} fn method of this class to call on each item.
21297      */
21298     iterateChildren : function(node, fn)
21299     {
21300         if (!node.childNodes.length) {
21301                 return;
21302         }
21303         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21304            fn.call(this, node.childNodes[i])
21305         }
21306     },
21307     
21308     
21309     /**
21310      * cleanTableWidths.
21311      *
21312      * Quite often pasting from word etc.. results in tables with column and widths.
21313      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21314      *
21315      */
21316     cleanTableWidths : function(node)
21317     {
21318          
21319          
21320         if (!node) {
21321             this.cleanTableWidths(this.doc.body);
21322             return;
21323         }
21324         
21325         // ignore list...
21326         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21327             return; 
21328         }
21329         Roo.log(node.tagName);
21330         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21331             this.iterateChildren(node, this.cleanTableWidths);
21332             return;
21333         }
21334         if (node.hasAttribute('width')) {
21335             node.removeAttribute('width');
21336         }
21337         
21338          
21339         if (node.hasAttribute("style")) {
21340             // pretty basic...
21341             
21342             var styles = node.getAttribute("style").split(";");
21343             var nstyle = [];
21344             Roo.each(styles, function(s) {
21345                 if (!s.match(/:/)) {
21346                     return;
21347                 }
21348                 var kv = s.split(":");
21349                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21350                     return;
21351                 }
21352                 // what ever is left... we allow.
21353                 nstyle.push(s);
21354             });
21355             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21356             if (!nstyle.length) {
21357                 node.removeAttribute('style');
21358             }
21359         }
21360         
21361         this.iterateChildren(node, this.cleanTableWidths);
21362         
21363         
21364     },
21365     
21366     
21367     
21368     
21369     domToHTML : function(currentElement, depth, nopadtext) {
21370         
21371         depth = depth || 0;
21372         nopadtext = nopadtext || false;
21373     
21374         if (!currentElement) {
21375             return this.domToHTML(this.doc.body);
21376         }
21377         
21378         //Roo.log(currentElement);
21379         var j;
21380         var allText = false;
21381         var nodeName = currentElement.nodeName;
21382         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21383         
21384         if  (nodeName == '#text') {
21385             
21386             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21387         }
21388         
21389         
21390         var ret = '';
21391         if (nodeName != 'BODY') {
21392              
21393             var i = 0;
21394             // Prints the node tagName, such as <A>, <IMG>, etc
21395             if (tagName) {
21396                 var attr = [];
21397                 for(i = 0; i < currentElement.attributes.length;i++) {
21398                     // quoting?
21399                     var aname = currentElement.attributes.item(i).name;
21400                     if (!currentElement.attributes.item(i).value.length) {
21401                         continue;
21402                     }
21403                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21404                 }
21405                 
21406                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21407             } 
21408             else {
21409                 
21410                 // eack
21411             }
21412         } else {
21413             tagName = false;
21414         }
21415         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21416             return ret;
21417         }
21418         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21419             nopadtext = true;
21420         }
21421         
21422         
21423         // Traverse the tree
21424         i = 0;
21425         var currentElementChild = currentElement.childNodes.item(i);
21426         var allText = true;
21427         var innerHTML  = '';
21428         lastnode = '';
21429         while (currentElementChild) {
21430             // Formatting code (indent the tree so it looks nice on the screen)
21431             var nopad = nopadtext;
21432             if (lastnode == 'SPAN') {
21433                 nopad  = true;
21434             }
21435             // text
21436             if  (currentElementChild.nodeName == '#text') {
21437                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21438                 toadd = nopadtext ? toadd : toadd.trim();
21439                 if (!nopad && toadd.length > 80) {
21440                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21441                 }
21442                 innerHTML  += toadd;
21443                 
21444                 i++;
21445                 currentElementChild = currentElement.childNodes.item(i);
21446                 lastNode = '';
21447                 continue;
21448             }
21449             allText = false;
21450             
21451             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21452                 
21453             // Recursively traverse the tree structure of the child node
21454             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21455             lastnode = currentElementChild.nodeName;
21456             i++;
21457             currentElementChild=currentElement.childNodes.item(i);
21458         }
21459         
21460         ret += innerHTML;
21461         
21462         if (!allText) {
21463                 // The remaining code is mostly for formatting the tree
21464             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21465         }
21466         
21467         
21468         if (tagName) {
21469             ret+= "</"+tagName+">";
21470         }
21471         return ret;
21472         
21473     },
21474         
21475     applyBlacklists : function()
21476     {
21477         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21478         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21479         
21480         this.white = [];
21481         this.black = [];
21482         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21483             if (b.indexOf(tag) > -1) {
21484                 return;
21485             }
21486             this.white.push(tag);
21487             
21488         }, this);
21489         
21490         Roo.each(w, function(tag) {
21491             if (b.indexOf(tag) > -1) {
21492                 return;
21493             }
21494             if (this.white.indexOf(tag) > -1) {
21495                 return;
21496             }
21497             this.white.push(tag);
21498             
21499         }, this);
21500         
21501         
21502         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21503             if (w.indexOf(tag) > -1) {
21504                 return;
21505             }
21506             this.black.push(tag);
21507             
21508         }, this);
21509         
21510         Roo.each(b, function(tag) {
21511             if (w.indexOf(tag) > -1) {
21512                 return;
21513             }
21514             if (this.black.indexOf(tag) > -1) {
21515                 return;
21516             }
21517             this.black.push(tag);
21518             
21519         }, this);
21520         
21521         
21522         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21523         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21524         
21525         this.cwhite = [];
21526         this.cblack = [];
21527         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21528             if (b.indexOf(tag) > -1) {
21529                 return;
21530             }
21531             this.cwhite.push(tag);
21532             
21533         }, this);
21534         
21535         Roo.each(w, function(tag) {
21536             if (b.indexOf(tag) > -1) {
21537                 return;
21538             }
21539             if (this.cwhite.indexOf(tag) > -1) {
21540                 return;
21541             }
21542             this.cwhite.push(tag);
21543             
21544         }, this);
21545         
21546         
21547         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21548             if (w.indexOf(tag) > -1) {
21549                 return;
21550             }
21551             this.cblack.push(tag);
21552             
21553         }, this);
21554         
21555         Roo.each(b, function(tag) {
21556             if (w.indexOf(tag) > -1) {
21557                 return;
21558             }
21559             if (this.cblack.indexOf(tag) > -1) {
21560                 return;
21561             }
21562             this.cblack.push(tag);
21563             
21564         }, this);
21565     },
21566     
21567     setStylesheets : function(stylesheets)
21568     {
21569         if(typeof(stylesheets) == 'string'){
21570             Roo.get(this.iframe.contentDocument.head).createChild({
21571                 tag : 'link',
21572                 rel : 'stylesheet',
21573                 type : 'text/css',
21574                 href : stylesheets
21575             });
21576             
21577             return;
21578         }
21579         var _this = this;
21580      
21581         Roo.each(stylesheets, function(s) {
21582             if(!s.length){
21583                 return;
21584             }
21585             
21586             Roo.get(_this.iframe.contentDocument.head).createChild({
21587                 tag : 'link',
21588                 rel : 'stylesheet',
21589                 type : 'text/css',
21590                 href : s
21591             });
21592         });
21593
21594         
21595     },
21596     
21597     removeStylesheets : function()
21598     {
21599         var _this = this;
21600         
21601         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21602             s.remove();
21603         });
21604     }
21605     
21606     // hide stuff that is not compatible
21607     /**
21608      * @event blur
21609      * @hide
21610      */
21611     /**
21612      * @event change
21613      * @hide
21614      */
21615     /**
21616      * @event focus
21617      * @hide
21618      */
21619     /**
21620      * @event specialkey
21621      * @hide
21622      */
21623     /**
21624      * @cfg {String} fieldClass @hide
21625      */
21626     /**
21627      * @cfg {String} focusClass @hide
21628      */
21629     /**
21630      * @cfg {String} autoCreate @hide
21631      */
21632     /**
21633      * @cfg {String} inputType @hide
21634      */
21635     /**
21636      * @cfg {String} invalidClass @hide
21637      */
21638     /**
21639      * @cfg {String} invalidText @hide
21640      */
21641     /**
21642      * @cfg {String} msgFx @hide
21643      */
21644     /**
21645      * @cfg {String} validateOnBlur @hide
21646      */
21647 });
21648
21649 Roo.HtmlEditorCore.white = [
21650         'area', 'br', 'img', 'input', 'hr', 'wbr',
21651         
21652        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21653        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21654        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21655        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21656        'table',   'ul',         'xmp', 
21657        
21658        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21659       'thead',   'tr', 
21660      
21661       'dir', 'menu', 'ol', 'ul', 'dl',
21662        
21663       'embed',  'object'
21664 ];
21665
21666
21667 Roo.HtmlEditorCore.black = [
21668     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21669         'applet', // 
21670         'base',   'basefont', 'bgsound', 'blink',  'body', 
21671         'frame',  'frameset', 'head',    'html',   'ilayer', 
21672         'iframe', 'layer',  'link',     'meta',    'object',   
21673         'script', 'style' ,'title',  'xml' // clean later..
21674 ];
21675 Roo.HtmlEditorCore.clean = [
21676     'script', 'style', 'title', 'xml'
21677 ];
21678 Roo.HtmlEditorCore.remove = [
21679     'font'
21680 ];
21681 // attributes..
21682
21683 Roo.HtmlEditorCore.ablack = [
21684     'on'
21685 ];
21686     
21687 Roo.HtmlEditorCore.aclean = [ 
21688     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21689 ];
21690
21691 // protocols..
21692 Roo.HtmlEditorCore.pwhite= [
21693         'http',  'https',  'mailto'
21694 ];
21695
21696 // white listed style attributes.
21697 Roo.HtmlEditorCore.cwhite= [
21698       //  'text-align', /// default is to allow most things..
21699       
21700          
21701 //        'font-size'//??
21702 ];
21703
21704 // black listed style attributes.
21705 Roo.HtmlEditorCore.cblack= [
21706       //  'font-size' -- this can be set by the project 
21707 ];
21708
21709
21710 Roo.HtmlEditorCore.swapCodes   =[ 
21711     [    8211, "--" ], 
21712     [    8212, "--" ], 
21713     [    8216,  "'" ],  
21714     [    8217, "'" ],  
21715     [    8220, '"' ],  
21716     [    8221, '"' ],  
21717     [    8226, "*" ],  
21718     [    8230, "..." ]
21719 ]; 
21720
21721     /*
21722  * - LGPL
21723  *
21724  * HtmlEditor
21725  * 
21726  */
21727
21728 /**
21729  * @class Roo.bootstrap.HtmlEditor
21730  * @extends Roo.bootstrap.TextArea
21731  * Bootstrap HtmlEditor class
21732
21733  * @constructor
21734  * Create a new HtmlEditor
21735  * @param {Object} config The config object
21736  */
21737
21738 Roo.bootstrap.HtmlEditor = function(config){
21739     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21740     if (!this.toolbars) {
21741         this.toolbars = [];
21742     }
21743     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21744     this.addEvents({
21745             /**
21746              * @event initialize
21747              * Fires when the editor is fully initialized (including the iframe)
21748              * @param {HtmlEditor} this
21749              */
21750             initialize: true,
21751             /**
21752              * @event activate
21753              * Fires when the editor is first receives the focus. Any insertion must wait
21754              * until after this event.
21755              * @param {HtmlEditor} this
21756              */
21757             activate: true,
21758              /**
21759              * @event beforesync
21760              * Fires before the textarea is updated with content from the editor iframe. Return false
21761              * to cancel the sync.
21762              * @param {HtmlEditor} this
21763              * @param {String} html
21764              */
21765             beforesync: true,
21766              /**
21767              * @event beforepush
21768              * Fires before the iframe editor is updated with content from the textarea. Return false
21769              * to cancel the push.
21770              * @param {HtmlEditor} this
21771              * @param {String} html
21772              */
21773             beforepush: true,
21774              /**
21775              * @event sync
21776              * Fires when the textarea is updated with content from the editor iframe.
21777              * @param {HtmlEditor} this
21778              * @param {String} html
21779              */
21780             sync: true,
21781              /**
21782              * @event push
21783              * Fires when the iframe editor is updated with content from the textarea.
21784              * @param {HtmlEditor} this
21785              * @param {String} html
21786              */
21787             push: true,
21788              /**
21789              * @event editmodechange
21790              * Fires when the editor switches edit modes
21791              * @param {HtmlEditor} this
21792              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21793              */
21794             editmodechange: true,
21795             /**
21796              * @event editorevent
21797              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21798              * @param {HtmlEditor} this
21799              */
21800             editorevent: true,
21801             /**
21802              * @event firstfocus
21803              * Fires when on first focus - needed by toolbars..
21804              * @param {HtmlEditor} this
21805              */
21806             firstfocus: true,
21807             /**
21808              * @event autosave
21809              * Auto save the htmlEditor value as a file into Events
21810              * @param {HtmlEditor} this
21811              */
21812             autosave: true,
21813             /**
21814              * @event savedpreview
21815              * preview the saved version of htmlEditor
21816              * @param {HtmlEditor} this
21817              */
21818             savedpreview: true
21819         });
21820 };
21821
21822
21823 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21824     
21825     
21826       /**
21827      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21828      */
21829     toolbars : false,
21830    
21831      /**
21832      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21833      *                        Roo.resizable.
21834      */
21835     resizable : false,
21836      /**
21837      * @cfg {Number} height (in pixels)
21838      */   
21839     height: 300,
21840    /**
21841      * @cfg {Number} width (in pixels)
21842      */   
21843     width: false,
21844     
21845     /**
21846      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21847      * 
21848      */
21849     stylesheets: false,
21850     
21851     // id of frame..
21852     frameId: false,
21853     
21854     // private properties
21855     validationEvent : false,
21856     deferHeight: true,
21857     initialized : false,
21858     activated : false,
21859     
21860     onFocus : Roo.emptyFn,
21861     iframePad:3,
21862     hideMode:'offsets',
21863     
21864     
21865     tbContainer : false,
21866     
21867     toolbarContainer :function() {
21868         return this.wrap.select('.x-html-editor-tb',true).first();
21869     },
21870
21871     /**
21872      * Protected method that will not generally be called directly. It
21873      * is called when the editor creates its toolbar. Override this method if you need to
21874      * add custom toolbar buttons.
21875      * @param {HtmlEditor} editor
21876      */
21877     createToolbar : function(){
21878         
21879         Roo.log("create toolbars");
21880         
21881         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21882         this.toolbars[0].render(this.toolbarContainer());
21883         
21884         return;
21885         
21886 //        if (!editor.toolbars || !editor.toolbars.length) {
21887 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21888 //        }
21889 //        
21890 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21891 //            editor.toolbars[i] = Roo.factory(
21892 //                    typeof(editor.toolbars[i]) == 'string' ?
21893 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21894 //                Roo.bootstrap.HtmlEditor);
21895 //            editor.toolbars[i].init(editor);
21896 //        }
21897     },
21898
21899      
21900     // private
21901     onRender : function(ct, position)
21902     {
21903        // Roo.log("Call onRender: " + this.xtype);
21904         var _t = this;
21905         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21906       
21907         this.wrap = this.inputEl().wrap({
21908             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21909         });
21910         
21911         this.editorcore.onRender(ct, position);
21912          
21913         if (this.resizable) {
21914             this.resizeEl = new Roo.Resizable(this.wrap, {
21915                 pinned : true,
21916                 wrap: true,
21917                 dynamic : true,
21918                 minHeight : this.height,
21919                 height: this.height,
21920                 handles : this.resizable,
21921                 width: this.width,
21922                 listeners : {
21923                     resize : function(r, w, h) {
21924                         _t.onResize(w,h); // -something
21925                     }
21926                 }
21927             });
21928             
21929         }
21930         this.createToolbar(this);
21931        
21932         
21933         if(!this.width && this.resizable){
21934             this.setSize(this.wrap.getSize());
21935         }
21936         if (this.resizeEl) {
21937             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21938             // should trigger onReize..
21939         }
21940         
21941     },
21942
21943     // private
21944     onResize : function(w, h)
21945     {
21946         Roo.log('resize: ' +w + ',' + h );
21947         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21948         var ew = false;
21949         var eh = false;
21950         
21951         if(this.inputEl() ){
21952             if(typeof w == 'number'){
21953                 var aw = w - this.wrap.getFrameWidth('lr');
21954                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21955                 ew = aw;
21956             }
21957             if(typeof h == 'number'){
21958                  var tbh = -11;  // fixme it needs to tool bar size!
21959                 for (var i =0; i < this.toolbars.length;i++) {
21960                     // fixme - ask toolbars for heights?
21961                     tbh += this.toolbars[i].el.getHeight();
21962                     //if (this.toolbars[i].footer) {
21963                     //    tbh += this.toolbars[i].footer.el.getHeight();
21964                     //}
21965                 }
21966               
21967                 
21968                 
21969                 
21970                 
21971                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21972                 ah -= 5; // knock a few pixes off for look..
21973                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21974                 var eh = ah;
21975             }
21976         }
21977         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21978         this.editorcore.onResize(ew,eh);
21979         
21980     },
21981
21982     /**
21983      * Toggles the editor between standard and source edit mode.
21984      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21985      */
21986     toggleSourceEdit : function(sourceEditMode)
21987     {
21988         this.editorcore.toggleSourceEdit(sourceEditMode);
21989         
21990         if(this.editorcore.sourceEditMode){
21991             Roo.log('editor - showing textarea');
21992             
21993 //            Roo.log('in');
21994 //            Roo.log(this.syncValue());
21995             this.syncValue();
21996             this.inputEl().removeClass(['hide', 'x-hidden']);
21997             this.inputEl().dom.removeAttribute('tabIndex');
21998             this.inputEl().focus();
21999         }else{
22000             Roo.log('editor - hiding textarea');
22001 //            Roo.log('out')
22002 //            Roo.log(this.pushValue()); 
22003             this.pushValue();
22004             
22005             this.inputEl().addClass(['hide', 'x-hidden']);
22006             this.inputEl().dom.setAttribute('tabIndex', -1);
22007             //this.deferFocus();
22008         }
22009          
22010         if(this.resizable){
22011             this.setSize(this.wrap.getSize());
22012         }
22013         
22014         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22015     },
22016  
22017     // private (for BoxComponent)
22018     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22019
22020     // private (for BoxComponent)
22021     getResizeEl : function(){
22022         return this.wrap;
22023     },
22024
22025     // private (for BoxComponent)
22026     getPositionEl : function(){
22027         return this.wrap;
22028     },
22029
22030     // private
22031     initEvents : function(){
22032         this.originalValue = this.getValue();
22033     },
22034
22035 //    /**
22036 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22037 //     * @method
22038 //     */
22039 //    markInvalid : Roo.emptyFn,
22040 //    /**
22041 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22042 //     * @method
22043 //     */
22044 //    clearInvalid : Roo.emptyFn,
22045
22046     setValue : function(v){
22047         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22048         this.editorcore.pushValue();
22049     },
22050
22051      
22052     // private
22053     deferFocus : function(){
22054         this.focus.defer(10, this);
22055     },
22056
22057     // doc'ed in Field
22058     focus : function(){
22059         this.editorcore.focus();
22060         
22061     },
22062       
22063
22064     // private
22065     onDestroy : function(){
22066         
22067         
22068         
22069         if(this.rendered){
22070             
22071             for (var i =0; i < this.toolbars.length;i++) {
22072                 // fixme - ask toolbars for heights?
22073                 this.toolbars[i].onDestroy();
22074             }
22075             
22076             this.wrap.dom.innerHTML = '';
22077             this.wrap.remove();
22078         }
22079     },
22080
22081     // private
22082     onFirstFocus : function(){
22083         //Roo.log("onFirstFocus");
22084         this.editorcore.onFirstFocus();
22085          for (var i =0; i < this.toolbars.length;i++) {
22086             this.toolbars[i].onFirstFocus();
22087         }
22088         
22089     },
22090     
22091     // private
22092     syncValue : function()
22093     {   
22094         this.editorcore.syncValue();
22095     },
22096     
22097     pushValue : function()
22098     {   
22099         this.editorcore.pushValue();
22100     }
22101      
22102     
22103     // hide stuff that is not compatible
22104     /**
22105      * @event blur
22106      * @hide
22107      */
22108     /**
22109      * @event change
22110      * @hide
22111      */
22112     /**
22113      * @event focus
22114      * @hide
22115      */
22116     /**
22117      * @event specialkey
22118      * @hide
22119      */
22120     /**
22121      * @cfg {String} fieldClass @hide
22122      */
22123     /**
22124      * @cfg {String} focusClass @hide
22125      */
22126     /**
22127      * @cfg {String} autoCreate @hide
22128      */
22129     /**
22130      * @cfg {String} inputType @hide
22131      */
22132     /**
22133      * @cfg {String} invalidClass @hide
22134      */
22135     /**
22136      * @cfg {String} invalidText @hide
22137      */
22138     /**
22139      * @cfg {String} msgFx @hide
22140      */
22141     /**
22142      * @cfg {String} validateOnBlur @hide
22143      */
22144 });
22145  
22146     
22147    
22148    
22149    
22150       
22151 Roo.namespace('Roo.bootstrap.htmleditor');
22152 /**
22153  * @class Roo.bootstrap.HtmlEditorToolbar1
22154  * Basic Toolbar
22155  * 
22156  * Usage:
22157  *
22158  new Roo.bootstrap.HtmlEditor({
22159     ....
22160     toolbars : [
22161         new Roo.bootstrap.HtmlEditorToolbar1({
22162             disable : { fonts: 1 , format: 1, ..., ... , ...],
22163             btns : [ .... ]
22164         })
22165     }
22166      
22167  * 
22168  * @cfg {Object} disable List of elements to disable..
22169  * @cfg {Array} btns List of additional buttons.
22170  * 
22171  * 
22172  * NEEDS Extra CSS? 
22173  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22174  */
22175  
22176 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22177 {
22178     
22179     Roo.apply(this, config);
22180     
22181     // default disabled, based on 'good practice'..
22182     this.disable = this.disable || {};
22183     Roo.applyIf(this.disable, {
22184         fontSize : true,
22185         colors : true,
22186         specialElements : true
22187     });
22188     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22189     
22190     this.editor = config.editor;
22191     this.editorcore = config.editor.editorcore;
22192     
22193     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22194     
22195     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22196     // dont call parent... till later.
22197 }
22198 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22199      
22200     bar : true,
22201     
22202     editor : false,
22203     editorcore : false,
22204     
22205     
22206     formats : [
22207         "p" ,  
22208         "h1","h2","h3","h4","h5","h6", 
22209         "pre", "code", 
22210         "abbr", "acronym", "address", "cite", "samp", "var",
22211         'div','span'
22212     ],
22213     
22214     onRender : function(ct, position)
22215     {
22216        // Roo.log("Call onRender: " + this.xtype);
22217         
22218        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22219        Roo.log(this.el);
22220        this.el.dom.style.marginBottom = '0';
22221        var _this = this;
22222        var editorcore = this.editorcore;
22223        var editor= this.editor;
22224        
22225        var children = [];
22226        var btn = function(id,cmd , toggle, handler){
22227        
22228             var  event = toggle ? 'toggle' : 'click';
22229        
22230             var a = {
22231                 size : 'sm',
22232                 xtype: 'Button',
22233                 xns: Roo.bootstrap,
22234                 glyphicon : id,
22235                 cmd : id || cmd,
22236                 enableToggle:toggle !== false,
22237                 //html : 'submit'
22238                 pressed : toggle ? false : null,
22239                 listeners : {}
22240             };
22241             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22242                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22243             };
22244             children.push(a);
22245             return a;
22246        }
22247         
22248         var style = {
22249                 xtype: 'Button',
22250                 size : 'sm',
22251                 xns: Roo.bootstrap,
22252                 glyphicon : 'font',
22253                 //html : 'submit'
22254                 menu : {
22255                     xtype: 'Menu',
22256                     xns: Roo.bootstrap,
22257                     items:  []
22258                 }
22259         };
22260         Roo.each(this.formats, function(f) {
22261             style.menu.items.push({
22262                 xtype :'MenuItem',
22263                 xns: Roo.bootstrap,
22264                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22265                 tagname : f,
22266                 listeners : {
22267                     click : function()
22268                     {
22269                         editorcore.insertTag(this.tagname);
22270                         editor.focus();
22271                     }
22272                 }
22273                 
22274             });
22275         });
22276          children.push(style);   
22277             
22278             
22279         btn('bold',false,true);
22280         btn('italic',false,true);
22281         btn('align-left', 'justifyleft',true);
22282         btn('align-center', 'justifycenter',true);
22283         btn('align-right' , 'justifyright',true);
22284         btn('link', false, false, function(btn) {
22285             //Roo.log("create link?");
22286             var url = prompt(this.createLinkText, this.defaultLinkValue);
22287             if(url && url != 'http:/'+'/'){
22288                 this.editorcore.relayCmd('createlink', url);
22289             }
22290         }),
22291         btn('list','insertunorderedlist',true);
22292         btn('pencil', false,true, function(btn){
22293                 Roo.log(this);
22294                 
22295                 this.toggleSourceEdit(btn.pressed);
22296         });
22297         /*
22298         var cog = {
22299                 xtype: 'Button',
22300                 size : 'sm',
22301                 xns: Roo.bootstrap,
22302                 glyphicon : 'cog',
22303                 //html : 'submit'
22304                 menu : {
22305                     xtype: 'Menu',
22306                     xns: Roo.bootstrap,
22307                     items:  []
22308                 }
22309         };
22310         
22311         cog.menu.items.push({
22312             xtype :'MenuItem',
22313             xns: Roo.bootstrap,
22314             html : Clean styles,
22315             tagname : f,
22316             listeners : {
22317                 click : function()
22318                 {
22319                     editorcore.insertTag(this.tagname);
22320                     editor.focus();
22321                 }
22322             }
22323             
22324         });
22325        */
22326         
22327          
22328        this.xtype = 'NavSimplebar';
22329         
22330         for(var i=0;i< children.length;i++) {
22331             
22332             this.buttons.add(this.addxtypeChild(children[i]));
22333             
22334         }
22335         
22336         editor.on('editorevent', this.updateToolbar, this);
22337     },
22338     onBtnClick : function(id)
22339     {
22340        this.editorcore.relayCmd(id);
22341        this.editorcore.focus();
22342     },
22343     
22344     /**
22345      * Protected method that will not generally be called directly. It triggers
22346      * a toolbar update by reading the markup state of the current selection in the editor.
22347      */
22348     updateToolbar: function(){
22349
22350         if(!this.editorcore.activated){
22351             this.editor.onFirstFocus(); // is this neeed?
22352             return;
22353         }
22354
22355         var btns = this.buttons; 
22356         var doc = this.editorcore.doc;
22357         btns.get('bold').setActive(doc.queryCommandState('bold'));
22358         btns.get('italic').setActive(doc.queryCommandState('italic'));
22359         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22360         
22361         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22362         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22363         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22364         
22365         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22366         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22367          /*
22368         
22369         var ans = this.editorcore.getAllAncestors();
22370         if (this.formatCombo) {
22371             
22372             
22373             var store = this.formatCombo.store;
22374             this.formatCombo.setValue("");
22375             for (var i =0; i < ans.length;i++) {
22376                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22377                     // select it..
22378                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22379                     break;
22380                 }
22381             }
22382         }
22383         
22384         
22385         
22386         // hides menus... - so this cant be on a menu...
22387         Roo.bootstrap.MenuMgr.hideAll();
22388         */
22389         Roo.bootstrap.MenuMgr.hideAll();
22390         //this.editorsyncValue();
22391     },
22392     onFirstFocus: function() {
22393         this.buttons.each(function(item){
22394            item.enable();
22395         });
22396     },
22397     toggleSourceEdit : function(sourceEditMode){
22398         
22399           
22400         if(sourceEditMode){
22401             Roo.log("disabling buttons");
22402            this.buttons.each( function(item){
22403                 if(item.cmd != 'pencil'){
22404                     item.disable();
22405                 }
22406             });
22407           
22408         }else{
22409             Roo.log("enabling buttons");
22410             if(this.editorcore.initialized){
22411                 this.buttons.each( function(item){
22412                     item.enable();
22413                 });
22414             }
22415             
22416         }
22417         Roo.log("calling toggole on editor");
22418         // tell the editor that it's been pressed..
22419         this.editor.toggleSourceEdit(sourceEditMode);
22420        
22421     }
22422 });
22423
22424
22425
22426
22427
22428 /**
22429  * @class Roo.bootstrap.Table.AbstractSelectionModel
22430  * @extends Roo.util.Observable
22431  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22432  * implemented by descendant classes.  This class should not be directly instantiated.
22433  * @constructor
22434  */
22435 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22436     this.locked = false;
22437     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22438 };
22439
22440
22441 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22442     /** @ignore Called by the grid automatically. Do not call directly. */
22443     init : function(grid){
22444         this.grid = grid;
22445         this.initEvents();
22446     },
22447
22448     /**
22449      * Locks the selections.
22450      */
22451     lock : function(){
22452         this.locked = true;
22453     },
22454
22455     /**
22456      * Unlocks the selections.
22457      */
22458     unlock : function(){
22459         this.locked = false;
22460     },
22461
22462     /**
22463      * Returns true if the selections are locked.
22464      * @return {Boolean}
22465      */
22466     isLocked : function(){
22467         return this.locked;
22468     }
22469 });
22470 /**
22471  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22472  * @class Roo.bootstrap.Table.RowSelectionModel
22473  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22474  * It supports multiple selections and keyboard selection/navigation. 
22475  * @constructor
22476  * @param {Object} config
22477  */
22478
22479 Roo.bootstrap.Table.RowSelectionModel = function(config){
22480     Roo.apply(this, config);
22481     this.selections = new Roo.util.MixedCollection(false, function(o){
22482         return o.id;
22483     });
22484
22485     this.last = false;
22486     this.lastActive = false;
22487
22488     this.addEvents({
22489         /**
22490              * @event selectionchange
22491              * Fires when the selection changes
22492              * @param {SelectionModel} this
22493              */
22494             "selectionchange" : true,
22495         /**
22496              * @event afterselectionchange
22497              * Fires after the selection changes (eg. by key press or clicking)
22498              * @param {SelectionModel} this
22499              */
22500             "afterselectionchange" : true,
22501         /**
22502              * @event beforerowselect
22503              * Fires when a row is selected being selected, return false to cancel.
22504              * @param {SelectionModel} this
22505              * @param {Number} rowIndex The selected index
22506              * @param {Boolean} keepExisting False if other selections will be cleared
22507              */
22508             "beforerowselect" : true,
22509         /**
22510              * @event rowselect
22511              * Fires when a row is selected.
22512              * @param {SelectionModel} this
22513              * @param {Number} rowIndex The selected index
22514              * @param {Roo.data.Record} r The record
22515              */
22516             "rowselect" : true,
22517         /**
22518              * @event rowdeselect
22519              * Fires when a row is deselected.
22520              * @param {SelectionModel} this
22521              * @param {Number} rowIndex The selected index
22522              */
22523         "rowdeselect" : true
22524     });
22525     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22526     this.locked = false;
22527  };
22528
22529 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22530     /**
22531      * @cfg {Boolean} singleSelect
22532      * True to allow selection of only one row at a time (defaults to false)
22533      */
22534     singleSelect : false,
22535
22536     // private
22537     initEvents : function()
22538     {
22539
22540         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22541         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22542         //}else{ // allow click to work like normal
22543          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22544         //}
22545         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22546         this.grid.on("rowclick", this.handleMouseDown, this);
22547         
22548         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22549             "up" : function(e){
22550                 if(!e.shiftKey){
22551                     this.selectPrevious(e.shiftKey);
22552                 }else if(this.last !== false && this.lastActive !== false){
22553                     var last = this.last;
22554                     this.selectRange(this.last,  this.lastActive-1);
22555                     this.grid.getView().focusRow(this.lastActive);
22556                     if(last !== false){
22557                         this.last = last;
22558                     }
22559                 }else{
22560                     this.selectFirstRow();
22561                 }
22562                 this.fireEvent("afterselectionchange", this);
22563             },
22564             "down" : function(e){
22565                 if(!e.shiftKey){
22566                     this.selectNext(e.shiftKey);
22567                 }else if(this.last !== false && this.lastActive !== false){
22568                     var last = this.last;
22569                     this.selectRange(this.last,  this.lastActive+1);
22570                     this.grid.getView().focusRow(this.lastActive);
22571                     if(last !== false){
22572                         this.last = last;
22573                     }
22574                 }else{
22575                     this.selectFirstRow();
22576                 }
22577                 this.fireEvent("afterselectionchange", this);
22578             },
22579             scope: this
22580         });
22581         this.grid.store.on('load', function(){
22582             this.selections.clear();
22583         },this);
22584         /*
22585         var view = this.grid.view;
22586         view.on("refresh", this.onRefresh, this);
22587         view.on("rowupdated", this.onRowUpdated, this);
22588         view.on("rowremoved", this.onRemove, this);
22589         */
22590     },
22591
22592     // private
22593     onRefresh : function()
22594     {
22595         var ds = this.grid.store, i, v = this.grid.view;
22596         var s = this.selections;
22597         s.each(function(r){
22598             if((i = ds.indexOfId(r.id)) != -1){
22599                 v.onRowSelect(i);
22600             }else{
22601                 s.remove(r);
22602             }
22603         });
22604     },
22605
22606     // private
22607     onRemove : function(v, index, r){
22608         this.selections.remove(r);
22609     },
22610
22611     // private
22612     onRowUpdated : function(v, index, r){
22613         if(this.isSelected(r)){
22614             v.onRowSelect(index);
22615         }
22616     },
22617
22618     /**
22619      * Select records.
22620      * @param {Array} records The records to select
22621      * @param {Boolean} keepExisting (optional) True to keep existing selections
22622      */
22623     selectRecords : function(records, keepExisting)
22624     {
22625         if(!keepExisting){
22626             this.clearSelections();
22627         }
22628             var ds = this.grid.store;
22629         for(var i = 0, len = records.length; i < len; i++){
22630             this.selectRow(ds.indexOf(records[i]), true);
22631         }
22632     },
22633
22634     /**
22635      * Gets the number of selected rows.
22636      * @return {Number}
22637      */
22638     getCount : function(){
22639         return this.selections.length;
22640     },
22641
22642     /**
22643      * Selects the first row in the grid.
22644      */
22645     selectFirstRow : function(){
22646         this.selectRow(0);
22647     },
22648
22649     /**
22650      * Select the last row.
22651      * @param {Boolean} keepExisting (optional) True to keep existing selections
22652      */
22653     selectLastRow : function(keepExisting){
22654         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22655         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22656     },
22657
22658     /**
22659      * Selects the row immediately following the last selected row.
22660      * @param {Boolean} keepExisting (optional) True to keep existing selections
22661      */
22662     selectNext : function(keepExisting)
22663     {
22664             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22665             this.selectRow(this.last+1, keepExisting);
22666             this.grid.getView().focusRow(this.last);
22667         }
22668     },
22669
22670     /**
22671      * Selects the row that precedes the last selected row.
22672      * @param {Boolean} keepExisting (optional) True to keep existing selections
22673      */
22674     selectPrevious : function(keepExisting){
22675         if(this.last){
22676             this.selectRow(this.last-1, keepExisting);
22677             this.grid.getView().focusRow(this.last);
22678         }
22679     },
22680
22681     /**
22682      * Returns the selected records
22683      * @return {Array} Array of selected records
22684      */
22685     getSelections : function(){
22686         return [].concat(this.selections.items);
22687     },
22688
22689     /**
22690      * Returns the first selected record.
22691      * @return {Record}
22692      */
22693     getSelected : function(){
22694         return this.selections.itemAt(0);
22695     },
22696
22697
22698     /**
22699      * Clears all selections.
22700      */
22701     clearSelections : function(fast)
22702     {
22703         if(this.locked) {
22704             return;
22705         }
22706         if(fast !== true){
22707                 var ds = this.grid.store;
22708             var s = this.selections;
22709             s.each(function(r){
22710                 this.deselectRow(ds.indexOfId(r.id));
22711             }, this);
22712             s.clear();
22713         }else{
22714             this.selections.clear();
22715         }
22716         this.last = false;
22717     },
22718
22719
22720     /**
22721      * Selects all rows.
22722      */
22723     selectAll : function(){
22724         if(this.locked) {
22725             return;
22726         }
22727         this.selections.clear();
22728         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22729             this.selectRow(i, true);
22730         }
22731     },
22732
22733     /**
22734      * Returns True if there is a selection.
22735      * @return {Boolean}
22736      */
22737     hasSelection : function(){
22738         return this.selections.length > 0;
22739     },
22740
22741     /**
22742      * Returns True if the specified row is selected.
22743      * @param {Number/Record} record The record or index of the record to check
22744      * @return {Boolean}
22745      */
22746     isSelected : function(index){
22747             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22748         return (r && this.selections.key(r.id) ? true : false);
22749     },
22750
22751     /**
22752      * Returns True if the specified record id is selected.
22753      * @param {String} id The id of record to check
22754      * @return {Boolean}
22755      */
22756     isIdSelected : function(id){
22757         return (this.selections.key(id) ? true : false);
22758     },
22759
22760
22761     // private
22762     handleMouseDBClick : function(e, t){
22763         
22764     },
22765     // private
22766     handleMouseDown : function(e, t)
22767     {
22768             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22769         if(this.isLocked() || rowIndex < 0 ){
22770             return;
22771         };
22772         if(e.shiftKey && this.last !== false){
22773             var last = this.last;
22774             this.selectRange(last, rowIndex, e.ctrlKey);
22775             this.last = last; // reset the last
22776             t.focus();
22777     
22778         }else{
22779             var isSelected = this.isSelected(rowIndex);
22780             //Roo.log("select row:" + rowIndex);
22781             if(isSelected){
22782                 this.deselectRow(rowIndex);
22783             } else {
22784                         this.selectRow(rowIndex, true);
22785             }
22786     
22787             /*
22788                 if(e.button !== 0 && isSelected){
22789                 alert('rowIndex 2: ' + rowIndex);
22790                     view.focusRow(rowIndex);
22791                 }else if(e.ctrlKey && isSelected){
22792                     this.deselectRow(rowIndex);
22793                 }else if(!isSelected){
22794                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22795                     view.focusRow(rowIndex);
22796                 }
22797             */
22798         }
22799         this.fireEvent("afterselectionchange", this);
22800     },
22801     // private
22802     handleDragableRowClick :  function(grid, rowIndex, e) 
22803     {
22804         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22805             this.selectRow(rowIndex, false);
22806             grid.view.focusRow(rowIndex);
22807              this.fireEvent("afterselectionchange", this);
22808         }
22809     },
22810     
22811     /**
22812      * Selects multiple rows.
22813      * @param {Array} rows Array of the indexes of the row to select
22814      * @param {Boolean} keepExisting (optional) True to keep existing selections
22815      */
22816     selectRows : function(rows, keepExisting){
22817         if(!keepExisting){
22818             this.clearSelections();
22819         }
22820         for(var i = 0, len = rows.length; i < len; i++){
22821             this.selectRow(rows[i], true);
22822         }
22823     },
22824
22825     /**
22826      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22827      * @param {Number} startRow The index of the first row in the range
22828      * @param {Number} endRow The index of the last row in the range
22829      * @param {Boolean} keepExisting (optional) True to retain existing selections
22830      */
22831     selectRange : function(startRow, endRow, keepExisting){
22832         if(this.locked) {
22833             return;
22834         }
22835         if(!keepExisting){
22836             this.clearSelections();
22837         }
22838         if(startRow <= endRow){
22839             for(var i = startRow; i <= endRow; i++){
22840                 this.selectRow(i, true);
22841             }
22842         }else{
22843             for(var i = startRow; i >= endRow; i--){
22844                 this.selectRow(i, true);
22845             }
22846         }
22847     },
22848
22849     /**
22850      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22851      * @param {Number} startRow The index of the first row in the range
22852      * @param {Number} endRow The index of the last row in the range
22853      */
22854     deselectRange : function(startRow, endRow, preventViewNotify){
22855         if(this.locked) {
22856             return;
22857         }
22858         for(var i = startRow; i <= endRow; i++){
22859             this.deselectRow(i, preventViewNotify);
22860         }
22861     },
22862
22863     /**
22864      * Selects a row.
22865      * @param {Number} row The index of the row to select
22866      * @param {Boolean} keepExisting (optional) True to keep existing selections
22867      */
22868     selectRow : function(index, keepExisting, preventViewNotify)
22869     {
22870             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
22871             return;
22872         }
22873         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22874             if(!keepExisting || this.singleSelect){
22875                 this.clearSelections();
22876             }
22877             
22878             var r = this.grid.store.getAt(index);
22879             //console.log('selectRow - record id :' + r.id);
22880             
22881             this.selections.add(r);
22882             this.last = this.lastActive = index;
22883             if(!preventViewNotify){
22884                 var proxy = new Roo.Element(
22885                                 this.grid.getRowDom(index)
22886                 );
22887                 proxy.addClass('bg-info info');
22888             }
22889             this.fireEvent("rowselect", this, index, r);
22890             this.fireEvent("selectionchange", this);
22891         }
22892     },
22893
22894     /**
22895      * Deselects a row.
22896      * @param {Number} row The index of the row to deselect
22897      */
22898     deselectRow : function(index, preventViewNotify)
22899     {
22900         if(this.locked) {
22901             return;
22902         }
22903         if(this.last == index){
22904             this.last = false;
22905         }
22906         if(this.lastActive == index){
22907             this.lastActive = false;
22908         }
22909         
22910         var r = this.grid.store.getAt(index);
22911         if (!r) {
22912             return;
22913         }
22914         
22915         this.selections.remove(r);
22916         //.console.log('deselectRow - record id :' + r.id);
22917         if(!preventViewNotify){
22918         
22919             var proxy = new Roo.Element(
22920                 this.grid.getRowDom(index)
22921             );
22922             proxy.removeClass('bg-info info');
22923         }
22924         this.fireEvent("rowdeselect", this, index);
22925         this.fireEvent("selectionchange", this);
22926     },
22927
22928     // private
22929     restoreLast : function(){
22930         if(this._last){
22931             this.last = this._last;
22932         }
22933     },
22934
22935     // private
22936     acceptsNav : function(row, col, cm){
22937         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22938     },
22939
22940     // private
22941     onEditorKey : function(field, e){
22942         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22943         if(k == e.TAB){
22944             e.stopEvent();
22945             ed.completeEdit();
22946             if(e.shiftKey){
22947                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22948             }else{
22949                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22950             }
22951         }else if(k == e.ENTER && !e.ctrlKey){
22952             e.stopEvent();
22953             ed.completeEdit();
22954             if(e.shiftKey){
22955                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22956             }else{
22957                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22958             }
22959         }else if(k == e.ESC){
22960             ed.cancelEdit();
22961         }
22962         if(newCell){
22963             g.startEditing(newCell[0], newCell[1]);
22964         }
22965     }
22966 });
22967 /*
22968  * Based on:
22969  * Ext JS Library 1.1.1
22970  * Copyright(c) 2006-2007, Ext JS, LLC.
22971  *
22972  * Originally Released Under LGPL - original licence link has changed is not relivant.
22973  *
22974  * Fork - LGPL
22975  * <script type="text/javascript">
22976  */
22977  
22978 /**
22979  * @class Roo.bootstrap.PagingToolbar
22980  * @extends Roo.bootstrap.NavSimplebar
22981  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22982  * @constructor
22983  * Create a new PagingToolbar
22984  * @param {Object} config The config object
22985  * @param {Roo.data.Store} store
22986  */
22987 Roo.bootstrap.PagingToolbar = function(config)
22988 {
22989     // old args format still supported... - xtype is prefered..
22990         // created from xtype...
22991     
22992     this.ds = config.dataSource;
22993     
22994     if (config.store && !this.ds) {
22995         this.store= Roo.factory(config.store, Roo.data);
22996         this.ds = this.store;
22997         this.ds.xmodule = this.xmodule || false;
22998     }
22999     
23000     this.toolbarItems = [];
23001     if (config.items) {
23002         this.toolbarItems = config.items;
23003     }
23004     
23005     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23006     
23007     this.cursor = 0;
23008     
23009     if (this.ds) { 
23010         this.bind(this.ds);
23011     }
23012     
23013     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23014     
23015 };
23016
23017 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23018     /**
23019      * @cfg {Roo.data.Store} dataSource
23020      * The underlying data store providing the paged data
23021      */
23022     /**
23023      * @cfg {String/HTMLElement/Element} container
23024      * container The id or element that will contain the toolbar
23025      */
23026     /**
23027      * @cfg {Boolean} displayInfo
23028      * True to display the displayMsg (defaults to false)
23029      */
23030     /**
23031      * @cfg {Number} pageSize
23032      * The number of records to display per page (defaults to 20)
23033      */
23034     pageSize: 20,
23035     /**
23036      * @cfg {String} displayMsg
23037      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23038      */
23039     displayMsg : 'Displaying {0} - {1} of {2}',
23040     /**
23041      * @cfg {String} emptyMsg
23042      * The message to display when no records are found (defaults to "No data to display")
23043      */
23044     emptyMsg : 'No data to display',
23045     /**
23046      * Customizable piece of the default paging text (defaults to "Page")
23047      * @type String
23048      */
23049     beforePageText : "Page",
23050     /**
23051      * Customizable piece of the default paging text (defaults to "of %0")
23052      * @type String
23053      */
23054     afterPageText : "of {0}",
23055     /**
23056      * Customizable piece of the default paging text (defaults to "First Page")
23057      * @type String
23058      */
23059     firstText : "First Page",
23060     /**
23061      * Customizable piece of the default paging text (defaults to "Previous Page")
23062      * @type String
23063      */
23064     prevText : "Previous Page",
23065     /**
23066      * Customizable piece of the default paging text (defaults to "Next Page")
23067      * @type String
23068      */
23069     nextText : "Next Page",
23070     /**
23071      * Customizable piece of the default paging text (defaults to "Last Page")
23072      * @type String
23073      */
23074     lastText : "Last Page",
23075     /**
23076      * Customizable piece of the default paging text (defaults to "Refresh")
23077      * @type String
23078      */
23079     refreshText : "Refresh",
23080
23081     buttons : false,
23082     // private
23083     onRender : function(ct, position) 
23084     {
23085         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23086         this.navgroup.parentId = this.id;
23087         this.navgroup.onRender(this.el, null);
23088         // add the buttons to the navgroup
23089         
23090         if(this.displayInfo){
23091             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23092             this.displayEl = this.el.select('.x-paging-info', true).first();
23093 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23094 //            this.displayEl = navel.el.select('span',true).first();
23095         }
23096         
23097         var _this = this;
23098         
23099         if(this.buttons){
23100             Roo.each(_this.buttons, function(e){ // this might need to use render????
23101                Roo.factory(e).onRender(_this.el, null);
23102             });
23103         }
23104             
23105         Roo.each(_this.toolbarItems, function(e) {
23106             _this.navgroup.addItem(e);
23107         });
23108         
23109         
23110         this.first = this.navgroup.addItem({
23111             tooltip: this.firstText,
23112             cls: "prev",
23113             icon : 'fa fa-backward',
23114             disabled: true,
23115             preventDefault: true,
23116             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23117         });
23118         
23119         this.prev =  this.navgroup.addItem({
23120             tooltip: this.prevText,
23121             cls: "prev",
23122             icon : 'fa fa-step-backward',
23123             disabled: true,
23124             preventDefault: true,
23125             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23126         });
23127     //this.addSeparator();
23128         
23129         
23130         var field = this.navgroup.addItem( {
23131             tagtype : 'span',
23132             cls : 'x-paging-position',
23133             
23134             html : this.beforePageText  +
23135                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23136                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23137          } ); //?? escaped?
23138         
23139         this.field = field.el.select('input', true).first();
23140         this.field.on("keydown", this.onPagingKeydown, this);
23141         this.field.on("focus", function(){this.dom.select();});
23142     
23143     
23144         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23145         //this.field.setHeight(18);
23146         //this.addSeparator();
23147         this.next = this.navgroup.addItem({
23148             tooltip: this.nextText,
23149             cls: "next",
23150             html : ' <i class="fa fa-step-forward">',
23151             disabled: true,
23152             preventDefault: true,
23153             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23154         });
23155         this.last = this.navgroup.addItem({
23156             tooltip: this.lastText,
23157             icon : 'fa fa-forward',
23158             cls: "next",
23159             disabled: true,
23160             preventDefault: true,
23161             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23162         });
23163     //this.addSeparator();
23164         this.loading = this.navgroup.addItem({
23165             tooltip: this.refreshText,
23166             icon: 'fa fa-refresh',
23167             preventDefault: true,
23168             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23169         });
23170         
23171     },
23172
23173     // private
23174     updateInfo : function(){
23175         if(this.displayEl){
23176             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23177             var msg = count == 0 ?
23178                 this.emptyMsg :
23179                 String.format(
23180                     this.displayMsg,
23181                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23182                 );
23183             this.displayEl.update(msg);
23184         }
23185     },
23186
23187     // private
23188     onLoad : function(ds, r, o){
23189        this.cursor = o.params ? o.params.start : 0;
23190        var d = this.getPageData(),
23191             ap = d.activePage,
23192             ps = d.pages;
23193         
23194        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23195        this.field.dom.value = ap;
23196        this.first.setDisabled(ap == 1);
23197        this.prev.setDisabled(ap == 1);
23198        this.next.setDisabled(ap == ps);
23199        this.last.setDisabled(ap == ps);
23200        this.loading.enable();
23201        this.updateInfo();
23202     },
23203
23204     // private
23205     getPageData : function(){
23206         var total = this.ds.getTotalCount();
23207         return {
23208             total : total,
23209             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23210             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23211         };
23212     },
23213
23214     // private
23215     onLoadError : function(){
23216         this.loading.enable();
23217     },
23218
23219     // private
23220     onPagingKeydown : function(e){
23221         var k = e.getKey();
23222         var d = this.getPageData();
23223         if(k == e.RETURN){
23224             var v = this.field.dom.value, pageNum;
23225             if(!v || isNaN(pageNum = parseInt(v, 10))){
23226                 this.field.dom.value = d.activePage;
23227                 return;
23228             }
23229             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23230             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23231             e.stopEvent();
23232         }
23233         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))
23234         {
23235           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23236           this.field.dom.value = pageNum;
23237           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23238           e.stopEvent();
23239         }
23240         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23241         {
23242           var v = this.field.dom.value, pageNum; 
23243           var increment = (e.shiftKey) ? 10 : 1;
23244           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23245                 increment *= -1;
23246           }
23247           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23248             this.field.dom.value = d.activePage;
23249             return;
23250           }
23251           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23252           {
23253             this.field.dom.value = parseInt(v, 10) + increment;
23254             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23255             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23256           }
23257           e.stopEvent();
23258         }
23259     },
23260
23261     // private
23262     beforeLoad : function(){
23263         if(this.loading){
23264             this.loading.disable();
23265         }
23266     },
23267
23268     // private
23269     onClick : function(which){
23270         
23271         var ds = this.ds;
23272         if (!ds) {
23273             return;
23274         }
23275         
23276         switch(which){
23277             case "first":
23278                 ds.load({params:{start: 0, limit: this.pageSize}});
23279             break;
23280             case "prev":
23281                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23282             break;
23283             case "next":
23284                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23285             break;
23286             case "last":
23287                 var total = ds.getTotalCount();
23288                 var extra = total % this.pageSize;
23289                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23290                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23291             break;
23292             case "refresh":
23293                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23294             break;
23295         }
23296     },
23297
23298     /**
23299      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23300      * @param {Roo.data.Store} store The data store to unbind
23301      */
23302     unbind : function(ds){
23303         ds.un("beforeload", this.beforeLoad, this);
23304         ds.un("load", this.onLoad, this);
23305         ds.un("loadexception", this.onLoadError, this);
23306         ds.un("remove", this.updateInfo, this);
23307         ds.un("add", this.updateInfo, this);
23308         this.ds = undefined;
23309     },
23310
23311     /**
23312      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23313      * @param {Roo.data.Store} store The data store to bind
23314      */
23315     bind : function(ds){
23316         ds.on("beforeload", this.beforeLoad, this);
23317         ds.on("load", this.onLoad, this);
23318         ds.on("loadexception", this.onLoadError, this);
23319         ds.on("remove", this.updateInfo, this);
23320         ds.on("add", this.updateInfo, this);
23321         this.ds = ds;
23322     }
23323 });/*
23324  * - LGPL
23325  *
23326  * element
23327  * 
23328  */
23329
23330 /**
23331  * @class Roo.bootstrap.MessageBar
23332  * @extends Roo.bootstrap.Component
23333  * Bootstrap MessageBar class
23334  * @cfg {String} html contents of the MessageBar
23335  * @cfg {String} weight (info | success | warning | danger) default info
23336  * @cfg {String} beforeClass insert the bar before the given class
23337  * @cfg {Boolean} closable (true | false) default false
23338  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23339  * 
23340  * @constructor
23341  * Create a new Element
23342  * @param {Object} config The config object
23343  */
23344
23345 Roo.bootstrap.MessageBar = function(config){
23346     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23347 };
23348
23349 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23350     
23351     html: '',
23352     weight: 'info',
23353     closable: false,
23354     fixed: false,
23355     beforeClass: 'bootstrap-sticky-wrap',
23356     
23357     getAutoCreate : function(){
23358         
23359         var cfg = {
23360             tag: 'div',
23361             cls: 'alert alert-dismissable alert-' + this.weight,
23362             cn: [
23363                 {
23364                     tag: 'span',
23365                     cls: 'message',
23366                     html: this.html || ''
23367                 }
23368             ]
23369         };
23370         
23371         if(this.fixed){
23372             cfg.cls += ' alert-messages-fixed';
23373         }
23374         
23375         if(this.closable){
23376             cfg.cn.push({
23377                 tag: 'button',
23378                 cls: 'close',
23379                 html: 'x'
23380             });
23381         }
23382         
23383         return cfg;
23384     },
23385     
23386     onRender : function(ct, position)
23387     {
23388         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23389         
23390         if(!this.el){
23391             var cfg = Roo.apply({},  this.getAutoCreate());
23392             cfg.id = Roo.id();
23393             
23394             if (this.cls) {
23395                 cfg.cls += ' ' + this.cls;
23396             }
23397             if (this.style) {
23398                 cfg.style = this.style;
23399             }
23400             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23401             
23402             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23403         }
23404         
23405         this.el.select('>button.close').on('click', this.hide, this);
23406         
23407     },
23408     
23409     show : function()
23410     {
23411         if (!this.rendered) {
23412             this.render();
23413         }
23414         
23415         this.el.show();
23416         
23417         this.fireEvent('show', this);
23418         
23419     },
23420     
23421     hide : function()
23422     {
23423         if (!this.rendered) {
23424             this.render();
23425         }
23426         
23427         this.el.hide();
23428         
23429         this.fireEvent('hide', this);
23430     },
23431     
23432     update : function()
23433     {
23434 //        var e = this.el.dom.firstChild;
23435 //        
23436 //        if(this.closable){
23437 //            e = e.nextSibling;
23438 //        }
23439 //        
23440 //        e.data = this.html || '';
23441
23442         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23443     }
23444    
23445 });
23446
23447  
23448
23449      /*
23450  * - LGPL
23451  *
23452  * Graph
23453  * 
23454  */
23455
23456
23457 /**
23458  * @class Roo.bootstrap.Graph
23459  * @extends Roo.bootstrap.Component
23460  * Bootstrap Graph class
23461 > Prameters
23462  -sm {number} sm 4
23463  -md {number} md 5
23464  @cfg {String} graphtype  bar | vbar | pie
23465  @cfg {number} g_x coodinator | centre x (pie)
23466  @cfg {number} g_y coodinator | centre y (pie)
23467  @cfg {number} g_r radius (pie)
23468  @cfg {number} g_height height of the chart (respected by all elements in the set)
23469  @cfg {number} g_width width of the chart (respected by all elements in the set)
23470  @cfg {Object} title The title of the chart
23471     
23472  -{Array}  values
23473  -opts (object) options for the chart 
23474      o {
23475      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23476      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23477      o vgutter (number)
23478      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.
23479      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23480      o to
23481      o stretch (boolean)
23482      o }
23483  -opts (object) options for the pie
23484      o{
23485      o cut
23486      o startAngle (number)
23487      o endAngle (number)
23488      } 
23489  *
23490  * @constructor
23491  * Create a new Input
23492  * @param {Object} config The config object
23493  */
23494
23495 Roo.bootstrap.Graph = function(config){
23496     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23497     
23498     this.addEvents({
23499         // img events
23500         /**
23501          * @event click
23502          * The img click event for the img.
23503          * @param {Roo.EventObject} e
23504          */
23505         "click" : true
23506     });
23507 };
23508
23509 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23510     
23511     sm: 4,
23512     md: 5,
23513     graphtype: 'bar',
23514     g_height: 250,
23515     g_width: 400,
23516     g_x: 50,
23517     g_y: 50,
23518     g_r: 30,
23519     opts:{
23520         //g_colors: this.colors,
23521         g_type: 'soft',
23522         g_gutter: '20%'
23523
23524     },
23525     title : false,
23526
23527     getAutoCreate : function(){
23528         
23529         var cfg = {
23530             tag: 'div',
23531             html : null
23532         };
23533         
23534         
23535         return  cfg;
23536     },
23537
23538     onRender : function(ct,position){
23539         
23540         
23541         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23542         
23543         if (typeof(Raphael) == 'undefined') {
23544             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23545             return;
23546         }
23547         
23548         this.raphael = Raphael(this.el.dom);
23549         
23550                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23551                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23552                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23553                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23554                 /*
23555                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23556                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23557                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23558                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23559                 
23560                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23561                 r.barchart(330, 10, 300, 220, data1);
23562                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23563                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23564                 */
23565                 
23566                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23567                 // r.barchart(30, 30, 560, 250,  xdata, {
23568                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23569                 //     axis : "0 0 1 1",
23570                 //     axisxlabels :  xdata
23571                 //     //yvalues : cols,
23572                    
23573                 // });
23574 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23575 //        
23576 //        this.load(null,xdata,{
23577 //                axis : "0 0 1 1",
23578 //                axisxlabels :  xdata
23579 //                });
23580
23581     },
23582
23583     load : function(graphtype,xdata,opts)
23584     {
23585         this.raphael.clear();
23586         if(!graphtype) {
23587             graphtype = this.graphtype;
23588         }
23589         if(!opts){
23590             opts = this.opts;
23591         }
23592         var r = this.raphael,
23593             fin = function () {
23594                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23595             },
23596             fout = function () {
23597                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23598             },
23599             pfin = function() {
23600                 this.sector.stop();
23601                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23602
23603                 if (this.label) {
23604                     this.label[0].stop();
23605                     this.label[0].attr({ r: 7.5 });
23606                     this.label[1].attr({ "font-weight": 800 });
23607                 }
23608             },
23609             pfout = function() {
23610                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23611
23612                 if (this.label) {
23613                     this.label[0].animate({ r: 5 }, 500, "bounce");
23614                     this.label[1].attr({ "font-weight": 400 });
23615                 }
23616             };
23617
23618         switch(graphtype){
23619             case 'bar':
23620                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23621                 break;
23622             case 'hbar':
23623                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23624                 break;
23625             case 'pie':
23626 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23627 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23628 //            
23629                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23630                 
23631                 break;
23632
23633         }
23634         
23635         if(this.title){
23636             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23637         }
23638         
23639     },
23640     
23641     setTitle: function(o)
23642     {
23643         this.title = o;
23644     },
23645     
23646     initEvents: function() {
23647         
23648         if(!this.href){
23649             this.el.on('click', this.onClick, this);
23650         }
23651     },
23652     
23653     onClick : function(e)
23654     {
23655         Roo.log('img onclick');
23656         this.fireEvent('click', this, e);
23657     }
23658    
23659 });
23660
23661  
23662 /*
23663  * - LGPL
23664  *
23665  * numberBox
23666  * 
23667  */
23668 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23669
23670 /**
23671  * @class Roo.bootstrap.dash.NumberBox
23672  * @extends Roo.bootstrap.Component
23673  * Bootstrap NumberBox class
23674  * @cfg {String} headline Box headline
23675  * @cfg {String} content Box content
23676  * @cfg {String} icon Box icon
23677  * @cfg {String} footer Footer text
23678  * @cfg {String} fhref Footer href
23679  * 
23680  * @constructor
23681  * Create a new NumberBox
23682  * @param {Object} config The config object
23683  */
23684
23685
23686 Roo.bootstrap.dash.NumberBox = function(config){
23687     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23688     
23689 };
23690
23691 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23692     
23693     headline : '',
23694     content : '',
23695     icon : '',
23696     footer : '',
23697     fhref : '',
23698     ficon : '',
23699     
23700     getAutoCreate : function(){
23701         
23702         var cfg = {
23703             tag : 'div',
23704             cls : 'small-box ',
23705             cn : [
23706                 {
23707                     tag : 'div',
23708                     cls : 'inner',
23709                     cn :[
23710                         {
23711                             tag : 'h3',
23712                             cls : 'roo-headline',
23713                             html : this.headline
23714                         },
23715                         {
23716                             tag : 'p',
23717                             cls : 'roo-content',
23718                             html : this.content
23719                         }
23720                     ]
23721                 }
23722             ]
23723         };
23724         
23725         if(this.icon){
23726             cfg.cn.push({
23727                 tag : 'div',
23728                 cls : 'icon',
23729                 cn :[
23730                     {
23731                         tag : 'i',
23732                         cls : 'ion ' + this.icon
23733                     }
23734                 ]
23735             });
23736         }
23737         
23738         if(this.footer){
23739             var footer = {
23740                 tag : 'a',
23741                 cls : 'small-box-footer',
23742                 href : this.fhref || '#',
23743                 html : this.footer
23744             };
23745             
23746             cfg.cn.push(footer);
23747             
23748         }
23749         
23750         return  cfg;
23751     },
23752
23753     onRender : function(ct,position){
23754         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23755
23756
23757        
23758                 
23759     },
23760
23761     setHeadline: function (value)
23762     {
23763         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23764     },
23765     
23766     setFooter: function (value, href)
23767     {
23768         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23769         
23770         if(href){
23771             this.el.select('a.small-box-footer',true).first().attr('href', href);
23772         }
23773         
23774     },
23775
23776     setContent: function (value)
23777     {
23778         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23779     },
23780
23781     initEvents: function() 
23782     {   
23783         
23784     }
23785     
23786 });
23787
23788  
23789 /*
23790  * - LGPL
23791  *
23792  * TabBox
23793  * 
23794  */
23795 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23796
23797 /**
23798  * @class Roo.bootstrap.dash.TabBox
23799  * @extends Roo.bootstrap.Component
23800  * Bootstrap TabBox class
23801  * @cfg {String} title Title of the TabBox
23802  * @cfg {String} icon Icon of the TabBox
23803  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23804  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23805  * 
23806  * @constructor
23807  * Create a new TabBox
23808  * @param {Object} config The config object
23809  */
23810
23811
23812 Roo.bootstrap.dash.TabBox = function(config){
23813     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23814     this.addEvents({
23815         // raw events
23816         /**
23817          * @event addpane
23818          * When a pane is added
23819          * @param {Roo.bootstrap.dash.TabPane} pane
23820          */
23821         "addpane" : true,
23822         /**
23823          * @event activatepane
23824          * When a pane is activated
23825          * @param {Roo.bootstrap.dash.TabPane} pane
23826          */
23827         "activatepane" : true
23828         
23829          
23830     });
23831     
23832     this.panes = [];
23833 };
23834
23835 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23836
23837     title : '',
23838     icon : false,
23839     showtabs : true,
23840     tabScrollable : false,
23841     
23842     getChildContainer : function()
23843     {
23844         return this.el.select('.tab-content', true).first();
23845     },
23846     
23847     getAutoCreate : function(){
23848         
23849         var header = {
23850             tag: 'li',
23851             cls: 'pull-left header',
23852             html: this.title,
23853             cn : []
23854         };
23855         
23856         if(this.icon){
23857             header.cn.push({
23858                 tag: 'i',
23859                 cls: 'fa ' + this.icon
23860             });
23861         }
23862         
23863         var h = {
23864             tag: 'ul',
23865             cls: 'nav nav-tabs pull-right',
23866             cn: [
23867                 header
23868             ]
23869         };
23870         
23871         if(this.tabScrollable){
23872             h = {
23873                 tag: 'div',
23874                 cls: 'tab-header',
23875                 cn: [
23876                     {
23877                         tag: 'ul',
23878                         cls: 'nav nav-tabs pull-right',
23879                         cn: [
23880                             header
23881                         ]
23882                     }
23883                 ]
23884             };
23885         }
23886         
23887         var cfg = {
23888             tag: 'div',
23889             cls: 'nav-tabs-custom',
23890             cn: [
23891                 h,
23892                 {
23893                     tag: 'div',
23894                     cls: 'tab-content no-padding',
23895                     cn: []
23896                 }
23897             ]
23898         };
23899
23900         return  cfg;
23901     },
23902     initEvents : function()
23903     {
23904         //Roo.log('add add pane handler');
23905         this.on('addpane', this.onAddPane, this);
23906     },
23907      /**
23908      * Updates the box title
23909      * @param {String} html to set the title to.
23910      */
23911     setTitle : function(value)
23912     {
23913         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23914     },
23915     onAddPane : function(pane)
23916     {
23917         this.panes.push(pane);
23918         //Roo.log('addpane');
23919         //Roo.log(pane);
23920         // tabs are rendere left to right..
23921         if(!this.showtabs){
23922             return;
23923         }
23924         
23925         var ctr = this.el.select('.nav-tabs', true).first();
23926          
23927          
23928         var existing = ctr.select('.nav-tab',true);
23929         var qty = existing.getCount();;
23930         
23931         
23932         var tab = ctr.createChild({
23933             tag : 'li',
23934             cls : 'nav-tab' + (qty ? '' : ' active'),
23935             cn : [
23936                 {
23937                     tag : 'a',
23938                     href:'#',
23939                     html : pane.title
23940                 }
23941             ]
23942         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23943         pane.tab = tab;
23944         
23945         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23946         if (!qty) {
23947             pane.el.addClass('active');
23948         }
23949         
23950                 
23951     },
23952     onTabClick : function(ev,un,ob,pane)
23953     {
23954         //Roo.log('tab - prev default');
23955         ev.preventDefault();
23956         
23957         
23958         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23959         pane.tab.addClass('active');
23960         //Roo.log(pane.title);
23961         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23962         // technically we should have a deactivate event.. but maybe add later.
23963         // and it should not de-activate the selected tab...
23964         this.fireEvent('activatepane', pane);
23965         pane.el.addClass('active');
23966         pane.fireEvent('activate');
23967         
23968         
23969     },
23970     
23971     getActivePane : function()
23972     {
23973         var r = false;
23974         Roo.each(this.panes, function(p) {
23975             if(p.el.hasClass('active')){
23976                 r = p;
23977                 return false;
23978             }
23979             
23980             return;
23981         });
23982         
23983         return r;
23984     }
23985     
23986     
23987 });
23988
23989  
23990 /*
23991  * - LGPL
23992  *
23993  * Tab pane
23994  * 
23995  */
23996 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23997 /**
23998  * @class Roo.bootstrap.TabPane
23999  * @extends Roo.bootstrap.Component
24000  * Bootstrap TabPane class
24001  * @cfg {Boolean} active (false | true) Default false
24002  * @cfg {String} title title of panel
24003
24004  * 
24005  * @constructor
24006  * Create a new TabPane
24007  * @param {Object} config The config object
24008  */
24009
24010 Roo.bootstrap.dash.TabPane = function(config){
24011     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24012     
24013     this.addEvents({
24014         // raw events
24015         /**
24016          * @event activate
24017          * When a pane is activated
24018          * @param {Roo.bootstrap.dash.TabPane} pane
24019          */
24020         "activate" : true
24021          
24022     });
24023 };
24024
24025 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24026     
24027     active : false,
24028     title : '',
24029     
24030     // the tabBox that this is attached to.
24031     tab : false,
24032      
24033     getAutoCreate : function() 
24034     {
24035         var cfg = {
24036             tag: 'div',
24037             cls: 'tab-pane'
24038         };
24039         
24040         if(this.active){
24041             cfg.cls += ' active';
24042         }
24043         
24044         return cfg;
24045     },
24046     initEvents  : function()
24047     {
24048         //Roo.log('trigger add pane handler');
24049         this.parent().fireEvent('addpane', this)
24050     },
24051     
24052      /**
24053      * Updates the tab title 
24054      * @param {String} html to set the title to.
24055      */
24056     setTitle: function(str)
24057     {
24058         if (!this.tab) {
24059             return;
24060         }
24061         this.title = str;
24062         this.tab.select('a', true).first().dom.innerHTML = str;
24063         
24064     }
24065     
24066     
24067     
24068 });
24069
24070  
24071
24072
24073  /*
24074  * - LGPL
24075  *
24076  * menu
24077  * 
24078  */
24079 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24080
24081 /**
24082  * @class Roo.bootstrap.menu.Menu
24083  * @extends Roo.bootstrap.Component
24084  * Bootstrap Menu class - container for Menu
24085  * @cfg {String} html Text of the menu
24086  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24087  * @cfg {String} icon Font awesome icon
24088  * @cfg {String} pos Menu align to (top | bottom) default bottom
24089  * 
24090  * 
24091  * @constructor
24092  * Create a new Menu
24093  * @param {Object} config The config object
24094  */
24095
24096
24097 Roo.bootstrap.menu.Menu = function(config){
24098     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24099     
24100     this.addEvents({
24101         /**
24102          * @event beforeshow
24103          * Fires before this menu is displayed
24104          * @param {Roo.bootstrap.menu.Menu} this
24105          */
24106         beforeshow : true,
24107         /**
24108          * @event beforehide
24109          * Fires before this menu is hidden
24110          * @param {Roo.bootstrap.menu.Menu} this
24111          */
24112         beforehide : true,
24113         /**
24114          * @event show
24115          * Fires after this menu is displayed
24116          * @param {Roo.bootstrap.menu.Menu} this
24117          */
24118         show : true,
24119         /**
24120          * @event hide
24121          * Fires after this menu is hidden
24122          * @param {Roo.bootstrap.menu.Menu} this
24123          */
24124         hide : true,
24125         /**
24126          * @event click
24127          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24128          * @param {Roo.bootstrap.menu.Menu} this
24129          * @param {Roo.EventObject} e
24130          */
24131         click : true
24132     });
24133     
24134 };
24135
24136 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24137     
24138     submenu : false,
24139     html : '',
24140     weight : 'default',
24141     icon : false,
24142     pos : 'bottom',
24143     
24144     
24145     getChildContainer : function() {
24146         if(this.isSubMenu){
24147             return this.el;
24148         }
24149         
24150         return this.el.select('ul.dropdown-menu', true).first();  
24151     },
24152     
24153     getAutoCreate : function()
24154     {
24155         var text = [
24156             {
24157                 tag : 'span',
24158                 cls : 'roo-menu-text',
24159                 html : this.html
24160             }
24161         ];
24162         
24163         if(this.icon){
24164             text.unshift({
24165                 tag : 'i',
24166                 cls : 'fa ' + this.icon
24167             })
24168         }
24169         
24170         
24171         var cfg = {
24172             tag : 'div',
24173             cls : 'btn-group',
24174             cn : [
24175                 {
24176                     tag : 'button',
24177                     cls : 'dropdown-button btn btn-' + this.weight,
24178                     cn : text
24179                 },
24180                 {
24181                     tag : 'button',
24182                     cls : 'dropdown-toggle btn btn-' + this.weight,
24183                     cn : [
24184                         {
24185                             tag : 'span',
24186                             cls : 'caret'
24187                         }
24188                     ]
24189                 },
24190                 {
24191                     tag : 'ul',
24192                     cls : 'dropdown-menu'
24193                 }
24194             ]
24195             
24196         };
24197         
24198         if(this.pos == 'top'){
24199             cfg.cls += ' dropup';
24200         }
24201         
24202         if(this.isSubMenu){
24203             cfg = {
24204                 tag : 'ul',
24205                 cls : 'dropdown-menu'
24206             }
24207         }
24208         
24209         return cfg;
24210     },
24211     
24212     onRender : function(ct, position)
24213     {
24214         this.isSubMenu = ct.hasClass('dropdown-submenu');
24215         
24216         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24217     },
24218     
24219     initEvents : function() 
24220     {
24221         if(this.isSubMenu){
24222             return;
24223         }
24224         
24225         this.hidden = true;
24226         
24227         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24228         this.triggerEl.on('click', this.onTriggerPress, this);
24229         
24230         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24231         this.buttonEl.on('click', this.onClick, this);
24232         
24233     },
24234     
24235     list : function()
24236     {
24237         if(this.isSubMenu){
24238             return this.el;
24239         }
24240         
24241         return this.el.select('ul.dropdown-menu', true).first();
24242     },
24243     
24244     onClick : function(e)
24245     {
24246         this.fireEvent("click", this, e);
24247     },
24248     
24249     onTriggerPress  : function(e)
24250     {   
24251         if (this.isVisible()) {
24252             this.hide();
24253         } else {
24254             this.show();
24255         }
24256     },
24257     
24258     isVisible : function(){
24259         return !this.hidden;
24260     },
24261     
24262     show : function()
24263     {
24264         this.fireEvent("beforeshow", this);
24265         
24266         this.hidden = false;
24267         this.el.addClass('open');
24268         
24269         Roo.get(document).on("mouseup", this.onMouseUp, this);
24270         
24271         this.fireEvent("show", this);
24272         
24273         
24274     },
24275     
24276     hide : function()
24277     {
24278         this.fireEvent("beforehide", this);
24279         
24280         this.hidden = true;
24281         this.el.removeClass('open');
24282         
24283         Roo.get(document).un("mouseup", this.onMouseUp);
24284         
24285         this.fireEvent("hide", this);
24286     },
24287     
24288     onMouseUp : function()
24289     {
24290         this.hide();
24291     }
24292     
24293 });
24294
24295  
24296  /*
24297  * - LGPL
24298  *
24299  * menu item
24300  * 
24301  */
24302 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24303
24304 /**
24305  * @class Roo.bootstrap.menu.Item
24306  * @extends Roo.bootstrap.Component
24307  * Bootstrap MenuItem class
24308  * @cfg {Boolean} submenu (true | false) default false
24309  * @cfg {String} html text of the item
24310  * @cfg {String} href the link
24311  * @cfg {Boolean} disable (true | false) default false
24312  * @cfg {Boolean} preventDefault (true | false) default true
24313  * @cfg {String} icon Font awesome icon
24314  * @cfg {String} pos Submenu align to (left | right) default right 
24315  * 
24316  * 
24317  * @constructor
24318  * Create a new Item
24319  * @param {Object} config The config object
24320  */
24321
24322
24323 Roo.bootstrap.menu.Item = function(config){
24324     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24325     this.addEvents({
24326         /**
24327          * @event mouseover
24328          * Fires when the mouse is hovering over this menu
24329          * @param {Roo.bootstrap.menu.Item} this
24330          * @param {Roo.EventObject} e
24331          */
24332         mouseover : true,
24333         /**
24334          * @event mouseout
24335          * Fires when the mouse exits this menu
24336          * @param {Roo.bootstrap.menu.Item} this
24337          * @param {Roo.EventObject} e
24338          */
24339         mouseout : true,
24340         // raw events
24341         /**
24342          * @event click
24343          * The raw click event for the entire grid.
24344          * @param {Roo.EventObject} e
24345          */
24346         click : true
24347     });
24348 };
24349
24350 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24351     
24352     submenu : false,
24353     href : '',
24354     html : '',
24355     preventDefault: true,
24356     disable : false,
24357     icon : false,
24358     pos : 'right',
24359     
24360     getAutoCreate : function()
24361     {
24362         var text = [
24363             {
24364                 tag : 'span',
24365                 cls : 'roo-menu-item-text',
24366                 html : this.html
24367             }
24368         ];
24369         
24370         if(this.icon){
24371             text.unshift({
24372                 tag : 'i',
24373                 cls : 'fa ' + this.icon
24374             })
24375         }
24376         
24377         var cfg = {
24378             tag : 'li',
24379             cn : [
24380                 {
24381                     tag : 'a',
24382                     href : this.href || '#',
24383                     cn : text
24384                 }
24385             ]
24386         };
24387         
24388         if(this.disable){
24389             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24390         }
24391         
24392         if(this.submenu){
24393             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24394             
24395             if(this.pos == 'left'){
24396                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24397             }
24398         }
24399         
24400         return cfg;
24401     },
24402     
24403     initEvents : function() 
24404     {
24405         this.el.on('mouseover', this.onMouseOver, this);
24406         this.el.on('mouseout', this.onMouseOut, this);
24407         
24408         this.el.select('a', true).first().on('click', this.onClick, this);
24409         
24410     },
24411     
24412     onClick : function(e)
24413     {
24414         if(this.preventDefault){
24415             e.preventDefault();
24416         }
24417         
24418         this.fireEvent("click", this, e);
24419     },
24420     
24421     onMouseOver : function(e)
24422     {
24423         if(this.submenu && this.pos == 'left'){
24424             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24425         }
24426         
24427         this.fireEvent("mouseover", this, e);
24428     },
24429     
24430     onMouseOut : function(e)
24431     {
24432         this.fireEvent("mouseout", this, e);
24433     }
24434 });
24435
24436  
24437
24438  /*
24439  * - LGPL
24440  *
24441  * menu separator
24442  * 
24443  */
24444 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24445
24446 /**
24447  * @class Roo.bootstrap.menu.Separator
24448  * @extends Roo.bootstrap.Component
24449  * Bootstrap Separator class
24450  * 
24451  * @constructor
24452  * Create a new Separator
24453  * @param {Object} config The config object
24454  */
24455
24456
24457 Roo.bootstrap.menu.Separator = function(config){
24458     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24459 };
24460
24461 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24462     
24463     getAutoCreate : function(){
24464         var cfg = {
24465             tag : 'li',
24466             cls: 'divider'
24467         };
24468         
24469         return cfg;
24470     }
24471    
24472 });
24473
24474  
24475
24476  /*
24477  * - LGPL
24478  *
24479  * Tooltip
24480  * 
24481  */
24482
24483 /**
24484  * @class Roo.bootstrap.Tooltip
24485  * Bootstrap Tooltip class
24486  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24487  * to determine which dom element triggers the tooltip.
24488  * 
24489  * It needs to add support for additional attributes like tooltip-position
24490  * 
24491  * @constructor
24492  * Create a new Toolti
24493  * @param {Object} config The config object
24494  */
24495
24496 Roo.bootstrap.Tooltip = function(config){
24497     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24498 };
24499
24500 Roo.apply(Roo.bootstrap.Tooltip, {
24501     /**
24502      * @function init initialize tooltip monitoring.
24503      * @static
24504      */
24505     currentEl : false,
24506     currentTip : false,
24507     currentRegion : false,
24508     
24509     //  init : delay?
24510     
24511     init : function()
24512     {
24513         Roo.get(document).on('mouseover', this.enter ,this);
24514         Roo.get(document).on('mouseout', this.leave, this);
24515          
24516         
24517         this.currentTip = new Roo.bootstrap.Tooltip();
24518     },
24519     
24520     enter : function(ev)
24521     {
24522         var dom = ev.getTarget();
24523         
24524         //Roo.log(['enter',dom]);
24525         var el = Roo.fly(dom);
24526         if (this.currentEl) {
24527             //Roo.log(dom);
24528             //Roo.log(this.currentEl);
24529             //Roo.log(this.currentEl.contains(dom));
24530             if (this.currentEl == el) {
24531                 return;
24532             }
24533             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24534                 return;
24535             }
24536
24537         }
24538         
24539         if (this.currentTip.el) {
24540             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24541         }    
24542         //Roo.log(ev);
24543         
24544         if(!el || el.dom == document){
24545             return;
24546         }
24547         
24548         var bindEl = el;
24549         
24550         // you can not look for children, as if el is the body.. then everythign is the child..
24551         if (!el.attr('tooltip')) { //
24552             if (!el.select("[tooltip]").elements.length) {
24553                 return;
24554             }
24555             // is the mouse over this child...?
24556             bindEl = el.select("[tooltip]").first();
24557             var xy = ev.getXY();
24558             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24559                 //Roo.log("not in region.");
24560                 return;
24561             }
24562             //Roo.log("child element over..");
24563             
24564         }
24565         this.currentEl = bindEl;
24566         this.currentTip.bind(bindEl);
24567         this.currentRegion = Roo.lib.Region.getRegion(dom);
24568         this.currentTip.enter();
24569         
24570     },
24571     leave : function(ev)
24572     {
24573         var dom = ev.getTarget();
24574         //Roo.log(['leave',dom]);
24575         if (!this.currentEl) {
24576             return;
24577         }
24578         
24579         
24580         if (dom != this.currentEl.dom) {
24581             return;
24582         }
24583         var xy = ev.getXY();
24584         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24585             return;
24586         }
24587         // only activate leave if mouse cursor is outside... bounding box..
24588         
24589         
24590         
24591         
24592         if (this.currentTip) {
24593             this.currentTip.leave();
24594         }
24595         //Roo.log('clear currentEl');
24596         this.currentEl = false;
24597         
24598         
24599     },
24600     alignment : {
24601         'left' : ['r-l', [-2,0], 'right'],
24602         'right' : ['l-r', [2,0], 'left'],
24603         'bottom' : ['t-b', [0,2], 'top'],
24604         'top' : [ 'b-t', [0,-2], 'bottom']
24605     }
24606     
24607 });
24608
24609
24610 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24611     
24612     
24613     bindEl : false,
24614     
24615     delay : null, // can be { show : 300 , hide: 500}
24616     
24617     timeout : null,
24618     
24619     hoverState : null, //???
24620     
24621     placement : 'bottom', 
24622     
24623     getAutoCreate : function(){
24624     
24625         var cfg = {
24626            cls : 'tooltip',
24627            role : 'tooltip',
24628            cn : [
24629                 {
24630                     cls : 'tooltip-arrow'
24631                 },
24632                 {
24633                     cls : 'tooltip-inner'
24634                 }
24635            ]
24636         };
24637         
24638         return cfg;
24639     },
24640     bind : function(el)
24641     {
24642         this.bindEl = el;
24643     },
24644       
24645     
24646     enter : function () {
24647        
24648         if (this.timeout != null) {
24649             clearTimeout(this.timeout);
24650         }
24651         
24652         this.hoverState = 'in';
24653          //Roo.log("enter - show");
24654         if (!this.delay || !this.delay.show) {
24655             this.show();
24656             return;
24657         }
24658         var _t = this;
24659         this.timeout = setTimeout(function () {
24660             if (_t.hoverState == 'in') {
24661                 _t.show();
24662             }
24663         }, this.delay.show);
24664     },
24665     leave : function()
24666     {
24667         clearTimeout(this.timeout);
24668     
24669         this.hoverState = 'out';
24670          if (!this.delay || !this.delay.hide) {
24671             this.hide();
24672             return;
24673         }
24674        
24675         var _t = this;
24676         this.timeout = setTimeout(function () {
24677             //Roo.log("leave - timeout");
24678             
24679             if (_t.hoverState == 'out') {
24680                 _t.hide();
24681                 Roo.bootstrap.Tooltip.currentEl = false;
24682             }
24683         }, delay);
24684     },
24685     
24686     show : function ()
24687     {
24688         if (!this.el) {
24689             this.render(document.body);
24690         }
24691         // set content.
24692         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24693         
24694         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24695         
24696         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24697         
24698         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24699         
24700         var placement = typeof this.placement == 'function' ?
24701             this.placement.call(this, this.el, on_el) :
24702             this.placement;
24703             
24704         var autoToken = /\s?auto?\s?/i;
24705         var autoPlace = autoToken.test(placement);
24706         if (autoPlace) {
24707             placement = placement.replace(autoToken, '') || 'top';
24708         }
24709         
24710         //this.el.detach()
24711         //this.el.setXY([0,0]);
24712         this.el.show();
24713         //this.el.dom.style.display='block';
24714         
24715         //this.el.appendTo(on_el);
24716         
24717         var p = this.getPosition();
24718         var box = this.el.getBox();
24719         
24720         if (autoPlace) {
24721             // fixme..
24722         }
24723         
24724         var align = Roo.bootstrap.Tooltip.alignment[placement];
24725         
24726         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24727         
24728         if(placement == 'top' || placement == 'bottom'){
24729             if(xy[0] < 0){
24730                 placement = 'right';
24731             }
24732             
24733             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24734                 placement = 'left';
24735             }
24736             
24737             var scroll = Roo.select('body', true).first().getScroll();
24738             
24739             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24740                 placement = 'top';
24741             }
24742             
24743         }
24744         
24745         align = Roo.bootstrap.Tooltip.alignment[placement];
24746         
24747         this.el.alignTo(this.bindEl, align[0],align[1]);
24748         //var arrow = this.el.select('.arrow',true).first();
24749         //arrow.set(align[2], 
24750         
24751         this.el.addClass(placement);
24752         
24753         this.el.addClass('in fade');
24754         
24755         this.hoverState = null;
24756         
24757         if (this.el.hasClass('fade')) {
24758             // fade it?
24759         }
24760         
24761     },
24762     hide : function()
24763     {
24764          
24765         if (!this.el) {
24766             return;
24767         }
24768         //this.el.setXY([0,0]);
24769         this.el.removeClass('in');
24770         //this.el.hide();
24771         
24772     }
24773     
24774 });
24775  
24776
24777  /*
24778  * - LGPL
24779  *
24780  * Location Picker
24781  * 
24782  */
24783
24784 /**
24785  * @class Roo.bootstrap.LocationPicker
24786  * @extends Roo.bootstrap.Component
24787  * Bootstrap LocationPicker class
24788  * @cfg {Number} latitude Position when init default 0
24789  * @cfg {Number} longitude Position when init default 0
24790  * @cfg {Number} zoom default 15
24791  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24792  * @cfg {Boolean} mapTypeControl default false
24793  * @cfg {Boolean} disableDoubleClickZoom default false
24794  * @cfg {Boolean} scrollwheel default true
24795  * @cfg {Boolean} streetViewControl default false
24796  * @cfg {Number} radius default 0
24797  * @cfg {String} locationName
24798  * @cfg {Boolean} draggable default true
24799  * @cfg {Boolean} enableAutocomplete default false
24800  * @cfg {Boolean} enableReverseGeocode default true
24801  * @cfg {String} markerTitle
24802  * 
24803  * @constructor
24804  * Create a new LocationPicker
24805  * @param {Object} config The config object
24806  */
24807
24808
24809 Roo.bootstrap.LocationPicker = function(config){
24810     
24811     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24812     
24813     this.addEvents({
24814         /**
24815          * @event initial
24816          * Fires when the picker initialized.
24817          * @param {Roo.bootstrap.LocationPicker} this
24818          * @param {Google Location} location
24819          */
24820         initial : true,
24821         /**
24822          * @event positionchanged
24823          * Fires when the picker position changed.
24824          * @param {Roo.bootstrap.LocationPicker} this
24825          * @param {Google Location} location
24826          */
24827         positionchanged : true,
24828         /**
24829          * @event resize
24830          * Fires when the map resize.
24831          * @param {Roo.bootstrap.LocationPicker} this
24832          */
24833         resize : true,
24834         /**
24835          * @event show
24836          * Fires when the map show.
24837          * @param {Roo.bootstrap.LocationPicker} this
24838          */
24839         show : true,
24840         /**
24841          * @event hide
24842          * Fires when the map hide.
24843          * @param {Roo.bootstrap.LocationPicker} this
24844          */
24845         hide : true,
24846         /**
24847          * @event mapClick
24848          * Fires when click the map.
24849          * @param {Roo.bootstrap.LocationPicker} this
24850          * @param {Map event} e
24851          */
24852         mapClick : true,
24853         /**
24854          * @event mapRightClick
24855          * Fires when right click the map.
24856          * @param {Roo.bootstrap.LocationPicker} this
24857          * @param {Map event} e
24858          */
24859         mapRightClick : true,
24860         /**
24861          * @event markerClick
24862          * Fires when click the marker.
24863          * @param {Roo.bootstrap.LocationPicker} this
24864          * @param {Map event} e
24865          */
24866         markerClick : true,
24867         /**
24868          * @event markerRightClick
24869          * Fires when right click the marker.
24870          * @param {Roo.bootstrap.LocationPicker} this
24871          * @param {Map event} e
24872          */
24873         markerRightClick : true,
24874         /**
24875          * @event OverlayViewDraw
24876          * Fires when OverlayView Draw
24877          * @param {Roo.bootstrap.LocationPicker} this
24878          */
24879         OverlayViewDraw : true,
24880         /**
24881          * @event OverlayViewOnAdd
24882          * Fires when OverlayView Draw
24883          * @param {Roo.bootstrap.LocationPicker} this
24884          */
24885         OverlayViewOnAdd : true,
24886         /**
24887          * @event OverlayViewOnRemove
24888          * Fires when OverlayView Draw
24889          * @param {Roo.bootstrap.LocationPicker} this
24890          */
24891         OverlayViewOnRemove : true,
24892         /**
24893          * @event OverlayViewShow
24894          * Fires when OverlayView Draw
24895          * @param {Roo.bootstrap.LocationPicker} this
24896          * @param {Pixel} cpx
24897          */
24898         OverlayViewShow : true,
24899         /**
24900          * @event OverlayViewHide
24901          * Fires when OverlayView Draw
24902          * @param {Roo.bootstrap.LocationPicker} this
24903          */
24904         OverlayViewHide : true,
24905         /**
24906          * @event loadexception
24907          * Fires when load google lib failed.
24908          * @param {Roo.bootstrap.LocationPicker} this
24909          */
24910         loadexception : true
24911     });
24912         
24913 };
24914
24915 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24916     
24917     gMapContext: false,
24918     
24919     latitude: 0,
24920     longitude: 0,
24921     zoom: 15,
24922     mapTypeId: false,
24923     mapTypeControl: false,
24924     disableDoubleClickZoom: false,
24925     scrollwheel: true,
24926     streetViewControl: false,
24927     radius: 0,
24928     locationName: '',
24929     draggable: true,
24930     enableAutocomplete: false,
24931     enableReverseGeocode: true,
24932     markerTitle: '',
24933     
24934     getAutoCreate: function()
24935     {
24936
24937         var cfg = {
24938             tag: 'div',
24939             cls: 'roo-location-picker'
24940         };
24941         
24942         return cfg
24943     },
24944     
24945     initEvents: function(ct, position)
24946     {       
24947         if(!this.el.getWidth() || this.isApplied()){
24948             return;
24949         }
24950         
24951         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24952         
24953         this.initial();
24954     },
24955     
24956     initial: function()
24957     {
24958         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24959             this.fireEvent('loadexception', this);
24960             return;
24961         }
24962         
24963         if(!this.mapTypeId){
24964             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24965         }
24966         
24967         this.gMapContext = this.GMapContext();
24968         
24969         this.initOverlayView();
24970         
24971         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24972         
24973         var _this = this;
24974                 
24975         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24976             _this.setPosition(_this.gMapContext.marker.position);
24977         });
24978         
24979         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24980             _this.fireEvent('mapClick', this, event);
24981             
24982         });
24983
24984         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24985             _this.fireEvent('mapRightClick', this, event);
24986             
24987         });
24988         
24989         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24990             _this.fireEvent('markerClick', this, event);
24991             
24992         });
24993
24994         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24995             _this.fireEvent('markerRightClick', this, event);
24996             
24997         });
24998         
24999         this.setPosition(this.gMapContext.location);
25000         
25001         this.fireEvent('initial', this, this.gMapContext.location);
25002     },
25003     
25004     initOverlayView: function()
25005     {
25006         var _this = this;
25007         
25008         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25009             
25010             draw: function()
25011             {
25012                 _this.fireEvent('OverlayViewDraw', _this);
25013             },
25014             
25015             onAdd: function()
25016             {
25017                 _this.fireEvent('OverlayViewOnAdd', _this);
25018             },
25019             
25020             onRemove: function()
25021             {
25022                 _this.fireEvent('OverlayViewOnRemove', _this);
25023             },
25024             
25025             show: function(cpx)
25026             {
25027                 _this.fireEvent('OverlayViewShow', _this, cpx);
25028             },
25029             
25030             hide: function()
25031             {
25032                 _this.fireEvent('OverlayViewHide', _this);
25033             }
25034             
25035         });
25036     },
25037     
25038     fromLatLngToContainerPixel: function(event)
25039     {
25040         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25041     },
25042     
25043     isApplied: function() 
25044     {
25045         return this.getGmapContext() == false ? false : true;
25046     },
25047     
25048     getGmapContext: function() 
25049     {
25050         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25051     },
25052     
25053     GMapContext: function() 
25054     {
25055         var position = new google.maps.LatLng(this.latitude, this.longitude);
25056         
25057         var _map = new google.maps.Map(this.el.dom, {
25058             center: position,
25059             zoom: this.zoom,
25060             mapTypeId: this.mapTypeId,
25061             mapTypeControl: this.mapTypeControl,
25062             disableDoubleClickZoom: this.disableDoubleClickZoom,
25063             scrollwheel: this.scrollwheel,
25064             streetViewControl: this.streetViewControl,
25065             locationName: this.locationName,
25066             draggable: this.draggable,
25067             enableAutocomplete: this.enableAutocomplete,
25068             enableReverseGeocode: this.enableReverseGeocode
25069         });
25070         
25071         var _marker = new google.maps.Marker({
25072             position: position,
25073             map: _map,
25074             title: this.markerTitle,
25075             draggable: this.draggable
25076         });
25077         
25078         return {
25079             map: _map,
25080             marker: _marker,
25081             circle: null,
25082             location: position,
25083             radius: this.radius,
25084             locationName: this.locationName,
25085             addressComponents: {
25086                 formatted_address: null,
25087                 addressLine1: null,
25088                 addressLine2: null,
25089                 streetName: null,
25090                 streetNumber: null,
25091                 city: null,
25092                 district: null,
25093                 state: null,
25094                 stateOrProvince: null
25095             },
25096             settings: this,
25097             domContainer: this.el.dom,
25098             geodecoder: new google.maps.Geocoder()
25099         };
25100     },
25101     
25102     drawCircle: function(center, radius, options) 
25103     {
25104         if (this.gMapContext.circle != null) {
25105             this.gMapContext.circle.setMap(null);
25106         }
25107         if (radius > 0) {
25108             radius *= 1;
25109             options = Roo.apply({}, options, {
25110                 strokeColor: "#0000FF",
25111                 strokeOpacity: .35,
25112                 strokeWeight: 2,
25113                 fillColor: "#0000FF",
25114                 fillOpacity: .2
25115             });
25116             
25117             options.map = this.gMapContext.map;
25118             options.radius = radius;
25119             options.center = center;
25120             this.gMapContext.circle = new google.maps.Circle(options);
25121             return this.gMapContext.circle;
25122         }
25123         
25124         return null;
25125     },
25126     
25127     setPosition: function(location) 
25128     {
25129         this.gMapContext.location = location;
25130         this.gMapContext.marker.setPosition(location);
25131         this.gMapContext.map.panTo(location);
25132         this.drawCircle(location, this.gMapContext.radius, {});
25133         
25134         var _this = this;
25135         
25136         if (this.gMapContext.settings.enableReverseGeocode) {
25137             this.gMapContext.geodecoder.geocode({
25138                 latLng: this.gMapContext.location
25139             }, function(results, status) {
25140                 
25141                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25142                     _this.gMapContext.locationName = results[0].formatted_address;
25143                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25144                     
25145                     _this.fireEvent('positionchanged', this, location);
25146                 }
25147             });
25148             
25149             return;
25150         }
25151         
25152         this.fireEvent('positionchanged', this, location);
25153     },
25154     
25155     resize: function()
25156     {
25157         google.maps.event.trigger(this.gMapContext.map, "resize");
25158         
25159         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25160         
25161         this.fireEvent('resize', this);
25162     },
25163     
25164     setPositionByLatLng: function(latitude, longitude)
25165     {
25166         this.setPosition(new google.maps.LatLng(latitude, longitude));
25167     },
25168     
25169     getCurrentPosition: function() 
25170     {
25171         return {
25172             latitude: this.gMapContext.location.lat(),
25173             longitude: this.gMapContext.location.lng()
25174         };
25175     },
25176     
25177     getAddressName: function() 
25178     {
25179         return this.gMapContext.locationName;
25180     },
25181     
25182     getAddressComponents: function() 
25183     {
25184         return this.gMapContext.addressComponents;
25185     },
25186     
25187     address_component_from_google_geocode: function(address_components) 
25188     {
25189         var result = {};
25190         
25191         for (var i = 0; i < address_components.length; i++) {
25192             var component = address_components[i];
25193             if (component.types.indexOf("postal_code") >= 0) {
25194                 result.postalCode = component.short_name;
25195             } else if (component.types.indexOf("street_number") >= 0) {
25196                 result.streetNumber = component.short_name;
25197             } else if (component.types.indexOf("route") >= 0) {
25198                 result.streetName = component.short_name;
25199             } else if (component.types.indexOf("neighborhood") >= 0) {
25200                 result.city = component.short_name;
25201             } else if (component.types.indexOf("locality") >= 0) {
25202                 result.city = component.short_name;
25203             } else if (component.types.indexOf("sublocality") >= 0) {
25204                 result.district = component.short_name;
25205             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25206                 result.stateOrProvince = component.short_name;
25207             } else if (component.types.indexOf("country") >= 0) {
25208                 result.country = component.short_name;
25209             }
25210         }
25211         
25212         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25213         result.addressLine2 = "";
25214         return result;
25215     },
25216     
25217     setZoomLevel: function(zoom)
25218     {
25219         this.gMapContext.map.setZoom(zoom);
25220     },
25221     
25222     show: function()
25223     {
25224         if(!this.el){
25225             return;
25226         }
25227         
25228         this.el.show();
25229         
25230         this.resize();
25231         
25232         this.fireEvent('show', this);
25233     },
25234     
25235     hide: function()
25236     {
25237         if(!this.el){
25238             return;
25239         }
25240         
25241         this.el.hide();
25242         
25243         this.fireEvent('hide', this);
25244     }
25245     
25246 });
25247
25248 Roo.apply(Roo.bootstrap.LocationPicker, {
25249     
25250     OverlayView : function(map, options)
25251     {
25252         options = options || {};
25253         
25254         this.setMap(map);
25255     }
25256     
25257     
25258 });/*
25259  * - LGPL
25260  *
25261  * Alert
25262  * 
25263  */
25264
25265 /**
25266  * @class Roo.bootstrap.Alert
25267  * @extends Roo.bootstrap.Component
25268  * Bootstrap Alert class
25269  * @cfg {String} title The title of alert
25270  * @cfg {String} html The content of alert
25271  * @cfg {String} weight (  success | info | warning | danger )
25272  * @cfg {String} faicon font-awesomeicon
25273  * 
25274  * @constructor
25275  * Create a new alert
25276  * @param {Object} config The config object
25277  */
25278
25279
25280 Roo.bootstrap.Alert = function(config){
25281     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25282     
25283 };
25284
25285 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25286     
25287     title: '',
25288     html: '',
25289     weight: false,
25290     faicon: false,
25291     
25292     getAutoCreate : function()
25293     {
25294         
25295         var cfg = {
25296             tag : 'div',
25297             cls : 'alert',
25298             cn : [
25299                 {
25300                     tag : 'i',
25301                     cls : 'roo-alert-icon'
25302                     
25303                 },
25304                 {
25305                     tag : 'b',
25306                     cls : 'roo-alert-title',
25307                     html : this.title
25308                 },
25309                 {
25310                     tag : 'span',
25311                     cls : 'roo-alert-text',
25312                     html : this.html
25313                 }
25314             ]
25315         };
25316         
25317         if(this.faicon){
25318             cfg.cn[0].cls += ' fa ' + this.faicon;
25319         }
25320         
25321         if(this.weight){
25322             cfg.cls += ' alert-' + this.weight;
25323         }
25324         
25325         return cfg;
25326     },
25327     
25328     initEvents: function() 
25329     {
25330         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25331     },
25332     
25333     setTitle : function(str)
25334     {
25335         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25336     },
25337     
25338     setText : function(str)
25339     {
25340         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25341     },
25342     
25343     setWeight : function(weight)
25344     {
25345         if(this.weight){
25346             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25347         }
25348         
25349         this.weight = weight;
25350         
25351         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25352     },
25353     
25354     setIcon : function(icon)
25355     {
25356         if(this.faicon){
25357             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25358         }
25359         
25360         this.faicon = icon;
25361         
25362         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25363     },
25364     
25365     hide: function() 
25366     {
25367         this.el.hide();   
25368     },
25369     
25370     show: function() 
25371     {  
25372         this.el.show();   
25373     }
25374     
25375 });
25376
25377  
25378 /*
25379 * Licence: LGPL
25380 */
25381
25382 /**
25383  * @class Roo.bootstrap.UploadCropbox
25384  * @extends Roo.bootstrap.Component
25385  * Bootstrap UploadCropbox class
25386  * @cfg {String} emptyText show when image has been loaded
25387  * @cfg {String} rotateNotify show when image too small to rotate
25388  * @cfg {Number} errorTimeout default 3000
25389  * @cfg {Number} minWidth default 300
25390  * @cfg {Number} minHeight default 300
25391  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25392  * @cfg {Boolean} isDocument (true|false) default false
25393  * @cfg {String} url action url
25394  * @cfg {String} paramName default 'imageUpload'
25395  * @cfg {String} method default POST
25396  * @cfg {Boolean} loadMask (true|false) default true
25397  * @cfg {Boolean} loadingText default 'Loading...'
25398  * 
25399  * @constructor
25400  * Create a new UploadCropbox
25401  * @param {Object} config The config object
25402  */
25403
25404 Roo.bootstrap.UploadCropbox = function(config){
25405     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25406     
25407     this.addEvents({
25408         /**
25409          * @event beforeselectfile
25410          * Fire before select file
25411          * @param {Roo.bootstrap.UploadCropbox} this
25412          */
25413         "beforeselectfile" : true,
25414         /**
25415          * @event initial
25416          * Fire after initEvent
25417          * @param {Roo.bootstrap.UploadCropbox} this
25418          */
25419         "initial" : true,
25420         /**
25421          * @event crop
25422          * Fire after initEvent
25423          * @param {Roo.bootstrap.UploadCropbox} this
25424          * @param {String} data
25425          */
25426         "crop" : true,
25427         /**
25428          * @event prepare
25429          * Fire when preparing the file data
25430          * @param {Roo.bootstrap.UploadCropbox} this
25431          * @param {Object} file
25432          */
25433         "prepare" : true,
25434         /**
25435          * @event exception
25436          * Fire when get exception
25437          * @param {Roo.bootstrap.UploadCropbox} this
25438          * @param {XMLHttpRequest} xhr
25439          */
25440         "exception" : true,
25441         /**
25442          * @event beforeloadcanvas
25443          * Fire before load the canvas
25444          * @param {Roo.bootstrap.UploadCropbox} this
25445          * @param {String} src
25446          */
25447         "beforeloadcanvas" : true,
25448         /**
25449          * @event trash
25450          * Fire when trash image
25451          * @param {Roo.bootstrap.UploadCropbox} this
25452          */
25453         "trash" : true,
25454         /**
25455          * @event download
25456          * Fire when download the image
25457          * @param {Roo.bootstrap.UploadCropbox} this
25458          */
25459         "download" : true,
25460         /**
25461          * @event footerbuttonclick
25462          * Fire when footerbuttonclick
25463          * @param {Roo.bootstrap.UploadCropbox} this
25464          * @param {String} type
25465          */
25466         "footerbuttonclick" : true,
25467         /**
25468          * @event resize
25469          * Fire when resize
25470          * @param {Roo.bootstrap.UploadCropbox} this
25471          */
25472         "resize" : true,
25473         /**
25474          * @event rotate
25475          * Fire when rotate the image
25476          * @param {Roo.bootstrap.UploadCropbox} this
25477          * @param {String} pos
25478          */
25479         "rotate" : true,
25480         /**
25481          * @event inspect
25482          * Fire when inspect the file
25483          * @param {Roo.bootstrap.UploadCropbox} this
25484          * @param {Object} file
25485          */
25486         "inspect" : true,
25487         /**
25488          * @event upload
25489          * Fire when xhr upload the file
25490          * @param {Roo.bootstrap.UploadCropbox} this
25491          * @param {Object} data
25492          */
25493         "upload" : true,
25494         /**
25495          * @event arrange
25496          * Fire when arrange the file data
25497          * @param {Roo.bootstrap.UploadCropbox} this
25498          * @param {Object} formData
25499          */
25500         "arrange" : true
25501     });
25502     
25503     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25504 };
25505
25506 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25507     
25508     emptyText : 'Click to upload image',
25509     rotateNotify : 'Image is too small to rotate',
25510     errorTimeout : 3000,
25511     scale : 0,
25512     baseScale : 1,
25513     rotate : 0,
25514     dragable : false,
25515     pinching : false,
25516     mouseX : 0,
25517     mouseY : 0,
25518     cropData : false,
25519     minWidth : 300,
25520     minHeight : 300,
25521     file : false,
25522     exif : {},
25523     baseRotate : 1,
25524     cropType : 'image/jpeg',
25525     buttons : false,
25526     canvasLoaded : false,
25527     isDocument : false,
25528     method : 'POST',
25529     paramName : 'imageUpload',
25530     loadMask : true,
25531     loadingText : 'Loading...',
25532     maskEl : false,
25533     
25534     getAutoCreate : function()
25535     {
25536         var cfg = {
25537             tag : 'div',
25538             cls : 'roo-upload-cropbox',
25539             cn : [
25540                 {
25541                     tag : 'input',
25542                     cls : 'roo-upload-cropbox-selector',
25543                     type : 'file'
25544                 },
25545                 {
25546                     tag : 'div',
25547                     cls : 'roo-upload-cropbox-body',
25548                     style : 'cursor:pointer',
25549                     cn : [
25550                         {
25551                             tag : 'div',
25552                             cls : 'roo-upload-cropbox-preview'
25553                         },
25554                         {
25555                             tag : 'div',
25556                             cls : 'roo-upload-cropbox-thumb'
25557                         },
25558                         {
25559                             tag : 'div',
25560                             cls : 'roo-upload-cropbox-empty-notify',
25561                             html : this.emptyText
25562                         },
25563                         {
25564                             tag : 'div',
25565                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25566                             html : this.rotateNotify
25567                         }
25568                     ]
25569                 },
25570                 {
25571                     tag : 'div',
25572                     cls : 'roo-upload-cropbox-footer',
25573                     cn : {
25574                         tag : 'div',
25575                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25576                         cn : []
25577                     }
25578                 }
25579             ]
25580         };
25581         
25582         return cfg;
25583     },
25584     
25585     onRender : function(ct, position)
25586     {
25587         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25588         
25589         if (this.buttons.length) {
25590             
25591             Roo.each(this.buttons, function(bb) {
25592                 
25593                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25594                 
25595                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25596                 
25597             }, this);
25598         }
25599         
25600         if(this.loadMask){
25601             this.maskEl = this.el;
25602         }
25603     },
25604     
25605     initEvents : function()
25606     {
25607         this.urlAPI = (window.createObjectURL && window) || 
25608                                 (window.URL && URL.revokeObjectURL && URL) || 
25609                                 (window.webkitURL && webkitURL);
25610                         
25611         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25612         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25613         
25614         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25615         this.selectorEl.hide();
25616         
25617         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25618         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25619         
25620         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25621         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25622         this.thumbEl.hide();
25623         
25624         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25625         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25626         
25627         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25628         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25629         this.errorEl.hide();
25630         
25631         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25632         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25633         this.footerEl.hide();
25634         
25635         this.setThumbBoxSize();
25636         
25637         this.bind();
25638         
25639         this.resize();
25640         
25641         this.fireEvent('initial', this);
25642     },
25643
25644     bind : function()
25645     {
25646         var _this = this;
25647         
25648         window.addEventListener("resize", function() { _this.resize(); } );
25649         
25650         this.bodyEl.on('click', this.beforeSelectFile, this);
25651         
25652         if(Roo.isTouch){
25653             this.bodyEl.on('touchstart', this.onTouchStart, this);
25654             this.bodyEl.on('touchmove', this.onTouchMove, this);
25655             this.bodyEl.on('touchend', this.onTouchEnd, this);
25656         }
25657         
25658         if(!Roo.isTouch){
25659             this.bodyEl.on('mousedown', this.onMouseDown, this);
25660             this.bodyEl.on('mousemove', this.onMouseMove, this);
25661             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25662             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25663             Roo.get(document).on('mouseup', this.onMouseUp, this);
25664         }
25665         
25666         this.selectorEl.on('change', this.onFileSelected, this);
25667     },
25668     
25669     reset : function()
25670     {    
25671         this.scale = 0;
25672         this.baseScale = 1;
25673         this.rotate = 0;
25674         this.baseRotate = 1;
25675         this.dragable = false;
25676         this.pinching = false;
25677         this.mouseX = 0;
25678         this.mouseY = 0;
25679         this.cropData = false;
25680         this.notifyEl.dom.innerHTML = this.emptyText;
25681         
25682         this.selectorEl.dom.value = '';
25683         
25684     },
25685     
25686     resize : function()
25687     {
25688         if(this.fireEvent('resize', this) != false){
25689             this.setThumbBoxPosition();
25690             this.setCanvasPosition();
25691         }
25692     },
25693     
25694     onFooterButtonClick : function(e, el, o, type)
25695     {
25696         switch (type) {
25697             case 'rotate-left' :
25698                 this.onRotateLeft(e);
25699                 break;
25700             case 'rotate-right' :
25701                 this.onRotateRight(e);
25702                 break;
25703             case 'picture' :
25704                 this.beforeSelectFile(e);
25705                 break;
25706             case 'trash' :
25707                 this.trash(e);
25708                 break;
25709             case 'crop' :
25710                 this.crop(e);
25711                 break;
25712             case 'download' :
25713                 this.download(e);
25714                 break;
25715             default :
25716                 break;
25717         }
25718         
25719         this.fireEvent('footerbuttonclick', this, type);
25720     },
25721     
25722     beforeSelectFile : function(e)
25723     {
25724         e.preventDefault();
25725         
25726         if(this.fireEvent('beforeselectfile', this) != false){
25727             this.selectorEl.dom.click();
25728         }
25729     },
25730     
25731     onFileSelected : function(e)
25732     {
25733         e.preventDefault();
25734         
25735         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25736             return;
25737         }
25738         
25739         var file = this.selectorEl.dom.files[0];
25740         
25741         if(this.fireEvent('inspect', this, file) != false){
25742             this.prepare(file);
25743         }
25744         
25745     },
25746     
25747     trash : function(e)
25748     {
25749         this.fireEvent('trash', this);
25750     },
25751     
25752     download : function(e)
25753     {
25754         this.fireEvent('download', this);
25755     },
25756     
25757     loadCanvas : function(src)
25758     {   
25759         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25760             
25761             this.reset();
25762             
25763             this.imageEl = document.createElement('img');
25764             
25765             var _this = this;
25766             
25767             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25768             
25769             this.imageEl.src = src;
25770         }
25771     },
25772     
25773     onLoadCanvas : function()
25774     {   
25775         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25776         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25777         
25778         this.bodyEl.un('click', this.beforeSelectFile, this);
25779         
25780         this.notifyEl.hide();
25781         this.thumbEl.show();
25782         this.footerEl.show();
25783         
25784         this.baseRotateLevel();
25785         
25786         if(this.isDocument){
25787             this.setThumbBoxSize();
25788         }
25789         
25790         this.setThumbBoxPosition();
25791         
25792         this.baseScaleLevel();
25793         
25794         this.draw();
25795         
25796         this.resize();
25797         
25798         this.canvasLoaded = true;
25799         
25800         if(this.loadMask){
25801             this.maskEl.unmask();
25802         }
25803         
25804     },
25805     
25806     setCanvasPosition : function()
25807     {   
25808         if(!this.canvasEl){
25809             return;
25810         }
25811         
25812         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25813         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25814         
25815         this.previewEl.setLeft(pw);
25816         this.previewEl.setTop(ph);
25817         
25818     },
25819     
25820     onMouseDown : function(e)
25821     {   
25822         e.stopEvent();
25823         
25824         this.dragable = true;
25825         this.pinching = false;
25826         
25827         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25828             this.dragable = false;
25829             return;
25830         }
25831         
25832         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25833         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25834         
25835     },
25836     
25837     onMouseMove : function(e)
25838     {   
25839         e.stopEvent();
25840         
25841         if(!this.canvasLoaded){
25842             return;
25843         }
25844         
25845         if (!this.dragable){
25846             return;
25847         }
25848         
25849         var minX = Math.ceil(this.thumbEl.getLeft(true));
25850         var minY = Math.ceil(this.thumbEl.getTop(true));
25851         
25852         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25853         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25854         
25855         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25856         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25857         
25858         x = x - this.mouseX;
25859         y = y - this.mouseY;
25860         
25861         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25862         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25863         
25864         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25865         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25866         
25867         this.previewEl.setLeft(bgX);
25868         this.previewEl.setTop(bgY);
25869         
25870         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25871         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25872     },
25873     
25874     onMouseUp : function(e)
25875     {   
25876         e.stopEvent();
25877         
25878         this.dragable = false;
25879     },
25880     
25881     onMouseWheel : function(e)
25882     {   
25883         e.stopEvent();
25884         
25885         this.startScale = this.scale;
25886         
25887         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25888         
25889         if(!this.zoomable()){
25890             this.scale = this.startScale;
25891             return;
25892         }
25893         
25894         this.draw();
25895         
25896         return;
25897     },
25898     
25899     zoomable : function()
25900     {
25901         var minScale = this.thumbEl.getWidth() / this.minWidth;
25902         
25903         if(this.minWidth < this.minHeight){
25904             minScale = this.thumbEl.getHeight() / this.minHeight;
25905         }
25906         
25907         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25908         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25909         
25910         if(
25911                 this.isDocument &&
25912                 (this.rotate == 0 || this.rotate == 180) && 
25913                 (
25914                     width > this.imageEl.OriginWidth || 
25915                     height > this.imageEl.OriginHeight ||
25916                     (width < this.minWidth && height < this.minHeight)
25917                 )
25918         ){
25919             return false;
25920         }
25921         
25922         if(
25923                 this.isDocument &&
25924                 (this.rotate == 90 || this.rotate == 270) && 
25925                 (
25926                     width > this.imageEl.OriginWidth || 
25927                     height > this.imageEl.OriginHeight ||
25928                     (width < this.minHeight && height < this.minWidth)
25929                 )
25930         ){
25931             return false;
25932         }
25933         
25934         if(
25935                 !this.isDocument &&
25936                 (this.rotate == 0 || this.rotate == 180) && 
25937                 (
25938                     width < this.minWidth || 
25939                     width > this.imageEl.OriginWidth || 
25940                     height < this.minHeight || 
25941                     height > this.imageEl.OriginHeight
25942                 )
25943         ){
25944             return false;
25945         }
25946         
25947         if(
25948                 !this.isDocument &&
25949                 (this.rotate == 90 || this.rotate == 270) && 
25950                 (
25951                     width < this.minHeight || 
25952                     width > this.imageEl.OriginWidth || 
25953                     height < this.minWidth || 
25954                     height > this.imageEl.OriginHeight
25955                 )
25956         ){
25957             return false;
25958         }
25959         
25960         return true;
25961         
25962     },
25963     
25964     onRotateLeft : function(e)
25965     {   
25966         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25967             
25968             var minScale = this.thumbEl.getWidth() / this.minWidth;
25969             
25970             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25971             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25972             
25973             this.startScale = this.scale;
25974             
25975             while (this.getScaleLevel() < minScale){
25976             
25977                 this.scale = this.scale + 1;
25978                 
25979                 if(!this.zoomable()){
25980                     break;
25981                 }
25982                 
25983                 if(
25984                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25985                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25986                 ){
25987                     continue;
25988                 }
25989                 
25990                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25991
25992                 this.draw();
25993                 
25994                 return;
25995             }
25996             
25997             this.scale = this.startScale;
25998             
25999             this.onRotateFail();
26000             
26001             return false;
26002         }
26003         
26004         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26005
26006         if(this.isDocument){
26007             this.setThumbBoxSize();
26008             this.setThumbBoxPosition();
26009             this.setCanvasPosition();
26010         }
26011         
26012         this.draw();
26013         
26014         this.fireEvent('rotate', this, 'left');
26015         
26016     },
26017     
26018     onRotateRight : function(e)
26019     {
26020         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26021             
26022             var minScale = this.thumbEl.getWidth() / this.minWidth;
26023         
26024             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26025             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26026             
26027             this.startScale = this.scale;
26028             
26029             while (this.getScaleLevel() < minScale){
26030             
26031                 this.scale = this.scale + 1;
26032                 
26033                 if(!this.zoomable()){
26034                     break;
26035                 }
26036                 
26037                 if(
26038                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26039                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26040                 ){
26041                     continue;
26042                 }
26043                 
26044                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26045
26046                 this.draw();
26047                 
26048                 return;
26049             }
26050             
26051             this.scale = this.startScale;
26052             
26053             this.onRotateFail();
26054             
26055             return false;
26056         }
26057         
26058         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26059
26060         if(this.isDocument){
26061             this.setThumbBoxSize();
26062             this.setThumbBoxPosition();
26063             this.setCanvasPosition();
26064         }
26065         
26066         this.draw();
26067         
26068         this.fireEvent('rotate', this, 'right');
26069     },
26070     
26071     onRotateFail : function()
26072     {
26073         this.errorEl.show(true);
26074         
26075         var _this = this;
26076         
26077         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26078     },
26079     
26080     draw : function()
26081     {
26082         this.previewEl.dom.innerHTML = '';
26083         
26084         var canvasEl = document.createElement("canvas");
26085         
26086         var contextEl = canvasEl.getContext("2d");
26087         
26088         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26089         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26090         var center = this.imageEl.OriginWidth / 2;
26091         
26092         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26093             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26094             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26095             center = this.imageEl.OriginHeight / 2;
26096         }
26097         
26098         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26099         
26100         contextEl.translate(center, center);
26101         contextEl.rotate(this.rotate * Math.PI / 180);
26102
26103         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26104         
26105         this.canvasEl = document.createElement("canvas");
26106         
26107         this.contextEl = this.canvasEl.getContext("2d");
26108         
26109         switch (this.rotate) {
26110             case 0 :
26111                 
26112                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26113                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26114                 
26115                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26116                 
26117                 break;
26118             case 90 : 
26119                 
26120                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26121                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26122                 
26123                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26124                     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);
26125                     break;
26126                 }
26127                 
26128                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26129                 
26130                 break;
26131             case 180 :
26132                 
26133                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26134                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26135                 
26136                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26137                     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);
26138                     break;
26139                 }
26140                 
26141                 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);
26142                 
26143                 break;
26144             case 270 :
26145                 
26146                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26147                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26148         
26149                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26150                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26151                     break;
26152                 }
26153                 
26154                 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);
26155                 
26156                 break;
26157             default : 
26158                 break;
26159         }
26160         
26161         this.previewEl.appendChild(this.canvasEl);
26162         
26163         this.setCanvasPosition();
26164     },
26165     
26166     crop : function()
26167     {
26168         if(!this.canvasLoaded){
26169             return;
26170         }
26171         
26172         var imageCanvas = document.createElement("canvas");
26173         
26174         var imageContext = imageCanvas.getContext("2d");
26175         
26176         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26177         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26178         
26179         var center = imageCanvas.width / 2;
26180         
26181         imageContext.translate(center, center);
26182         
26183         imageContext.rotate(this.rotate * Math.PI / 180);
26184         
26185         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26186         
26187         var canvas = document.createElement("canvas");
26188         
26189         var context = canvas.getContext("2d");
26190                 
26191         canvas.width = this.minWidth;
26192         canvas.height = this.minHeight;
26193
26194         switch (this.rotate) {
26195             case 0 :
26196                 
26197                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26198                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26199                 
26200                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26201                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26202                 
26203                 var targetWidth = this.minWidth - 2 * x;
26204                 var targetHeight = this.minHeight - 2 * y;
26205                 
26206                 var scale = 1;
26207                 
26208                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26209                     scale = targetWidth / width;
26210                 }
26211                 
26212                 if(x > 0 && y == 0){
26213                     scale = targetHeight / height;
26214                 }
26215                 
26216                 if(x > 0 && y > 0){
26217                     scale = targetWidth / width;
26218                     
26219                     if(width < height){
26220                         scale = targetHeight / height;
26221                     }
26222                 }
26223                 
26224                 context.scale(scale, scale);
26225                 
26226                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26227                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26228
26229                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26230                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26231
26232                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26233                 
26234                 break;
26235             case 90 : 
26236                 
26237                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26238                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26239                 
26240                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26241                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26242                 
26243                 var targetWidth = this.minWidth - 2 * x;
26244                 var targetHeight = this.minHeight - 2 * y;
26245                 
26246                 var scale = 1;
26247                 
26248                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26249                     scale = targetWidth / width;
26250                 }
26251                 
26252                 if(x > 0 && y == 0){
26253                     scale = targetHeight / height;
26254                 }
26255                 
26256                 if(x > 0 && y > 0){
26257                     scale = targetWidth / width;
26258                     
26259                     if(width < height){
26260                         scale = targetHeight / height;
26261                     }
26262                 }
26263                 
26264                 context.scale(scale, scale);
26265                 
26266                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26267                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26268
26269                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26270                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26271                 
26272                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26273                 
26274                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26275                 
26276                 break;
26277             case 180 :
26278                 
26279                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26280                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26281                 
26282                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26283                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26284                 
26285                 var targetWidth = this.minWidth - 2 * x;
26286                 var targetHeight = this.minHeight - 2 * y;
26287                 
26288                 var scale = 1;
26289                 
26290                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26291                     scale = targetWidth / width;
26292                 }
26293                 
26294                 if(x > 0 && y == 0){
26295                     scale = targetHeight / height;
26296                 }
26297                 
26298                 if(x > 0 && y > 0){
26299                     scale = targetWidth / width;
26300                     
26301                     if(width < height){
26302                         scale = targetHeight / height;
26303                     }
26304                 }
26305                 
26306                 context.scale(scale, scale);
26307                 
26308                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26309                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26310
26311                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26312                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26313
26314                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26315                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26316                 
26317                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26318                 
26319                 break;
26320             case 270 :
26321                 
26322                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26323                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26324                 
26325                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26326                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26327                 
26328                 var targetWidth = this.minWidth - 2 * x;
26329                 var targetHeight = this.minHeight - 2 * y;
26330                 
26331                 var scale = 1;
26332                 
26333                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26334                     scale = targetWidth / width;
26335                 }
26336                 
26337                 if(x > 0 && y == 0){
26338                     scale = targetHeight / height;
26339                 }
26340                 
26341                 if(x > 0 && y > 0){
26342                     scale = targetWidth / width;
26343                     
26344                     if(width < height){
26345                         scale = targetHeight / height;
26346                     }
26347                 }
26348                 
26349                 context.scale(scale, scale);
26350                 
26351                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26352                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26353
26354                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26355                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26356                 
26357                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26358                 
26359                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26360                 
26361                 break;
26362             default : 
26363                 break;
26364         }
26365         
26366         this.cropData = canvas.toDataURL(this.cropType);
26367         
26368         if(this.fireEvent('crop', this, this.cropData) !== false){
26369             this.process(this.file, this.cropData);
26370         }
26371         
26372         return;
26373         
26374     },
26375     
26376     setThumbBoxSize : function()
26377     {
26378         var width, height;
26379         
26380         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26381             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26382             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26383             
26384             this.minWidth = width;
26385             this.minHeight = height;
26386             
26387             if(this.rotate == 90 || this.rotate == 270){
26388                 this.minWidth = height;
26389                 this.minHeight = width;
26390             }
26391         }
26392         
26393         height = 300;
26394         width = Math.ceil(this.minWidth * height / this.minHeight);
26395         
26396         if(this.minWidth > this.minHeight){
26397             width = 300;
26398             height = Math.ceil(this.minHeight * width / this.minWidth);
26399         }
26400         
26401         this.thumbEl.setStyle({
26402             width : width + 'px',
26403             height : height + 'px'
26404         });
26405
26406         return;
26407             
26408     },
26409     
26410     setThumbBoxPosition : function()
26411     {
26412         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26413         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26414         
26415         this.thumbEl.setLeft(x);
26416         this.thumbEl.setTop(y);
26417         
26418     },
26419     
26420     baseRotateLevel : function()
26421     {
26422         this.baseRotate = 1;
26423         
26424         if(
26425                 typeof(this.exif) != 'undefined' &&
26426                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26427                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26428         ){
26429             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26430         }
26431         
26432         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26433         
26434     },
26435     
26436     baseScaleLevel : function()
26437     {
26438         var width, height;
26439         
26440         if(this.isDocument){
26441             
26442             if(this.baseRotate == 6 || this.baseRotate == 8){
26443             
26444                 height = this.thumbEl.getHeight();
26445                 this.baseScale = height / this.imageEl.OriginWidth;
26446
26447                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26448                     width = this.thumbEl.getWidth();
26449                     this.baseScale = width / this.imageEl.OriginHeight;
26450                 }
26451
26452                 return;
26453             }
26454
26455             height = this.thumbEl.getHeight();
26456             this.baseScale = height / this.imageEl.OriginHeight;
26457
26458             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26459                 width = this.thumbEl.getWidth();
26460                 this.baseScale = width / this.imageEl.OriginWidth;
26461             }
26462
26463             return;
26464         }
26465         
26466         if(this.baseRotate == 6 || this.baseRotate == 8){
26467             
26468             width = this.thumbEl.getHeight();
26469             this.baseScale = width / this.imageEl.OriginHeight;
26470             
26471             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26472                 height = this.thumbEl.getWidth();
26473                 this.baseScale = height / this.imageEl.OriginHeight;
26474             }
26475             
26476             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26477                 height = this.thumbEl.getWidth();
26478                 this.baseScale = height / this.imageEl.OriginHeight;
26479                 
26480                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26481                     width = this.thumbEl.getHeight();
26482                     this.baseScale = width / this.imageEl.OriginWidth;
26483                 }
26484             }
26485             
26486             return;
26487         }
26488         
26489         width = this.thumbEl.getWidth();
26490         this.baseScale = width / this.imageEl.OriginWidth;
26491         
26492         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26493             height = this.thumbEl.getHeight();
26494             this.baseScale = height / this.imageEl.OriginHeight;
26495         }
26496         
26497         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26498             
26499             height = this.thumbEl.getHeight();
26500             this.baseScale = height / this.imageEl.OriginHeight;
26501             
26502             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26503                 width = this.thumbEl.getWidth();
26504                 this.baseScale = width / this.imageEl.OriginWidth;
26505             }
26506             
26507         }
26508         
26509         return;
26510     },
26511     
26512     getScaleLevel : function()
26513     {
26514         return this.baseScale * Math.pow(1.1, this.scale);
26515     },
26516     
26517     onTouchStart : function(e)
26518     {
26519         if(!this.canvasLoaded){
26520             this.beforeSelectFile(e);
26521             return;
26522         }
26523         
26524         var touches = e.browserEvent.touches;
26525         
26526         if(!touches){
26527             return;
26528         }
26529         
26530         if(touches.length == 1){
26531             this.onMouseDown(e);
26532             return;
26533         }
26534         
26535         if(touches.length != 2){
26536             return;
26537         }
26538         
26539         var coords = [];
26540         
26541         for(var i = 0, finger; finger = touches[i]; i++){
26542             coords.push(finger.pageX, finger.pageY);
26543         }
26544         
26545         var x = Math.pow(coords[0] - coords[2], 2);
26546         var y = Math.pow(coords[1] - coords[3], 2);
26547         
26548         this.startDistance = Math.sqrt(x + y);
26549         
26550         this.startScale = this.scale;
26551         
26552         this.pinching = true;
26553         this.dragable = false;
26554         
26555     },
26556     
26557     onTouchMove : function(e)
26558     {
26559         if(!this.pinching && !this.dragable){
26560             return;
26561         }
26562         
26563         var touches = e.browserEvent.touches;
26564         
26565         if(!touches){
26566             return;
26567         }
26568         
26569         if(this.dragable){
26570             this.onMouseMove(e);
26571             return;
26572         }
26573         
26574         var coords = [];
26575         
26576         for(var i = 0, finger; finger = touches[i]; i++){
26577             coords.push(finger.pageX, finger.pageY);
26578         }
26579         
26580         var x = Math.pow(coords[0] - coords[2], 2);
26581         var y = Math.pow(coords[1] - coords[3], 2);
26582         
26583         this.endDistance = Math.sqrt(x + y);
26584         
26585         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26586         
26587         if(!this.zoomable()){
26588             this.scale = this.startScale;
26589             return;
26590         }
26591         
26592         this.draw();
26593         
26594     },
26595     
26596     onTouchEnd : function(e)
26597     {
26598         this.pinching = false;
26599         this.dragable = false;
26600         
26601     },
26602     
26603     process : function(file, crop)
26604     {
26605         if(this.loadMask){
26606             this.maskEl.mask(this.loadingText);
26607         }
26608         
26609         this.xhr = new XMLHttpRequest();
26610         
26611         file.xhr = this.xhr;
26612
26613         this.xhr.open(this.method, this.url, true);
26614         
26615         var headers = {
26616             "Accept": "application/json",
26617             "Cache-Control": "no-cache",
26618             "X-Requested-With": "XMLHttpRequest"
26619         };
26620         
26621         for (var headerName in headers) {
26622             var headerValue = headers[headerName];
26623             if (headerValue) {
26624                 this.xhr.setRequestHeader(headerName, headerValue);
26625             }
26626         }
26627         
26628         var _this = this;
26629         
26630         this.xhr.onload = function()
26631         {
26632             _this.xhrOnLoad(_this.xhr);
26633         }
26634         
26635         this.xhr.onerror = function()
26636         {
26637             _this.xhrOnError(_this.xhr);
26638         }
26639         
26640         var formData = new FormData();
26641
26642         formData.append('returnHTML', 'NO');
26643         
26644         if(crop){
26645             formData.append('crop', crop);
26646         }
26647         
26648         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26649             formData.append(this.paramName, file, file.name);
26650         }
26651         
26652         if(typeof(file.filename) != 'undefined'){
26653             formData.append('filename', file.filename);
26654         }
26655         
26656         if(typeof(file.mimetype) != 'undefined'){
26657             formData.append('mimetype', file.mimetype);
26658         }
26659         
26660         if(this.fireEvent('arrange', this, formData) != false){
26661             this.xhr.send(formData);
26662         };
26663     },
26664     
26665     xhrOnLoad : function(xhr)
26666     {
26667         if(this.loadMask){
26668             this.maskEl.unmask();
26669         }
26670         
26671         if (xhr.readyState !== 4) {
26672             this.fireEvent('exception', this, xhr);
26673             return;
26674         }
26675
26676         var response = Roo.decode(xhr.responseText);
26677         
26678         if(!response.success){
26679             this.fireEvent('exception', this, xhr);
26680             return;
26681         }
26682         
26683         var response = Roo.decode(xhr.responseText);
26684         
26685         this.fireEvent('upload', this, response);
26686         
26687     },
26688     
26689     xhrOnError : function()
26690     {
26691         if(this.loadMask){
26692             this.maskEl.unmask();
26693         }
26694         
26695         Roo.log('xhr on error');
26696         
26697         var response = Roo.decode(xhr.responseText);
26698           
26699         Roo.log(response);
26700         
26701     },
26702     
26703     prepare : function(file)
26704     {   
26705         if(this.loadMask){
26706             this.maskEl.mask(this.loadingText);
26707         }
26708         
26709         this.file = false;
26710         this.exif = {};
26711         
26712         if(typeof(file) === 'string'){
26713             this.loadCanvas(file);
26714             return;
26715         }
26716         
26717         if(!file || !this.urlAPI){
26718             return;
26719         }
26720         
26721         this.file = file;
26722         this.cropType = file.type;
26723         
26724         var _this = this;
26725         
26726         if(this.fireEvent('prepare', this, this.file) != false){
26727             
26728             var reader = new FileReader();
26729             
26730             reader.onload = function (e) {
26731                 if (e.target.error) {
26732                     Roo.log(e.target.error);
26733                     return;
26734                 }
26735                 
26736                 var buffer = e.target.result,
26737                     dataView = new DataView(buffer),
26738                     offset = 2,
26739                     maxOffset = dataView.byteLength - 4,
26740                     markerBytes,
26741                     markerLength;
26742                 
26743                 if (dataView.getUint16(0) === 0xffd8) {
26744                     while (offset < maxOffset) {
26745                         markerBytes = dataView.getUint16(offset);
26746                         
26747                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26748                             markerLength = dataView.getUint16(offset + 2) + 2;
26749                             if (offset + markerLength > dataView.byteLength) {
26750                                 Roo.log('Invalid meta data: Invalid segment size.');
26751                                 break;
26752                             }
26753                             
26754                             if(markerBytes == 0xffe1){
26755                                 _this.parseExifData(
26756                                     dataView,
26757                                     offset,
26758                                     markerLength
26759                                 );
26760                             }
26761                             
26762                             offset += markerLength;
26763                             
26764                             continue;
26765                         }
26766                         
26767                         break;
26768                     }
26769                     
26770                 }
26771                 
26772                 var url = _this.urlAPI.createObjectURL(_this.file);
26773                 
26774                 _this.loadCanvas(url);
26775                 
26776                 return;
26777             }
26778             
26779             reader.readAsArrayBuffer(this.file);
26780             
26781         }
26782         
26783     },
26784     
26785     parseExifData : function(dataView, offset, length)
26786     {
26787         var tiffOffset = offset + 10,
26788             littleEndian,
26789             dirOffset;
26790     
26791         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26792             // No Exif data, might be XMP data instead
26793             return;
26794         }
26795         
26796         // Check for the ASCII code for "Exif" (0x45786966):
26797         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26798             // No Exif data, might be XMP data instead
26799             return;
26800         }
26801         if (tiffOffset + 8 > dataView.byteLength) {
26802             Roo.log('Invalid Exif data: Invalid segment size.');
26803             return;
26804         }
26805         // Check for the two null bytes:
26806         if (dataView.getUint16(offset + 8) !== 0x0000) {
26807             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26808             return;
26809         }
26810         // Check the byte alignment:
26811         switch (dataView.getUint16(tiffOffset)) {
26812         case 0x4949:
26813             littleEndian = true;
26814             break;
26815         case 0x4D4D:
26816             littleEndian = false;
26817             break;
26818         default:
26819             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26820             return;
26821         }
26822         // Check for the TIFF tag marker (0x002A):
26823         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26824             Roo.log('Invalid Exif data: Missing TIFF marker.');
26825             return;
26826         }
26827         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26828         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26829         
26830         this.parseExifTags(
26831             dataView,
26832             tiffOffset,
26833             tiffOffset + dirOffset,
26834             littleEndian
26835         );
26836     },
26837     
26838     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26839     {
26840         var tagsNumber,
26841             dirEndOffset,
26842             i;
26843         if (dirOffset + 6 > dataView.byteLength) {
26844             Roo.log('Invalid Exif data: Invalid directory offset.');
26845             return;
26846         }
26847         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26848         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26849         if (dirEndOffset + 4 > dataView.byteLength) {
26850             Roo.log('Invalid Exif data: Invalid directory size.');
26851             return;
26852         }
26853         for (i = 0; i < tagsNumber; i += 1) {
26854             this.parseExifTag(
26855                 dataView,
26856                 tiffOffset,
26857                 dirOffset + 2 + 12 * i, // tag offset
26858                 littleEndian
26859             );
26860         }
26861         // Return the offset to the next directory:
26862         return dataView.getUint32(dirEndOffset, littleEndian);
26863     },
26864     
26865     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26866     {
26867         var tag = dataView.getUint16(offset, littleEndian);
26868         
26869         this.exif[tag] = this.getExifValue(
26870             dataView,
26871             tiffOffset,
26872             offset,
26873             dataView.getUint16(offset + 2, littleEndian), // tag type
26874             dataView.getUint32(offset + 4, littleEndian), // tag length
26875             littleEndian
26876         );
26877     },
26878     
26879     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26880     {
26881         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26882             tagSize,
26883             dataOffset,
26884             values,
26885             i,
26886             str,
26887             c;
26888     
26889         if (!tagType) {
26890             Roo.log('Invalid Exif data: Invalid tag type.');
26891             return;
26892         }
26893         
26894         tagSize = tagType.size * length;
26895         // Determine if the value is contained in the dataOffset bytes,
26896         // or if the value at the dataOffset is a pointer to the actual data:
26897         dataOffset = tagSize > 4 ?
26898                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26899         if (dataOffset + tagSize > dataView.byteLength) {
26900             Roo.log('Invalid Exif data: Invalid data offset.');
26901             return;
26902         }
26903         if (length === 1) {
26904             return tagType.getValue(dataView, dataOffset, littleEndian);
26905         }
26906         values = [];
26907         for (i = 0; i < length; i += 1) {
26908             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26909         }
26910         
26911         if (tagType.ascii) {
26912             str = '';
26913             // Concatenate the chars:
26914             for (i = 0; i < values.length; i += 1) {
26915                 c = values[i];
26916                 // Ignore the terminating NULL byte(s):
26917                 if (c === '\u0000') {
26918                     break;
26919                 }
26920                 str += c;
26921             }
26922             return str;
26923         }
26924         return values;
26925     }
26926     
26927 });
26928
26929 Roo.apply(Roo.bootstrap.UploadCropbox, {
26930     tags : {
26931         'Orientation': 0x0112
26932     },
26933     
26934     Orientation: {
26935             1: 0, //'top-left',
26936 //            2: 'top-right',
26937             3: 180, //'bottom-right',
26938 //            4: 'bottom-left',
26939 //            5: 'left-top',
26940             6: 90, //'right-top',
26941 //            7: 'right-bottom',
26942             8: 270 //'left-bottom'
26943     },
26944     
26945     exifTagTypes : {
26946         // byte, 8-bit unsigned int:
26947         1: {
26948             getValue: function (dataView, dataOffset) {
26949                 return dataView.getUint8(dataOffset);
26950             },
26951             size: 1
26952         },
26953         // ascii, 8-bit byte:
26954         2: {
26955             getValue: function (dataView, dataOffset) {
26956                 return String.fromCharCode(dataView.getUint8(dataOffset));
26957             },
26958             size: 1,
26959             ascii: true
26960         },
26961         // short, 16 bit int:
26962         3: {
26963             getValue: function (dataView, dataOffset, littleEndian) {
26964                 return dataView.getUint16(dataOffset, littleEndian);
26965             },
26966             size: 2
26967         },
26968         // long, 32 bit int:
26969         4: {
26970             getValue: function (dataView, dataOffset, littleEndian) {
26971                 return dataView.getUint32(dataOffset, littleEndian);
26972             },
26973             size: 4
26974         },
26975         // rational = two long values, first is numerator, second is denominator:
26976         5: {
26977             getValue: function (dataView, dataOffset, littleEndian) {
26978                 return dataView.getUint32(dataOffset, littleEndian) /
26979                     dataView.getUint32(dataOffset + 4, littleEndian);
26980             },
26981             size: 8
26982         },
26983         // slong, 32 bit signed int:
26984         9: {
26985             getValue: function (dataView, dataOffset, littleEndian) {
26986                 return dataView.getInt32(dataOffset, littleEndian);
26987             },
26988             size: 4
26989         },
26990         // srational, two slongs, first is numerator, second is denominator:
26991         10: {
26992             getValue: function (dataView, dataOffset, littleEndian) {
26993                 return dataView.getInt32(dataOffset, littleEndian) /
26994                     dataView.getInt32(dataOffset + 4, littleEndian);
26995             },
26996             size: 8
26997         }
26998     },
26999     
27000     footer : {
27001         STANDARD : [
27002             {
27003                 tag : 'div',
27004                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27005                 action : 'rotate-left',
27006                 cn : [
27007                     {
27008                         tag : 'button',
27009                         cls : 'btn btn-default',
27010                         html : '<i class="fa fa-undo"></i>'
27011                     }
27012                 ]
27013             },
27014             {
27015                 tag : 'div',
27016                 cls : 'btn-group roo-upload-cropbox-picture',
27017                 action : 'picture',
27018                 cn : [
27019                     {
27020                         tag : 'button',
27021                         cls : 'btn btn-default',
27022                         html : '<i class="fa fa-picture-o"></i>'
27023                     }
27024                 ]
27025             },
27026             {
27027                 tag : 'div',
27028                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27029                 action : 'rotate-right',
27030                 cn : [
27031                     {
27032                         tag : 'button',
27033                         cls : 'btn btn-default',
27034                         html : '<i class="fa fa-repeat"></i>'
27035                     }
27036                 ]
27037             }
27038         ],
27039         DOCUMENT : [
27040             {
27041                 tag : 'div',
27042                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27043                 action : 'rotate-left',
27044                 cn : [
27045                     {
27046                         tag : 'button',
27047                         cls : 'btn btn-default',
27048                         html : '<i class="fa fa-undo"></i>'
27049                     }
27050                 ]
27051             },
27052             {
27053                 tag : 'div',
27054                 cls : 'btn-group roo-upload-cropbox-download',
27055                 action : 'download',
27056                 cn : [
27057                     {
27058                         tag : 'button',
27059                         cls : 'btn btn-default',
27060                         html : '<i class="fa fa-download"></i>'
27061                     }
27062                 ]
27063             },
27064             {
27065                 tag : 'div',
27066                 cls : 'btn-group roo-upload-cropbox-crop',
27067                 action : 'crop',
27068                 cn : [
27069                     {
27070                         tag : 'button',
27071                         cls : 'btn btn-default',
27072                         html : '<i class="fa fa-crop"></i>'
27073                     }
27074                 ]
27075             },
27076             {
27077                 tag : 'div',
27078                 cls : 'btn-group roo-upload-cropbox-trash',
27079                 action : 'trash',
27080                 cn : [
27081                     {
27082                         tag : 'button',
27083                         cls : 'btn btn-default',
27084                         html : '<i class="fa fa-trash"></i>'
27085                     }
27086                 ]
27087             },
27088             {
27089                 tag : 'div',
27090                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27091                 action : 'rotate-right',
27092                 cn : [
27093                     {
27094                         tag : 'button',
27095                         cls : 'btn btn-default',
27096                         html : '<i class="fa fa-repeat"></i>'
27097                     }
27098                 ]
27099             }
27100         ],
27101         ROTATOR : [
27102             {
27103                 tag : 'div',
27104                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27105                 action : 'rotate-left',
27106                 cn : [
27107                     {
27108                         tag : 'button',
27109                         cls : 'btn btn-default',
27110                         html : '<i class="fa fa-undo"></i>'
27111                     }
27112                 ]
27113             },
27114             {
27115                 tag : 'div',
27116                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27117                 action : 'rotate-right',
27118                 cn : [
27119                     {
27120                         tag : 'button',
27121                         cls : 'btn btn-default',
27122                         html : '<i class="fa fa-repeat"></i>'
27123                     }
27124                 ]
27125             }
27126         ]
27127     }
27128 });
27129
27130 /*
27131 * Licence: LGPL
27132 */
27133
27134 /**
27135  * @class Roo.bootstrap.DocumentManager
27136  * @extends Roo.bootstrap.Component
27137  * Bootstrap DocumentManager class
27138  * @cfg {String} paramName default 'imageUpload'
27139  * @cfg {String} method default POST
27140  * @cfg {String} url action url
27141  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27142  * @cfg {Boolean} multiple multiple upload default true
27143  * @cfg {Number} thumbSize default 300
27144  * @cfg {String} fieldLabel
27145  * @cfg {Number} labelWidth default 4
27146  * @cfg {String} labelAlign (left|top) default left
27147  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27148  * 
27149  * @constructor
27150  * Create a new DocumentManager
27151  * @param {Object} config The config object
27152  */
27153
27154 Roo.bootstrap.DocumentManager = function(config){
27155     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27156     
27157     this.addEvents({
27158         /**
27159          * @event initial
27160          * Fire when initial the DocumentManager
27161          * @param {Roo.bootstrap.DocumentManager} this
27162          */
27163         "initial" : true,
27164         /**
27165          * @event inspect
27166          * inspect selected file
27167          * @param {Roo.bootstrap.DocumentManager} this
27168          * @param {File} file
27169          */
27170         "inspect" : true,
27171         /**
27172          * @event exception
27173          * Fire when xhr load exception
27174          * @param {Roo.bootstrap.DocumentManager} this
27175          * @param {XMLHttpRequest} xhr
27176          */
27177         "exception" : true,
27178         /**
27179          * @event prepare
27180          * prepare the form data
27181          * @param {Roo.bootstrap.DocumentManager} this
27182          * @param {Object} formData
27183          */
27184         "prepare" : true,
27185         /**
27186          * @event remove
27187          * Fire when remove the file
27188          * @param {Roo.bootstrap.DocumentManager} this
27189          * @param {Object} file
27190          */
27191         "remove" : true,
27192         /**
27193          * @event refresh
27194          * Fire after refresh the file
27195          * @param {Roo.bootstrap.DocumentManager} this
27196          */
27197         "refresh" : true,
27198         /**
27199          * @event click
27200          * Fire after click the image
27201          * @param {Roo.bootstrap.DocumentManager} this
27202          * @param {Object} file
27203          */
27204         "click" : true,
27205         /**
27206          * @event edit
27207          * Fire when upload a image and editable set to true
27208          * @param {Roo.bootstrap.DocumentManager} this
27209          * @param {Object} file
27210          */
27211         "edit" : true,
27212         /**
27213          * @event beforeselectfile
27214          * Fire before select file
27215          * @param {Roo.bootstrap.DocumentManager} this
27216          */
27217         "beforeselectfile" : true,
27218         /**
27219          * @event process
27220          * Fire before process file
27221          * @param {Roo.bootstrap.DocumentManager} this
27222          * @param {Object} file
27223          */
27224         "process" : true
27225         
27226     });
27227 };
27228
27229 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27230     
27231     boxes : 0,
27232     inputName : '',
27233     thumbSize : 300,
27234     multiple : true,
27235     files : [],
27236     method : 'POST',
27237     url : '',
27238     paramName : 'imageUpload',
27239     fieldLabel : '',
27240     labelWidth : 4,
27241     labelAlign : 'left',
27242     editable : true,
27243     delegates : [],
27244     
27245     
27246     xhr : false, 
27247     
27248     getAutoCreate : function()
27249     {   
27250         var managerWidget = {
27251             tag : 'div',
27252             cls : 'roo-document-manager',
27253             cn : [
27254                 {
27255                     tag : 'input',
27256                     cls : 'roo-document-manager-selector',
27257                     type : 'file'
27258                 },
27259                 {
27260                     tag : 'div',
27261                     cls : 'roo-document-manager-uploader',
27262                     cn : [
27263                         {
27264                             tag : 'div',
27265                             cls : 'roo-document-manager-upload-btn',
27266                             html : '<i class="fa fa-plus"></i>'
27267                         }
27268                     ]
27269                     
27270                 }
27271             ]
27272         };
27273         
27274         var content = [
27275             {
27276                 tag : 'div',
27277                 cls : 'column col-md-12',
27278                 cn : managerWidget
27279             }
27280         ];
27281         
27282         if(this.fieldLabel.length){
27283             
27284             content = [
27285                 {
27286                     tag : 'div',
27287                     cls : 'column col-md-12',
27288                     html : this.fieldLabel
27289                 },
27290                 {
27291                     tag : 'div',
27292                     cls : 'column col-md-12',
27293                     cn : managerWidget
27294                 }
27295             ];
27296
27297             if(this.labelAlign == 'left'){
27298                 content = [
27299                     {
27300                         tag : 'div',
27301                         cls : 'column col-md-' + this.labelWidth,
27302                         html : this.fieldLabel
27303                     },
27304                     {
27305                         tag : 'div',
27306                         cls : 'column col-md-' + (12 - this.labelWidth),
27307                         cn : managerWidget
27308                     }
27309                 ];
27310                 
27311             }
27312         }
27313         
27314         var cfg = {
27315             tag : 'div',
27316             cls : 'row clearfix',
27317             cn : content
27318         };
27319         
27320         return cfg;
27321         
27322     },
27323     
27324     initEvents : function()
27325     {
27326         this.managerEl = this.el.select('.roo-document-manager', true).first();
27327         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27328         
27329         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27330         this.selectorEl.hide();
27331         
27332         if(this.multiple){
27333             this.selectorEl.attr('multiple', 'multiple');
27334         }
27335         
27336         this.selectorEl.on('change', this.onFileSelected, this);
27337         
27338         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27339         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27340         
27341         this.uploader.on('click', this.onUploaderClick, this);
27342         
27343         this.renderProgressDialog();
27344         
27345         var _this = this;
27346         
27347         window.addEventListener("resize", function() { _this.refresh(); } );
27348         
27349         this.fireEvent('initial', this);
27350     },
27351     
27352     renderProgressDialog : function()
27353     {
27354         var _this = this;
27355         
27356         this.progressDialog = new Roo.bootstrap.Modal({
27357             cls : 'roo-document-manager-progress-dialog',
27358             allow_close : false,
27359             title : '',
27360             buttons : [
27361                 {
27362                     name  :'cancel',
27363                     weight : 'danger',
27364                     html : 'Cancel'
27365                 }
27366             ], 
27367             listeners : { 
27368                 btnclick : function() {
27369                     _this.uploadCancel();
27370                     this.hide();
27371                 }
27372             }
27373         });
27374          
27375         this.progressDialog.render(Roo.get(document.body));
27376          
27377         this.progress = new Roo.bootstrap.Progress({
27378             cls : 'roo-document-manager-progress',
27379             active : true,
27380             striped : true
27381         });
27382         
27383         this.progress.render(this.progressDialog.getChildContainer());
27384         
27385         this.progressBar = new Roo.bootstrap.ProgressBar({
27386             cls : 'roo-document-manager-progress-bar',
27387             aria_valuenow : 0,
27388             aria_valuemin : 0,
27389             aria_valuemax : 12,
27390             panel : 'success'
27391         });
27392         
27393         this.progressBar.render(this.progress.getChildContainer());
27394     },
27395     
27396     onUploaderClick : function(e)
27397     {
27398         e.preventDefault();
27399      
27400         if(this.fireEvent('beforeselectfile', this) != false){
27401             this.selectorEl.dom.click();
27402         }
27403         
27404     },
27405     
27406     onFileSelected : function(e)
27407     {
27408         e.preventDefault();
27409         
27410         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27411             return;
27412         }
27413         
27414         Roo.each(this.selectorEl.dom.files, function(file){
27415             if(this.fireEvent('inspect', this, file) != false){
27416                 this.files.push(file);
27417             }
27418         }, this);
27419         
27420         this.queue();
27421         
27422     },
27423     
27424     queue : function()
27425     {
27426         this.selectorEl.dom.value = '';
27427         
27428         if(!this.files.length){
27429             return;
27430         }
27431         
27432         if(this.boxes > 0 && this.files.length > this.boxes){
27433             this.files = this.files.slice(0, this.boxes);
27434         }
27435         
27436         this.uploader.show();
27437         
27438         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27439             this.uploader.hide();
27440         }
27441         
27442         var _this = this;
27443         
27444         var files = [];
27445         
27446         var docs = [];
27447         
27448         Roo.each(this.files, function(file){
27449             
27450             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27451                 var f = this.renderPreview(file);
27452                 files.push(f);
27453                 return;
27454             }
27455             
27456             if(file.type.indexOf('image') != -1){
27457                 this.delegates.push(
27458                     (function(){
27459                         _this.process(file);
27460                     }).createDelegate(this)
27461                 );
27462         
27463                 return;
27464             }
27465             
27466             docs.push(
27467                 (function(){
27468                     _this.process(file);
27469                 }).createDelegate(this)
27470             );
27471             
27472         }, this);
27473         
27474         this.files = files;
27475         
27476         this.delegates = this.delegates.concat(docs);
27477         
27478         if(!this.delegates.length){
27479             this.refresh();
27480             return;
27481         }
27482         
27483         this.progressBar.aria_valuemax = this.delegates.length;
27484         
27485         this.arrange();
27486         
27487         return;
27488     },
27489     
27490     arrange : function()
27491     {
27492         if(!this.delegates.length){
27493             this.progressDialog.hide();
27494             this.refresh();
27495             return;
27496         }
27497         
27498         var delegate = this.delegates.shift();
27499         
27500         this.progressDialog.show();
27501         
27502         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27503         
27504         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27505         
27506         delegate();
27507     },
27508     
27509     refresh : function()
27510     {
27511         this.uploader.show();
27512         
27513         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27514             this.uploader.hide();
27515         }
27516         
27517         Roo.isTouch ? this.closable(false) : this.closable(true);
27518         
27519         this.fireEvent('refresh', this);
27520     },
27521     
27522     onRemove : function(e, el, o)
27523     {
27524         e.preventDefault();
27525         
27526         this.fireEvent('remove', this, o);
27527         
27528     },
27529     
27530     remove : function(o)
27531     {
27532         var files = [];
27533         
27534         Roo.each(this.files, function(file){
27535             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27536                 files.push(file);
27537                 return;
27538             }
27539
27540             o.target.remove();
27541
27542         }, this);
27543         
27544         this.files = files;
27545         
27546         this.refresh();
27547     },
27548     
27549     clear : function()
27550     {
27551         Roo.each(this.files, function(file){
27552             if(!file.target){
27553                 return;
27554             }
27555             
27556             file.target.remove();
27557
27558         }, this);
27559         
27560         this.files = [];
27561         
27562         this.refresh();
27563     },
27564     
27565     onClick : function(e, el, o)
27566     {
27567         e.preventDefault();
27568         
27569         this.fireEvent('click', this, o);
27570         
27571     },
27572     
27573     closable : function(closable)
27574     {
27575         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27576             
27577             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27578             
27579             if(closable){
27580                 el.show();
27581                 return;
27582             }
27583             
27584             el.hide();
27585             
27586         }, this);
27587     },
27588     
27589     xhrOnLoad : function(xhr)
27590     {
27591         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27592             el.remove();
27593         }, this);
27594         
27595         if (xhr.readyState !== 4) {
27596             this.arrange();
27597             this.fireEvent('exception', this, xhr);
27598             return;
27599         }
27600
27601         var response = Roo.decode(xhr.responseText);
27602         
27603         if(!response.success){
27604             this.arrange();
27605             this.fireEvent('exception', this, xhr);
27606             return;
27607         }
27608         
27609         var file = this.renderPreview(response.data);
27610         
27611         this.files.push(file);
27612         
27613         this.arrange();
27614         
27615     },
27616     
27617     xhrOnError : function(xhr)
27618     {
27619         Roo.log('xhr on error');
27620         
27621         var response = Roo.decode(xhr.responseText);
27622           
27623         Roo.log(response);
27624         
27625         this.arrange();
27626     },
27627     
27628     process : function(file)
27629     {
27630         if(this.fireEvent('process', this, file) !== false){
27631             if(this.editable && file.type.indexOf('image') != -1){
27632                 this.fireEvent('edit', this, file);
27633                 return;
27634             }
27635
27636             this.uploadStart(file, false);
27637
27638             return;
27639         }
27640         
27641     },
27642     
27643     uploadStart : function(file, crop)
27644     {
27645         this.xhr = new XMLHttpRequest();
27646         
27647         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27648             this.arrange();
27649             return;
27650         }
27651         
27652         file.xhr = this.xhr;
27653             
27654         this.managerEl.createChild({
27655             tag : 'div',
27656             cls : 'roo-document-manager-loading',
27657             cn : [
27658                 {
27659                     tag : 'div',
27660                     tooltip : file.name,
27661                     cls : 'roo-document-manager-thumb',
27662                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27663                 }
27664             ]
27665
27666         });
27667
27668         this.xhr.open(this.method, this.url, true);
27669         
27670         var headers = {
27671             "Accept": "application/json",
27672             "Cache-Control": "no-cache",
27673             "X-Requested-With": "XMLHttpRequest"
27674         };
27675         
27676         for (var headerName in headers) {
27677             var headerValue = headers[headerName];
27678             if (headerValue) {
27679                 this.xhr.setRequestHeader(headerName, headerValue);
27680             }
27681         }
27682         
27683         var _this = this;
27684         
27685         this.xhr.onload = function()
27686         {
27687             _this.xhrOnLoad(_this.xhr);
27688         }
27689         
27690         this.xhr.onerror = function()
27691         {
27692             _this.xhrOnError(_this.xhr);
27693         }
27694         
27695         var formData = new FormData();
27696
27697         formData.append('returnHTML', 'NO');
27698         
27699         if(crop){
27700             formData.append('crop', crop);
27701         }
27702         
27703         formData.append(this.paramName, file, file.name);
27704         
27705         if(this.fireEvent('prepare', this, formData) != false){
27706             this.xhr.send(formData);
27707         };
27708     },
27709     
27710     uploadCancel : function()
27711     {
27712         if (this.xhr) {
27713             this.xhr.abort();
27714         }
27715         
27716         
27717         this.delegates = [];
27718         
27719         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27720             el.remove();
27721         }, this);
27722         
27723         this.arrange();
27724     },
27725     
27726     renderPreview : function(file)
27727     {
27728         if(typeof(file.target) != 'undefined' && file.target){
27729             return file;
27730         }
27731         
27732         var previewEl = this.managerEl.createChild({
27733             tag : 'div',
27734             cls : 'roo-document-manager-preview',
27735             cn : [
27736                 {
27737                     tag : 'div',
27738                     tooltip : file.filename,
27739                     cls : 'roo-document-manager-thumb',
27740                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27741                 },
27742                 {
27743                     tag : 'button',
27744                     cls : 'close',
27745                     html : '<i class="fa fa-times-circle"></i>'
27746                 }
27747             ]
27748         });
27749
27750         var close = previewEl.select('button.close', true).first();
27751
27752         close.on('click', this.onRemove, this, file);
27753
27754         file.target = previewEl;
27755
27756         var image = previewEl.select('img', true).first();
27757         
27758         var _this = this;
27759         
27760         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27761         
27762         image.on('click', this.onClick, this, file);
27763         
27764         return file;
27765         
27766     },
27767     
27768     onPreviewLoad : function(file, image)
27769     {
27770         if(typeof(file.target) == 'undefined' || !file.target){
27771             return;
27772         }
27773         
27774         var width = image.dom.naturalWidth || image.dom.width;
27775         var height = image.dom.naturalHeight || image.dom.height;
27776         
27777         if(width > height){
27778             file.target.addClass('wide');
27779             return;
27780         }
27781         
27782         file.target.addClass('tall');
27783         return;
27784         
27785     },
27786     
27787     uploadFromSource : function(file, crop)
27788     {
27789         this.xhr = new XMLHttpRequest();
27790         
27791         this.managerEl.createChild({
27792             tag : 'div',
27793             cls : 'roo-document-manager-loading',
27794             cn : [
27795                 {
27796                     tag : 'div',
27797                     tooltip : file.name,
27798                     cls : 'roo-document-manager-thumb',
27799                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27800                 }
27801             ]
27802
27803         });
27804
27805         this.xhr.open(this.method, this.url, true);
27806         
27807         var headers = {
27808             "Accept": "application/json",
27809             "Cache-Control": "no-cache",
27810             "X-Requested-With": "XMLHttpRequest"
27811         };
27812         
27813         for (var headerName in headers) {
27814             var headerValue = headers[headerName];
27815             if (headerValue) {
27816                 this.xhr.setRequestHeader(headerName, headerValue);
27817             }
27818         }
27819         
27820         var _this = this;
27821         
27822         this.xhr.onload = function()
27823         {
27824             _this.xhrOnLoad(_this.xhr);
27825         }
27826         
27827         this.xhr.onerror = function()
27828         {
27829             _this.xhrOnError(_this.xhr);
27830         }
27831         
27832         var formData = new FormData();
27833
27834         formData.append('returnHTML', 'NO');
27835         
27836         formData.append('crop', crop);
27837         
27838         if(typeof(file.filename) != 'undefined'){
27839             formData.append('filename', file.filename);
27840         }
27841         
27842         if(typeof(file.mimetype) != 'undefined'){
27843             formData.append('mimetype', file.mimetype);
27844         }
27845         
27846         if(this.fireEvent('prepare', this, formData) != false){
27847             this.xhr.send(formData);
27848         };
27849     }
27850 });
27851
27852 /*
27853 * Licence: LGPL
27854 */
27855
27856 /**
27857  * @class Roo.bootstrap.DocumentViewer
27858  * @extends Roo.bootstrap.Component
27859  * Bootstrap DocumentViewer class
27860  * 
27861  * @constructor
27862  * Create a new DocumentViewer
27863  * @param {Object} config The config object
27864  */
27865
27866 Roo.bootstrap.DocumentViewer = function(config){
27867     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27868     
27869     this.addEvents({
27870         /**
27871          * @event initial
27872          * Fire after initEvent
27873          * @param {Roo.bootstrap.DocumentViewer} this
27874          */
27875         "initial" : true,
27876         /**
27877          * @event click
27878          * Fire after click
27879          * @param {Roo.bootstrap.DocumentViewer} this
27880          */
27881         "click" : true,
27882         /**
27883          * @event trash
27884          * Fire after trash button
27885          * @param {Roo.bootstrap.DocumentViewer} this
27886          */
27887         "trash" : true
27888         
27889     });
27890 };
27891
27892 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27893     
27894     getAutoCreate : function()
27895     {
27896         var cfg = {
27897             tag : 'div',
27898             cls : 'roo-document-viewer',
27899             cn : [
27900                 {
27901                     tag : 'div',
27902                     cls : 'roo-document-viewer-body',
27903                     cn : [
27904                         {
27905                             tag : 'div',
27906                             cls : 'roo-document-viewer-thumb',
27907                             cn : [
27908                                 {
27909                                     tag : 'img',
27910                                     cls : 'roo-document-viewer-image'
27911                                 }
27912                             ]
27913                         }
27914                     ]
27915                 },
27916                 {
27917                     tag : 'div',
27918                     cls : 'roo-document-viewer-footer',
27919                     cn : {
27920                         tag : 'div',
27921                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27922                         cn : [
27923                             {
27924                                 tag : 'div',
27925                                 cls : 'btn-group',
27926                                 cn : [
27927                                     {
27928                                         tag : 'button',
27929                                         cls : 'btn btn-default roo-document-viewer-trash',
27930                                         html : '<i class="fa fa-trash"></i>'
27931                                     }
27932                                 ]
27933                             }
27934                         ]
27935                     }
27936                 }
27937             ]
27938         };
27939         
27940         return cfg;
27941     },
27942     
27943     initEvents : function()
27944     {
27945         
27946         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27947         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27948         
27949         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27950         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27951         
27952         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27953         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27954         
27955         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27956         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27957         
27958         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27959         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27960         
27961         this.bodyEl.on('click', this.onClick, this);
27962         
27963         this.trashBtn.on('click', this.onTrash, this);
27964         
27965     },
27966     
27967     initial : function()
27968     {
27969 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27970         
27971         
27972         this.fireEvent('initial', this);
27973         
27974     },
27975     
27976     onClick : function(e)
27977     {
27978         e.preventDefault();
27979         
27980         this.fireEvent('click', this);
27981     },
27982     
27983     onTrash : function(e)
27984     {
27985         e.preventDefault();
27986         
27987         this.fireEvent('trash', this);
27988     }
27989     
27990 });
27991 /*
27992  * - LGPL
27993  *
27994  * nav progress bar
27995  * 
27996  */
27997
27998 /**
27999  * @class Roo.bootstrap.NavProgressBar
28000  * @extends Roo.bootstrap.Component
28001  * Bootstrap NavProgressBar class
28002  * 
28003  * @constructor
28004  * Create a new nav progress bar
28005  * @param {Object} config The config object
28006  */
28007
28008 Roo.bootstrap.NavProgressBar = function(config){
28009     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28010
28011     this.bullets = this.bullets || [];
28012    
28013 //    Roo.bootstrap.NavProgressBar.register(this);
28014      this.addEvents({
28015         /**
28016              * @event changed
28017              * Fires when the active item changes
28018              * @param {Roo.bootstrap.NavProgressBar} this
28019              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28020              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28021          */
28022         'changed': true
28023      });
28024     
28025 };
28026
28027 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28028     
28029     bullets : [],
28030     barItems : [],
28031     
28032     getAutoCreate : function()
28033     {
28034         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28035         
28036         cfg = {
28037             tag : 'div',
28038             cls : 'roo-navigation-bar-group',
28039             cn : [
28040                 {
28041                     tag : 'div',
28042                     cls : 'roo-navigation-top-bar'
28043                 },
28044                 {
28045                     tag : 'div',
28046                     cls : 'roo-navigation-bullets-bar',
28047                     cn : [
28048                         {
28049                             tag : 'ul',
28050                             cls : 'roo-navigation-bar'
28051                         }
28052                     ]
28053                 },
28054                 
28055                 {
28056                     tag : 'div',
28057                     cls : 'roo-navigation-bottom-bar'
28058                 }
28059             ]
28060             
28061         };
28062         
28063         return cfg;
28064         
28065     },
28066     
28067     initEvents: function() 
28068     {
28069         
28070     },
28071     
28072     onRender : function(ct, position) 
28073     {
28074         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28075         
28076         if(this.bullets.length){
28077             Roo.each(this.bullets, function(b){
28078                this.addItem(b);
28079             }, this);
28080         }
28081         
28082         this.format();
28083         
28084     },
28085     
28086     addItem : function(cfg)
28087     {
28088         var item = new Roo.bootstrap.NavProgressItem(cfg);
28089         
28090         item.parentId = this.id;
28091         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28092         
28093         if(cfg.html){
28094             var top = new Roo.bootstrap.Element({
28095                 tag : 'div',
28096                 cls : 'roo-navigation-bar-text'
28097             });
28098             
28099             var bottom = new Roo.bootstrap.Element({
28100                 tag : 'div',
28101                 cls : 'roo-navigation-bar-text'
28102             });
28103             
28104             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28105             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28106             
28107             var topText = new Roo.bootstrap.Element({
28108                 tag : 'span',
28109                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28110             });
28111             
28112             var bottomText = new Roo.bootstrap.Element({
28113                 tag : 'span',
28114                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28115             });
28116             
28117             topText.onRender(top.el, null);
28118             bottomText.onRender(bottom.el, null);
28119             
28120             item.topEl = top;
28121             item.bottomEl = bottom;
28122         }
28123         
28124         this.barItems.push(item);
28125         
28126         return item;
28127     },
28128     
28129     getActive : function()
28130     {
28131         var active = false;
28132         
28133         Roo.each(this.barItems, function(v){
28134             
28135             if (!v.isActive()) {
28136                 return;
28137             }
28138             
28139             active = v;
28140             return false;
28141             
28142         });
28143         
28144         return active;
28145     },
28146     
28147     setActiveItem : function(item)
28148     {
28149         var prev = false;
28150         
28151         Roo.each(this.barItems, function(v){
28152             if (v.rid == item.rid) {
28153                 return ;
28154             }
28155             
28156             if (v.isActive()) {
28157                 v.setActive(false);
28158                 prev = v;
28159             }
28160         });
28161
28162         item.setActive(true);
28163         
28164         this.fireEvent('changed', this, item, prev);
28165     },
28166     
28167     getBarItem: function(rid)
28168     {
28169         var ret = false;
28170         
28171         Roo.each(this.barItems, function(e) {
28172             if (e.rid != rid) {
28173                 return;
28174             }
28175             
28176             ret =  e;
28177             return false;
28178         });
28179         
28180         return ret;
28181     },
28182     
28183     indexOfItem : function(item)
28184     {
28185         var index = false;
28186         
28187         Roo.each(this.barItems, function(v, i){
28188             
28189             if (v.rid != item.rid) {
28190                 return;
28191             }
28192             
28193             index = i;
28194             return false
28195         });
28196         
28197         return index;
28198     },
28199     
28200     setActiveNext : function()
28201     {
28202         var i = this.indexOfItem(this.getActive());
28203         
28204         if (i > this.barItems.length) {
28205             return;
28206         }
28207         
28208         this.setActiveItem(this.barItems[i+1]);
28209     },
28210     
28211     setActivePrev : function()
28212     {
28213         var i = this.indexOfItem(this.getActive());
28214         
28215         if (i  < 1) {
28216             return;
28217         }
28218         
28219         this.setActiveItem(this.barItems[i-1]);
28220     },
28221     
28222     format : function()
28223     {
28224         if(!this.barItems.length){
28225             return;
28226         }
28227      
28228         var width = 100 / this.barItems.length;
28229         
28230         Roo.each(this.barItems, function(i){
28231             i.el.setStyle('width', width + '%');
28232             i.topEl.el.setStyle('width', width + '%');
28233             i.bottomEl.el.setStyle('width', width + '%');
28234         }, this);
28235         
28236     }
28237     
28238 });
28239 /*
28240  * - LGPL
28241  *
28242  * Nav Progress Item
28243  * 
28244  */
28245
28246 /**
28247  * @class Roo.bootstrap.NavProgressItem
28248  * @extends Roo.bootstrap.Component
28249  * Bootstrap NavProgressItem class
28250  * @cfg {String} rid the reference id
28251  * @cfg {Boolean} active (true|false) Is item active default false
28252  * @cfg {Boolean} disabled (true|false) Is item active default false
28253  * @cfg {String} html
28254  * @cfg {String} position (top|bottom) text position default bottom
28255  * @cfg {String} icon show icon instead of number
28256  * 
28257  * @constructor
28258  * Create a new NavProgressItem
28259  * @param {Object} config The config object
28260  */
28261 Roo.bootstrap.NavProgressItem = function(config){
28262     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28263     this.addEvents({
28264         // raw events
28265         /**
28266          * @event click
28267          * The raw click event for the entire grid.
28268          * @param {Roo.bootstrap.NavProgressItem} this
28269          * @param {Roo.EventObject} e
28270          */
28271         "click" : true
28272     });
28273    
28274 };
28275
28276 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28277     
28278     rid : '',
28279     active : false,
28280     disabled : false,
28281     html : '',
28282     position : 'bottom',
28283     icon : false,
28284     
28285     getAutoCreate : function()
28286     {
28287         var iconCls = 'roo-navigation-bar-item-icon';
28288         
28289         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28290         
28291         var cfg = {
28292             tag: 'li',
28293             cls: 'roo-navigation-bar-item',
28294             cn : [
28295                 {
28296                     tag : 'i',
28297                     cls : iconCls
28298                 }
28299             ]
28300         };
28301         
28302         if(this.active){
28303             cfg.cls += ' active';
28304         }
28305         if(this.disabled){
28306             cfg.cls += ' disabled';
28307         }
28308         
28309         return cfg;
28310     },
28311     
28312     disable : function()
28313     {
28314         this.setDisabled(true);
28315     },
28316     
28317     enable : function()
28318     {
28319         this.setDisabled(false);
28320     },
28321     
28322     initEvents: function() 
28323     {
28324         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28325         
28326         this.iconEl.on('click', this.onClick, this);
28327     },
28328     
28329     onClick : function(e)
28330     {
28331         e.preventDefault();
28332         
28333         if(this.disabled){
28334             return;
28335         }
28336         
28337         if(this.fireEvent('click', this, e) === false){
28338             return;
28339         };
28340         
28341         this.parent().setActiveItem(this);
28342     },
28343     
28344     isActive: function () 
28345     {
28346         return this.active;
28347     },
28348     
28349     setActive : function(state)
28350     {
28351         if(this.active == state){
28352             return;
28353         }
28354         
28355         this.active = state;
28356         
28357         if (state) {
28358             this.el.addClass('active');
28359             return;
28360         }
28361         
28362         this.el.removeClass('active');
28363         
28364         return;
28365     },
28366     
28367     setDisabled : function(state)
28368     {
28369         if(this.disabled == state){
28370             return;
28371         }
28372         
28373         this.disabled = state;
28374         
28375         if (state) {
28376             this.el.addClass('disabled');
28377             return;
28378         }
28379         
28380         this.el.removeClass('disabled');
28381     },
28382     
28383     tooltipEl : function()
28384     {
28385         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28386     }
28387 });
28388  
28389
28390  /*
28391  * - LGPL
28392  *
28393  * FieldLabel
28394  * 
28395  */
28396
28397 /**
28398  * @class Roo.bootstrap.FieldLabel
28399  * @extends Roo.bootstrap.Component
28400  * Bootstrap FieldLabel class
28401  * @cfg {String} html contents of the element
28402  * @cfg {String} tag tag of the element default label
28403  * @cfg {String} cls class of the element
28404  * @cfg {String} target label target 
28405  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28406  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28407  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28408  * @cfg {String} iconTooltip default "This field is required"
28409  * 
28410  * @constructor
28411  * Create a new FieldLabel
28412  * @param {Object} config The config object
28413  */
28414
28415 Roo.bootstrap.FieldLabel = function(config){
28416     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28417     
28418     this.addEvents({
28419             /**
28420              * @event invalid
28421              * Fires after the field has been marked as invalid.
28422              * @param {Roo.form.FieldLabel} this
28423              * @param {String} msg The validation message
28424              */
28425             invalid : true,
28426             /**
28427              * @event valid
28428              * Fires after the field has been validated with no errors.
28429              * @param {Roo.form.FieldLabel} this
28430              */
28431             valid : true
28432         });
28433 };
28434
28435 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28436     
28437     tag: 'label',
28438     cls: '',
28439     html: '',
28440     target: '',
28441     allowBlank : true,
28442     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28443     validClass : 'text-success fa fa-lg fa-check',
28444     iconTooltip : 'This field is required',
28445     
28446     getAutoCreate : function(){
28447         
28448         var cfg = {
28449             tag : this.tag,
28450             cls : 'roo-bootstrap-field-label ' + this.cls,
28451             for : this.target,
28452             cn : [
28453                 {
28454                     tag : 'i',
28455                     cls : '',
28456                     tooltip : this.iconTooltip
28457                 },
28458                 {
28459                     tag : 'span',
28460                     html : this.html
28461                 }
28462             ] 
28463         };
28464         
28465         return cfg;
28466     },
28467     
28468     initEvents: function() 
28469     {
28470         Roo.bootstrap.Element.superclass.initEvents.call(this);
28471         
28472         this.iconEl = this.el.select('i', true).first();
28473         
28474         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28475         
28476         Roo.bootstrap.FieldLabel.register(this);
28477     },
28478     
28479     /**
28480      * Mark this field as valid
28481      */
28482     markValid : function()
28483     {
28484         this.iconEl.show();
28485         
28486         this.iconEl.removeClass(this.invalidClass);
28487         
28488         this.iconEl.addClass(this.validClass);
28489         
28490         this.fireEvent('valid', this);
28491     },
28492     
28493     /**
28494      * Mark this field as invalid
28495      * @param {String} msg The validation message
28496      */
28497     markInvalid : function(msg)
28498     {
28499         this.iconEl.show();
28500         
28501         this.iconEl.removeClass(this.validClass);
28502         
28503         this.iconEl.addClass(this.invalidClass);
28504         
28505         this.fireEvent('invalid', this, msg);
28506     }
28507     
28508    
28509 });
28510
28511 Roo.apply(Roo.bootstrap.FieldLabel, {
28512     
28513     groups: {},
28514     
28515      /**
28516     * register a FieldLabel Group
28517     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28518     */
28519     register : function(label)
28520     {
28521         if(this.groups.hasOwnProperty(label.target)){
28522             return;
28523         }
28524      
28525         this.groups[label.target] = label;
28526         
28527     },
28528     /**
28529     * fetch a FieldLabel Group based on the target
28530     * @param {string} target
28531     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28532     */
28533     get: function(target) {
28534         if (typeof(this.groups[target]) == 'undefined') {
28535             return false;
28536         }
28537         
28538         return this.groups[target] ;
28539     }
28540 });
28541
28542  
28543
28544  /*
28545  * - LGPL
28546  *
28547  * page DateSplitField.
28548  * 
28549  */
28550
28551
28552 /**
28553  * @class Roo.bootstrap.DateSplitField
28554  * @extends Roo.bootstrap.Component
28555  * Bootstrap DateSplitField class
28556  * @cfg {string} fieldLabel - the label associated
28557  * @cfg {Number} labelWidth set the width of label (0-12)
28558  * @cfg {String} labelAlign (top|left)
28559  * @cfg {Boolean} dayAllowBlank (true|false) default false
28560  * @cfg {Boolean} monthAllowBlank (true|false) default false
28561  * @cfg {Boolean} yearAllowBlank (true|false) default false
28562  * @cfg {string} dayPlaceholder 
28563  * @cfg {string} monthPlaceholder
28564  * @cfg {string} yearPlaceholder
28565  * @cfg {string} dayFormat default 'd'
28566  * @cfg {string} monthFormat default 'm'
28567  * @cfg {string} yearFormat default 'Y'
28568
28569  *     
28570  * @constructor
28571  * Create a new DateSplitField
28572  * @param {Object} config The config object
28573  */
28574
28575 Roo.bootstrap.DateSplitField = function(config){
28576     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28577     
28578     this.addEvents({
28579         // raw events
28580          /**
28581          * @event years
28582          * getting the data of years
28583          * @param {Roo.bootstrap.DateSplitField} this
28584          * @param {Object} years
28585          */
28586         "years" : true,
28587         /**
28588          * @event days
28589          * getting the data of days
28590          * @param {Roo.bootstrap.DateSplitField} this
28591          * @param {Object} days
28592          */
28593         "days" : true,
28594         /**
28595          * @event invalid
28596          * Fires after the field has been marked as invalid.
28597          * @param {Roo.form.Field} this
28598          * @param {String} msg The validation message
28599          */
28600         invalid : true,
28601        /**
28602          * @event valid
28603          * Fires after the field has been validated with no errors.
28604          * @param {Roo.form.Field} this
28605          */
28606         valid : true
28607     });
28608 };
28609
28610 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28611     
28612     fieldLabel : '',
28613     labelAlign : 'top',
28614     labelWidth : 3,
28615     dayAllowBlank : false,
28616     monthAllowBlank : false,
28617     yearAllowBlank : false,
28618     dayPlaceholder : '',
28619     monthPlaceholder : '',
28620     yearPlaceholder : '',
28621     dayFormat : 'd',
28622     monthFormat : 'm',
28623     yearFormat : 'Y',
28624     isFormField : true,
28625     
28626     getAutoCreate : function()
28627     {
28628         var cfg = {
28629             tag : 'div',
28630             cls : 'row roo-date-split-field-group',
28631             cn : [
28632                 {
28633                     tag : 'input',
28634                     type : 'hidden',
28635                     cls : 'form-hidden-field roo-date-split-field-group-value',
28636                     name : this.name
28637                 }
28638             ]
28639         };
28640         
28641         if(this.fieldLabel){
28642             cfg.cn.push({
28643                 tag : 'div',
28644                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28645                 cn : [
28646                     {
28647                         tag : 'label',
28648                         html : this.fieldLabel
28649                     }
28650                 ]
28651             });
28652         }
28653         
28654         Roo.each(['day', 'month', 'year'], function(t){
28655             cfg.cn.push({
28656                 tag : 'div',
28657                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28658             });
28659         }, this);
28660         
28661         return cfg;
28662     },
28663     
28664     inputEl: function ()
28665     {
28666         return this.el.select('.roo-date-split-field-group-value', true).first();
28667     },
28668     
28669     onRender : function(ct, position) 
28670     {
28671         var _this = this;
28672         
28673         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28674         
28675         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28676         
28677         this.dayField = new Roo.bootstrap.ComboBox({
28678             allowBlank : this.dayAllowBlank,
28679             alwaysQuery : true,
28680             displayField : 'value',
28681             editable : false,
28682             fieldLabel : '',
28683             forceSelection : true,
28684             mode : 'local',
28685             placeholder : this.dayPlaceholder,
28686             selectOnFocus : true,
28687             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28688             triggerAction : 'all',
28689             typeAhead : true,
28690             valueField : 'value',
28691             store : new Roo.data.SimpleStore({
28692                 data : (function() {    
28693                     var days = [];
28694                     _this.fireEvent('days', _this, days);
28695                     return days;
28696                 })(),
28697                 fields : [ 'value' ]
28698             }),
28699             listeners : {
28700                 select : function (_self, record, index)
28701                 {
28702                     _this.setValue(_this.getValue());
28703                 }
28704             }
28705         });
28706
28707         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28708         
28709         this.monthField = new Roo.bootstrap.MonthField({
28710             after : '<i class=\"fa fa-calendar\"></i>',
28711             allowBlank : this.monthAllowBlank,
28712             placeholder : this.monthPlaceholder,
28713             readOnly : true,
28714             listeners : {
28715                 render : function (_self)
28716                 {
28717                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28718                         e.preventDefault();
28719                         _self.focus();
28720                     });
28721                 },
28722                 select : function (_self, oldvalue, newvalue)
28723                 {
28724                     _this.setValue(_this.getValue());
28725                 }
28726             }
28727         });
28728         
28729         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28730         
28731         this.yearField = new Roo.bootstrap.ComboBox({
28732             allowBlank : this.yearAllowBlank,
28733             alwaysQuery : true,
28734             displayField : 'value',
28735             editable : false,
28736             fieldLabel : '',
28737             forceSelection : true,
28738             mode : 'local',
28739             placeholder : this.yearPlaceholder,
28740             selectOnFocus : true,
28741             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28742             triggerAction : 'all',
28743             typeAhead : true,
28744             valueField : 'value',
28745             store : new Roo.data.SimpleStore({
28746                 data : (function() {
28747                     var years = [];
28748                     _this.fireEvent('years', _this, years);
28749                     return years;
28750                 })(),
28751                 fields : [ 'value' ]
28752             }),
28753             listeners : {
28754                 select : function (_self, record, index)
28755                 {
28756                     _this.setValue(_this.getValue());
28757                 }
28758             }
28759         });
28760
28761         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28762     },
28763     
28764     setValue : function(v, format)
28765     {
28766         this.inputEl.dom.value = v;
28767         
28768         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28769         
28770         var d = Date.parseDate(v, f);
28771         
28772         if(!d){
28773             this.validate();
28774             return;
28775         }
28776         
28777         this.setDay(d.format(this.dayFormat));
28778         this.setMonth(d.format(this.monthFormat));
28779         this.setYear(d.format(this.yearFormat));
28780         
28781         this.validate();
28782         
28783         return;
28784     },
28785     
28786     setDay : function(v)
28787     {
28788         this.dayField.setValue(v);
28789         this.inputEl.dom.value = this.getValue();
28790         this.validate();
28791         return;
28792     },
28793     
28794     setMonth : function(v)
28795     {
28796         this.monthField.setValue(v, true);
28797         this.inputEl.dom.value = this.getValue();
28798         this.validate();
28799         return;
28800     },
28801     
28802     setYear : function(v)
28803     {
28804         this.yearField.setValue(v);
28805         this.inputEl.dom.value = this.getValue();
28806         this.validate();
28807         return;
28808     },
28809     
28810     getDay : function()
28811     {
28812         return this.dayField.getValue();
28813     },
28814     
28815     getMonth : function()
28816     {
28817         return this.monthField.getValue();
28818     },
28819     
28820     getYear : function()
28821     {
28822         return this.yearField.getValue();
28823     },
28824     
28825     getValue : function()
28826     {
28827         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28828         
28829         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28830         
28831         return date;
28832     },
28833     
28834     reset : function()
28835     {
28836         this.setDay('');
28837         this.setMonth('');
28838         this.setYear('');
28839         this.inputEl.dom.value = '';
28840         this.validate();
28841         return;
28842     },
28843     
28844     validate : function()
28845     {
28846         var d = this.dayField.validate();
28847         var m = this.monthField.validate();
28848         var y = this.yearField.validate();
28849         
28850         var valid = true;
28851         
28852         if(
28853                 (!this.dayAllowBlank && !d) ||
28854                 (!this.monthAllowBlank && !m) ||
28855                 (!this.yearAllowBlank && !y)
28856         ){
28857             valid = false;
28858         }
28859         
28860         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28861             return valid;
28862         }
28863         
28864         if(valid){
28865             this.markValid();
28866             return valid;
28867         }
28868         
28869         this.markInvalid();
28870         
28871         return valid;
28872     },
28873     
28874     markValid : function()
28875     {
28876         
28877         var label = this.el.select('label', true).first();
28878         var icon = this.el.select('i.fa-star', true).first();
28879
28880         if(label && icon){
28881             icon.remove();
28882         }
28883         
28884         this.fireEvent('valid', this);
28885     },
28886     
28887      /**
28888      * Mark this field as invalid
28889      * @param {String} msg The validation message
28890      */
28891     markInvalid : function(msg)
28892     {
28893         
28894         var label = this.el.select('label', true).first();
28895         var icon = this.el.select('i.fa-star', true).first();
28896
28897         if(label && !icon){
28898             this.el.select('.roo-date-split-field-label', true).createChild({
28899                 tag : 'i',
28900                 cls : 'text-danger fa fa-lg fa-star',
28901                 tooltip : 'This field is required',
28902                 style : 'margin-right:5px;'
28903             }, label, true);
28904         }
28905         
28906         this.fireEvent('invalid', this, msg);
28907     },
28908     
28909     clearInvalid : function()
28910     {
28911         var label = this.el.select('label', true).first();
28912         var icon = this.el.select('i.fa-star', true).first();
28913
28914         if(label && icon){
28915             icon.remove();
28916         }
28917         
28918         this.fireEvent('valid', this);
28919     },
28920     
28921     getName: function()
28922     {
28923         return this.name;
28924     }
28925     
28926 });
28927
28928  /**
28929  *
28930  * This is based on 
28931  * http://masonry.desandro.com
28932  *
28933  * The idea is to render all the bricks based on vertical width...
28934  *
28935  * The original code extends 'outlayer' - we might need to use that....
28936  * 
28937  */
28938
28939
28940 /**
28941  * @class Roo.bootstrap.LayoutMasonry
28942  * @extends Roo.bootstrap.Component
28943  * Bootstrap Layout Masonry class
28944  * 
28945  * @constructor
28946  * Create a new Element
28947  * @param {Object} config The config object
28948  */
28949
28950 Roo.bootstrap.LayoutMasonry = function(config){
28951     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28952     
28953     this.bricks = [];
28954     
28955 };
28956
28957 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28958     
28959     /**
28960      * @cfg {Boolean} isLayoutInstant = no animation?
28961      */   
28962     isLayoutInstant : false, // needed?
28963    
28964     /**
28965      * @cfg {Number} boxWidth  width of the columns
28966      */   
28967     boxWidth : 450,
28968     
28969       /**
28970      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28971      */   
28972     boxHeight : 0,
28973     
28974     /**
28975      * @cfg {Number} padWidth padding below box..
28976      */   
28977     padWidth : 10, 
28978     
28979     /**
28980      * @cfg {Number} gutter gutter width..
28981      */   
28982     gutter : 10,
28983     
28984      /**
28985      * @cfg {Number} maxCols maximum number of columns
28986      */   
28987     
28988     maxCols: 0,
28989     
28990     /**
28991      * @cfg {Boolean} isAutoInitial defalut true
28992      */   
28993     isAutoInitial : true, 
28994     
28995     containerWidth: 0,
28996     
28997     /**
28998      * @cfg {Boolean} isHorizontal defalut false
28999      */   
29000     isHorizontal : false, 
29001
29002     currentSize : null,
29003     
29004     tag: 'div',
29005     
29006     cls: '',
29007     
29008     bricks: null, //CompositeElement
29009     
29010     cols : 1,
29011     
29012     _isLayoutInited : false,
29013     
29014 //    isAlternative : false, // only use for vertical layout...
29015     
29016     /**
29017      * @cfg {Number} alternativePadWidth padding below box..
29018      */   
29019     alternativePadWidth : 50, 
29020     
29021     getAutoCreate : function(){
29022         
29023         var cfg = {
29024             tag: this.tag,
29025             cls: 'blog-masonary-wrapper ' + this.cls,
29026             cn : {
29027                 cls : 'mas-boxes masonary'
29028             }
29029         };
29030         
29031         return cfg;
29032     },
29033     
29034     getChildContainer: function( )
29035     {
29036         if (this.boxesEl) {
29037             return this.boxesEl;
29038         }
29039         
29040         this.boxesEl = this.el.select('.mas-boxes').first();
29041         
29042         return this.boxesEl;
29043     },
29044     
29045     
29046     initEvents : function()
29047     {
29048         var _this = this;
29049         
29050         if(this.isAutoInitial){
29051             Roo.log('hook children rendered');
29052             this.on('childrenrendered', function() {
29053                 Roo.log('children rendered');
29054                 _this.initial();
29055             } ,this);
29056         }
29057     },
29058     
29059     initial : function()
29060     {
29061         this.currentSize = this.el.getBox(true);
29062         
29063         Roo.EventManager.onWindowResize(this.resize, this); 
29064
29065         if(!this.isAutoInitial){
29066             this.layout();
29067             return;
29068         }
29069         
29070         this.layout();
29071         
29072         return;
29073         //this.layout.defer(500,this);
29074         
29075     },
29076     
29077     resize : function()
29078     {
29079         Roo.log('resize');
29080         
29081         var cs = this.el.getBox(true);
29082         
29083         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29084             Roo.log("no change in with or X");
29085             return;
29086         }
29087         
29088         this.currentSize = cs;
29089         
29090         this.layout();
29091         
29092     },
29093     
29094     layout : function()
29095     {   
29096         this._resetLayout();
29097         
29098         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29099         
29100         this.layoutItems( isInstant );
29101       
29102         this._isLayoutInited = true;
29103         
29104     },
29105     
29106     _resetLayout : function()
29107     {
29108         if(this.isHorizontal){
29109             this.horizontalMeasureColumns();
29110             return;
29111         }
29112         
29113         this.verticalMeasureColumns();
29114         
29115     },
29116     
29117     verticalMeasureColumns : function()
29118     {
29119         this.getContainerWidth();
29120         
29121 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29122 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29123 //            return;
29124 //        }
29125         
29126         var boxWidth = this.boxWidth + this.padWidth;
29127         
29128         if(this.containerWidth < this.boxWidth){
29129             boxWidth = this.containerWidth
29130         }
29131         
29132         var containerWidth = this.containerWidth;
29133         
29134         var cols = Math.floor(containerWidth / boxWidth);
29135         
29136         this.cols = Math.max( cols, 1 );
29137         
29138         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29139         
29140         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29141         
29142         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29143         
29144         this.colWidth = boxWidth + avail - this.padWidth;
29145         
29146         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29147         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29148     },
29149     
29150     horizontalMeasureColumns : function()
29151     {
29152         this.getContainerWidth();
29153         
29154         var boxWidth = this.boxWidth;
29155         
29156         if(this.containerWidth < boxWidth){
29157             boxWidth = this.containerWidth;
29158         }
29159         
29160         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29161         
29162         this.el.setHeight(boxWidth);
29163         
29164     },
29165     
29166     getContainerWidth : function()
29167     {
29168         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29169     },
29170     
29171     layoutItems : function( isInstant )
29172     {
29173         var items = Roo.apply([], this.bricks);
29174         
29175         if(this.isHorizontal){
29176             this._horizontalLayoutItems( items , isInstant );
29177             return;
29178         }
29179         
29180 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29181 //            this._verticalAlternativeLayoutItems( items , isInstant );
29182 //            return;
29183 //        }
29184         
29185         this._verticalLayoutItems( items , isInstant );
29186         
29187     },
29188     
29189     _verticalLayoutItems : function ( items , isInstant)
29190     {
29191         if ( !items || !items.length ) {
29192             return;
29193         }
29194         
29195         var standard = [
29196             ['xs', 'xs', 'xs', 'tall'],
29197             ['xs', 'xs', 'tall'],
29198             ['xs', 'xs', 'sm'],
29199             ['xs', 'xs', 'xs'],
29200             ['xs', 'tall'],
29201             ['xs', 'sm'],
29202             ['xs', 'xs'],
29203             ['xs'],
29204             
29205             ['sm', 'xs', 'xs'],
29206             ['sm', 'xs'],
29207             ['sm'],
29208             
29209             ['tall', 'xs', 'xs', 'xs'],
29210             ['tall', 'xs', 'xs'],
29211             ['tall', 'xs'],
29212             ['tall']
29213             
29214         ];
29215         
29216         var queue = [];
29217         
29218         var boxes = [];
29219         
29220         var box = [];
29221         
29222         Roo.each(items, function(item, k){
29223             
29224             switch (item.size) {
29225                 // these layouts take up a full box,
29226                 case 'md' :
29227                 case 'md-left' :
29228                 case 'md-right' :
29229                 case 'wide' :
29230                     
29231                     if(box.length){
29232                         boxes.push(box);
29233                         box = [];
29234                     }
29235                     
29236                     boxes.push([item]);
29237                     
29238                     break;
29239                     
29240                 case 'xs' :
29241                 case 'sm' :
29242                 case 'tall' :
29243                     
29244                     box.push(item);
29245                     
29246                     break;
29247                 default :
29248                     break;
29249                     
29250             }
29251             
29252         }, this);
29253         
29254         if(box.length){
29255             boxes.push(box);
29256             box = [];
29257         }
29258         
29259         var filterPattern = function(box, length)
29260         {
29261             if(!box.length){
29262                 return;
29263             }
29264             
29265             var match = false;
29266             
29267             var pattern = box.slice(0, length);
29268             
29269             var format = [];
29270             
29271             Roo.each(pattern, function(i){
29272                 format.push(i.size);
29273             }, this);
29274             
29275             Roo.each(standard, function(s){
29276                 
29277                 if(String(s) != String(format)){
29278                     return;
29279                 }
29280                 
29281                 match = true;
29282                 return false;
29283                 
29284             }, this);
29285             
29286             if(!match && length == 1){
29287                 return;
29288             }
29289             
29290             if(!match){
29291                 filterPattern(box, length - 1);
29292                 return;
29293             }
29294                 
29295             queue.push(pattern);
29296
29297             box = box.slice(length, box.length);
29298
29299             filterPattern(box, 4);
29300
29301             return;
29302             
29303         }
29304         
29305         Roo.each(boxes, function(box, k){
29306             
29307             if(!box.length){
29308                 return;
29309             }
29310             
29311             if(box.length == 1){
29312                 queue.push(box);
29313                 return;
29314             }
29315             
29316             filterPattern(box, 4);
29317             
29318         }, this);
29319         
29320         this._processVerticalLayoutQueue( queue, isInstant );
29321         
29322     },
29323     
29324 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29325 //    {
29326 //        if ( !items || !items.length ) {
29327 //            return;
29328 //        }
29329 //
29330 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29331 //        
29332 //    },
29333     
29334     _horizontalLayoutItems : function ( items , isInstant)
29335     {
29336         if ( !items || !items.length || items.length < 3) {
29337             return;
29338         }
29339         
29340         items.reverse();
29341         
29342         var eItems = items.slice(0, 3);
29343         
29344         items = items.slice(3, items.length);
29345         
29346         var standard = [
29347             ['xs', 'xs', 'xs', 'wide'],
29348             ['xs', 'xs', 'wide'],
29349             ['xs', 'xs', 'sm'],
29350             ['xs', 'xs', 'xs'],
29351             ['xs', 'wide'],
29352             ['xs', 'sm'],
29353             ['xs', 'xs'],
29354             ['xs'],
29355             
29356             ['sm', 'xs', 'xs'],
29357             ['sm', 'xs'],
29358             ['sm'],
29359             
29360             ['wide', 'xs', 'xs', 'xs'],
29361             ['wide', 'xs', 'xs'],
29362             ['wide', 'xs'],
29363             ['wide'],
29364             
29365             ['wide-thin']
29366         ];
29367         
29368         var queue = [];
29369         
29370         var boxes = [];
29371         
29372         var box = [];
29373         
29374         Roo.each(items, function(item, k){
29375             
29376             switch (item.size) {
29377                 case 'md' :
29378                 case 'md-left' :
29379                 case 'md-right' :
29380                 case 'tall' :
29381                     
29382                     if(box.length){
29383                         boxes.push(box);
29384                         box = [];
29385                     }
29386                     
29387                     boxes.push([item]);
29388                     
29389                     break;
29390                     
29391                 case 'xs' :
29392                 case 'sm' :
29393                 case 'wide' :
29394                 case 'wide-thin' :
29395                     
29396                     box.push(item);
29397                     
29398                     break;
29399                 default :
29400                     break;
29401                     
29402             }
29403             
29404         }, this);
29405         
29406         if(box.length){
29407             boxes.push(box);
29408             box = [];
29409         }
29410         
29411         var filterPattern = function(box, length)
29412         {
29413             if(!box.length){
29414                 return;
29415             }
29416             
29417             var match = false;
29418             
29419             var pattern = box.slice(0, length);
29420             
29421             var format = [];
29422             
29423             Roo.each(pattern, function(i){
29424                 format.push(i.size);
29425             }, this);
29426             
29427             Roo.each(standard, function(s){
29428                 
29429                 if(String(s) != String(format)){
29430                     return;
29431                 }
29432                 
29433                 match = true;
29434                 return false;
29435                 
29436             }, this);
29437             
29438             if(!match && length == 1){
29439                 return;
29440             }
29441             
29442             if(!match){
29443                 filterPattern(box, length - 1);
29444                 return;
29445             }
29446                 
29447             queue.push(pattern);
29448
29449             box = box.slice(length, box.length);
29450
29451             filterPattern(box, 4);
29452
29453             return;
29454             
29455         }
29456         
29457         Roo.each(boxes, function(box, k){
29458             
29459             if(!box.length){
29460                 return;
29461             }
29462             
29463             if(box.length == 1){
29464                 queue.push(box);
29465                 return;
29466             }
29467             
29468             filterPattern(box, 4);
29469             
29470         }, this);
29471         
29472         
29473         var prune = [];
29474         
29475         var pos = this.el.getBox(true);
29476         
29477         var minX = pos.x;
29478         
29479         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29480         
29481         var hit_end = false;
29482         
29483         Roo.each(queue, function(box){
29484             
29485             if(hit_end){
29486                 
29487                 Roo.each(box, function(b){
29488                 
29489                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29490                     b.el.hide();
29491
29492                 }, this);
29493
29494                 return;
29495             }
29496             
29497             var mx = 0;
29498             
29499             Roo.each(box, function(b){
29500                 
29501                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29502                 b.el.show();
29503
29504                 mx = Math.max(mx, b.x);
29505                 
29506             }, this);
29507             
29508             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29509             
29510             if(maxX < minX){
29511                 
29512                 Roo.each(box, function(b){
29513                 
29514                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29515                     b.el.hide();
29516                     
29517                 }, this);
29518                 
29519                 hit_end = true;
29520                 
29521                 return;
29522             }
29523             
29524             prune.push(box);
29525             
29526         }, this);
29527         
29528         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29529     },
29530     
29531     /** Sets position of item in DOM
29532     * @param {Element} item
29533     * @param {Number} x - horizontal position
29534     * @param {Number} y - vertical position
29535     * @param {Boolean} isInstant - disables transitions
29536     */
29537     _processVerticalLayoutQueue : function( queue, isInstant )
29538     {
29539         var pos = this.el.getBox(true);
29540         var x = pos.x;
29541         var y = pos.y;
29542         var maxY = [];
29543         
29544         for (var i = 0; i < this.cols; i++){
29545             maxY[i] = pos.y;
29546         }
29547         
29548         Roo.each(queue, function(box, k){
29549             
29550             var col = k % this.cols;
29551             
29552             Roo.each(box, function(b,kk){
29553                 
29554                 b.el.position('absolute');
29555                 
29556                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29557                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29558                 
29559                 if(b.size == 'md-left' || b.size == 'md-right'){
29560                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29561                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29562                 }
29563                 
29564                 b.el.setWidth(width);
29565                 b.el.setHeight(height);
29566                 // iframe?
29567                 b.el.select('iframe',true).setSize(width,height);
29568                 
29569             }, this);
29570             
29571             for (var i = 0; i < this.cols; i++){
29572                 
29573                 if(maxY[i] < maxY[col]){
29574                     col = i;
29575                     continue;
29576                 }
29577                 
29578                 col = Math.min(col, i);
29579                 
29580             }
29581             
29582             x = pos.x + col * (this.colWidth + this.padWidth);
29583             
29584             y = maxY[col];
29585             
29586             var positions = [];
29587             
29588             switch (box.length){
29589                 case 1 :
29590                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29591                     break;
29592                 case 2 :
29593                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29594                     break;
29595                 case 3 :
29596                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29597                     break;
29598                 case 4 :
29599                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29600                     break;
29601                 default :
29602                     break;
29603             }
29604             
29605             Roo.each(box, function(b,kk){
29606                 
29607                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29608                 
29609                 var sz = b.el.getSize();
29610                 
29611                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29612                 
29613             }, this);
29614             
29615         }, this);
29616         
29617         var mY = 0;
29618         
29619         for (var i = 0; i < this.cols; i++){
29620             mY = Math.max(mY, maxY[i]);
29621         }
29622         
29623         this.el.setHeight(mY - pos.y);
29624         
29625     },
29626     
29627 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29628 //    {
29629 //        var pos = this.el.getBox(true);
29630 //        var x = pos.x;
29631 //        var y = pos.y;
29632 //        var maxX = pos.right;
29633 //        
29634 //        var maxHeight = 0;
29635 //        
29636 //        Roo.each(items, function(item, k){
29637 //            
29638 //            var c = k % 2;
29639 //            
29640 //            item.el.position('absolute');
29641 //                
29642 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29643 //
29644 //            item.el.setWidth(width);
29645 //
29646 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29647 //
29648 //            item.el.setHeight(height);
29649 //            
29650 //            if(c == 0){
29651 //                item.el.setXY([x, y], isInstant ? false : true);
29652 //            } else {
29653 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29654 //            }
29655 //            
29656 //            y = y + height + this.alternativePadWidth;
29657 //            
29658 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29659 //            
29660 //        }, this);
29661 //        
29662 //        this.el.setHeight(maxHeight);
29663 //        
29664 //    },
29665     
29666     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29667     {
29668         var pos = this.el.getBox(true);
29669         
29670         var minX = pos.x;
29671         var minY = pos.y;
29672         
29673         var maxX = pos.right;
29674         
29675         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29676         
29677         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29678         
29679         Roo.each(queue, function(box, k){
29680             
29681             Roo.each(box, function(b, kk){
29682                 
29683                 b.el.position('absolute');
29684                 
29685                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29686                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29687                 
29688                 if(b.size == 'md-left' || b.size == 'md-right'){
29689                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29690                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29691                 }
29692                 
29693                 b.el.setWidth(width);
29694                 b.el.setHeight(height);
29695                 
29696             }, this);
29697             
29698             if(!box.length){
29699                 return;
29700             }
29701             
29702             var positions = [];
29703             
29704             switch (box.length){
29705                 case 1 :
29706                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29707                     break;
29708                 case 2 :
29709                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29710                     break;
29711                 case 3 :
29712                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29713                     break;
29714                 case 4 :
29715                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29716                     break;
29717                 default :
29718                     break;
29719             }
29720             
29721             Roo.each(box, function(b,kk){
29722                 
29723                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29724                 
29725                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29726                 
29727             }, this);
29728             
29729         }, this);
29730         
29731     },
29732     
29733     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29734     {
29735         Roo.each(eItems, function(b,k){
29736             
29737             b.size = (k == 0) ? 'sm' : 'xs';
29738             b.x = (k == 0) ? 2 : 1;
29739             b.y = (k == 0) ? 2 : 1;
29740             
29741             b.el.position('absolute');
29742             
29743             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29744                 
29745             b.el.setWidth(width);
29746             
29747             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29748             
29749             b.el.setHeight(height);
29750             
29751         }, this);
29752
29753         var positions = [];
29754         
29755         positions.push({
29756             x : maxX - this.unitWidth * 2 - this.gutter,
29757             y : minY
29758         });
29759         
29760         positions.push({
29761             x : maxX - this.unitWidth,
29762             y : minY + (this.unitWidth + this.gutter) * 2
29763         });
29764         
29765         positions.push({
29766             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29767             y : minY
29768         });
29769         
29770         Roo.each(eItems, function(b,k){
29771             
29772             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29773
29774         }, this);
29775         
29776     },
29777     
29778     getVerticalOneBoxColPositions : function(x, y, box)
29779     {
29780         var pos = [];
29781         
29782         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29783         
29784         if(box[0].size == 'md-left'){
29785             rand = 0;
29786         }
29787         
29788         if(box[0].size == 'md-right'){
29789             rand = 1;
29790         }
29791         
29792         pos.push({
29793             x : x + (this.unitWidth + this.gutter) * rand,
29794             y : y
29795         });
29796         
29797         return pos;
29798     },
29799     
29800     getVerticalTwoBoxColPositions : function(x, y, box)
29801     {
29802         var pos = [];
29803         
29804         if(box[0].size == 'xs'){
29805             
29806             pos.push({
29807                 x : x,
29808                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29809             });
29810
29811             pos.push({
29812                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29813                 y : y
29814             });
29815             
29816             return pos;
29817             
29818         }
29819         
29820         pos.push({
29821             x : x,
29822             y : y
29823         });
29824
29825         pos.push({
29826             x : x + (this.unitWidth + this.gutter) * 2,
29827             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29828         });
29829         
29830         return pos;
29831         
29832     },
29833     
29834     getVerticalThreeBoxColPositions : function(x, y, box)
29835     {
29836         var pos = [];
29837         
29838         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29839             
29840             pos.push({
29841                 x : x,
29842                 y : y
29843             });
29844
29845             pos.push({
29846                 x : x + (this.unitWidth + this.gutter) * 1,
29847                 y : y
29848             });
29849             
29850             pos.push({
29851                 x : x + (this.unitWidth + this.gutter) * 2,
29852                 y : y
29853             });
29854             
29855             return pos;
29856             
29857         }
29858         
29859         if(box[0].size == 'xs' && box[1].size == 'xs'){
29860             
29861             pos.push({
29862                 x : x,
29863                 y : y
29864             });
29865
29866             pos.push({
29867                 x : x,
29868                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29869             });
29870             
29871             pos.push({
29872                 x : x + (this.unitWidth + this.gutter) * 1,
29873                 y : y
29874             });
29875             
29876             return pos;
29877             
29878         }
29879         
29880         pos.push({
29881             x : x,
29882             y : y
29883         });
29884
29885         pos.push({
29886             x : x + (this.unitWidth + this.gutter) * 2,
29887             y : y
29888         });
29889
29890         pos.push({
29891             x : x + (this.unitWidth + this.gutter) * 2,
29892             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29893         });
29894             
29895         return pos;
29896         
29897     },
29898     
29899     getVerticalFourBoxColPositions : function(x, y, box)
29900     {
29901         var pos = [];
29902         
29903         if(box[0].size == 'xs'){
29904             
29905             pos.push({
29906                 x : x,
29907                 y : y
29908             });
29909
29910             pos.push({
29911                 x : x,
29912                 y : y + (this.unitHeight + this.gutter) * 1
29913             });
29914             
29915             pos.push({
29916                 x : x,
29917                 y : y + (this.unitHeight + this.gutter) * 2
29918             });
29919             
29920             pos.push({
29921                 x : x + (this.unitWidth + this.gutter) * 1,
29922                 y : y
29923             });
29924             
29925             return pos;
29926             
29927         }
29928         
29929         pos.push({
29930             x : x,
29931             y : y
29932         });
29933
29934         pos.push({
29935             x : x + (this.unitWidth + this.gutter) * 2,
29936             y : y
29937         });
29938
29939         pos.push({
29940             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29941             y : y + (this.unitHeight + this.gutter) * 1
29942         });
29943
29944         pos.push({
29945             x : x + (this.unitWidth + this.gutter) * 2,
29946             y : y + (this.unitWidth + this.gutter) * 2
29947         });
29948
29949         return pos;
29950         
29951     },
29952     
29953     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29954     {
29955         var pos = [];
29956         
29957         if(box[0].size == 'md-left'){
29958             pos.push({
29959                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29960                 y : minY
29961             });
29962             
29963             return pos;
29964         }
29965         
29966         if(box[0].size == 'md-right'){
29967             pos.push({
29968                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29969                 y : minY + (this.unitWidth + this.gutter) * 1
29970             });
29971             
29972             return pos;
29973         }
29974         
29975         var rand = Math.floor(Math.random() * (4 - box[0].y));
29976         
29977         pos.push({
29978             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29979             y : minY + (this.unitWidth + this.gutter) * rand
29980         });
29981         
29982         return pos;
29983         
29984     },
29985     
29986     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29987     {
29988         var pos = [];
29989         
29990         if(box[0].size == 'xs'){
29991             
29992             pos.push({
29993                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29994                 y : minY
29995             });
29996
29997             pos.push({
29998                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29999                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30000             });
30001             
30002             return pos;
30003             
30004         }
30005         
30006         pos.push({
30007             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30008             y : minY
30009         });
30010
30011         pos.push({
30012             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30013             y : minY + (this.unitWidth + this.gutter) * 2
30014         });
30015         
30016         return pos;
30017         
30018     },
30019     
30020     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30021     {
30022         var pos = [];
30023         
30024         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30025             
30026             pos.push({
30027                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30028                 y : minY
30029             });
30030
30031             pos.push({
30032                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30033                 y : minY + (this.unitWidth + this.gutter) * 1
30034             });
30035             
30036             pos.push({
30037                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30038                 y : minY + (this.unitWidth + this.gutter) * 2
30039             });
30040             
30041             return pos;
30042             
30043         }
30044         
30045         if(box[0].size == 'xs' && box[1].size == 'xs'){
30046             
30047             pos.push({
30048                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30049                 y : minY
30050             });
30051
30052             pos.push({
30053                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30054                 y : minY
30055             });
30056             
30057             pos.push({
30058                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30059                 y : minY + (this.unitWidth + this.gutter) * 1
30060             });
30061             
30062             return pos;
30063             
30064         }
30065         
30066         pos.push({
30067             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30068             y : minY
30069         });
30070
30071         pos.push({
30072             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30073             y : minY + (this.unitWidth + this.gutter) * 2
30074         });
30075
30076         pos.push({
30077             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30078             y : minY + (this.unitWidth + this.gutter) * 2
30079         });
30080             
30081         return pos;
30082         
30083     },
30084     
30085     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30086     {
30087         var pos = [];
30088         
30089         if(box[0].size == 'xs'){
30090             
30091             pos.push({
30092                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30093                 y : minY
30094             });
30095
30096             pos.push({
30097                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30098                 y : minY
30099             });
30100             
30101             pos.push({
30102                 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),
30103                 y : minY
30104             });
30105             
30106             pos.push({
30107                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30108                 y : minY + (this.unitWidth + this.gutter) * 1
30109             });
30110             
30111             return pos;
30112             
30113         }
30114         
30115         pos.push({
30116             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30117             y : minY
30118         });
30119         
30120         pos.push({
30121             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30122             y : minY + (this.unitWidth + this.gutter) * 2
30123         });
30124         
30125         pos.push({
30126             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30127             y : minY + (this.unitWidth + this.gutter) * 2
30128         });
30129         
30130         pos.push({
30131             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),
30132             y : minY + (this.unitWidth + this.gutter) * 2
30133         });
30134
30135         return pos;
30136         
30137     }
30138     
30139 });
30140
30141  
30142
30143  /**
30144  *
30145  * This is based on 
30146  * http://masonry.desandro.com
30147  *
30148  * The idea is to render all the bricks based on vertical width...
30149  *
30150  * The original code extends 'outlayer' - we might need to use that....
30151  * 
30152  */
30153
30154
30155 /**
30156  * @class Roo.bootstrap.LayoutMasonryAuto
30157  * @extends Roo.bootstrap.Component
30158  * Bootstrap Layout Masonry class
30159  * 
30160  * @constructor
30161  * Create a new Element
30162  * @param {Object} config The config object
30163  */
30164
30165 Roo.bootstrap.LayoutMasonryAuto = function(config){
30166     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30167 };
30168
30169 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30170     
30171       /**
30172      * @cfg {Boolean} isFitWidth  - resize the width..
30173      */   
30174     isFitWidth : false,  // options..
30175     /**
30176      * @cfg {Boolean} isOriginLeft = left align?
30177      */   
30178     isOriginLeft : true,
30179     /**
30180      * @cfg {Boolean} isOriginTop = top align?
30181      */   
30182     isOriginTop : false,
30183     /**
30184      * @cfg {Boolean} isLayoutInstant = no animation?
30185      */   
30186     isLayoutInstant : false, // needed?
30187     /**
30188      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30189      */   
30190     isResizingContainer : true,
30191     /**
30192      * @cfg {Number} columnWidth  width of the columns 
30193      */   
30194     
30195     columnWidth : 0,
30196     
30197     /**
30198      * @cfg {Number} maxCols maximum number of columns
30199      */   
30200     
30201     maxCols: 0,
30202     /**
30203      * @cfg {Number} padHeight padding below box..
30204      */   
30205     
30206     padHeight : 10, 
30207     
30208     /**
30209      * @cfg {Boolean} isAutoInitial defalut true
30210      */   
30211     
30212     isAutoInitial : true, 
30213     
30214     // private?
30215     gutter : 0,
30216     
30217     containerWidth: 0,
30218     initialColumnWidth : 0,
30219     currentSize : null,
30220     
30221     colYs : null, // array.
30222     maxY : 0,
30223     padWidth: 10,
30224     
30225     
30226     tag: 'div',
30227     cls: '',
30228     bricks: null, //CompositeElement
30229     cols : 0, // array?
30230     // element : null, // wrapped now this.el
30231     _isLayoutInited : null, 
30232     
30233     
30234     getAutoCreate : function(){
30235         
30236         var cfg = {
30237             tag: this.tag,
30238             cls: 'blog-masonary-wrapper ' + this.cls,
30239             cn : {
30240                 cls : 'mas-boxes masonary'
30241             }
30242         };
30243         
30244         return cfg;
30245     },
30246     
30247     getChildContainer: function( )
30248     {
30249         if (this.boxesEl) {
30250             return this.boxesEl;
30251         }
30252         
30253         this.boxesEl = this.el.select('.mas-boxes').first();
30254         
30255         return this.boxesEl;
30256     },
30257     
30258     
30259     initEvents : function()
30260     {
30261         var _this = this;
30262         
30263         if(this.isAutoInitial){
30264             Roo.log('hook children rendered');
30265             this.on('childrenrendered', function() {
30266                 Roo.log('children rendered');
30267                 _this.initial();
30268             } ,this);
30269         }
30270         
30271     },
30272     
30273     initial : function()
30274     {
30275         this.reloadItems();
30276
30277         this.currentSize = this.el.getBox(true);
30278
30279         /// was window resize... - let's see if this works..
30280         Roo.EventManager.onWindowResize(this.resize, this); 
30281
30282         if(!this.isAutoInitial){
30283             this.layout();
30284             return;
30285         }
30286         
30287         this.layout.defer(500,this);
30288     },
30289     
30290     reloadItems: function()
30291     {
30292         this.bricks = this.el.select('.masonry-brick', true);
30293         
30294         this.bricks.each(function(b) {
30295             //Roo.log(b.getSize());
30296             if (!b.attr('originalwidth')) {
30297                 b.attr('originalwidth',  b.getSize().width);
30298             }
30299             
30300         });
30301         
30302         Roo.log(this.bricks.elements.length);
30303     },
30304     
30305     resize : function()
30306     {
30307         Roo.log('resize');
30308         var cs = this.el.getBox(true);
30309         
30310         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30311             Roo.log("no change in with or X");
30312             return;
30313         }
30314         this.currentSize = cs;
30315         this.layout();
30316     },
30317     
30318     layout : function()
30319     {
30320          Roo.log('layout');
30321         this._resetLayout();
30322         //this._manageStamps();
30323       
30324         // don't animate first layout
30325         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30326         this.layoutItems( isInstant );
30327       
30328         // flag for initalized
30329         this._isLayoutInited = true;
30330     },
30331     
30332     layoutItems : function( isInstant )
30333     {
30334         //var items = this._getItemsForLayout( this.items );
30335         // original code supports filtering layout items.. we just ignore it..
30336         
30337         this._layoutItems( this.bricks , isInstant );
30338       
30339         this._postLayout();
30340     },
30341     _layoutItems : function ( items , isInstant)
30342     {
30343        //this.fireEvent( 'layout', this, items );
30344     
30345
30346         if ( !items || !items.elements.length ) {
30347           // no items, emit event with empty array
30348             return;
30349         }
30350
30351         var queue = [];
30352         items.each(function(item) {
30353             Roo.log("layout item");
30354             Roo.log(item);
30355             // get x/y object from method
30356             var position = this._getItemLayoutPosition( item );
30357             // enqueue
30358             position.item = item;
30359             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30360             queue.push( position );
30361         }, this);
30362       
30363         this._processLayoutQueue( queue );
30364     },
30365     /** Sets position of item in DOM
30366     * @param {Element} item
30367     * @param {Number} x - horizontal position
30368     * @param {Number} y - vertical position
30369     * @param {Boolean} isInstant - disables transitions
30370     */
30371     _processLayoutQueue : function( queue )
30372     {
30373         for ( var i=0, len = queue.length; i < len; i++ ) {
30374             var obj = queue[i];
30375             obj.item.position('absolute');
30376             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30377         }
30378     },
30379       
30380     
30381     /**
30382     * Any logic you want to do after each layout,
30383     * i.e. size the container
30384     */
30385     _postLayout : function()
30386     {
30387         this.resizeContainer();
30388     },
30389     
30390     resizeContainer : function()
30391     {
30392         if ( !this.isResizingContainer ) {
30393             return;
30394         }
30395         var size = this._getContainerSize();
30396         if ( size ) {
30397             this.el.setSize(size.width,size.height);
30398             this.boxesEl.setSize(size.width,size.height);
30399         }
30400     },
30401     
30402     
30403     
30404     _resetLayout : function()
30405     {
30406         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30407         this.colWidth = this.el.getWidth();
30408         //this.gutter = this.el.getWidth(); 
30409         
30410         this.measureColumns();
30411
30412         // reset column Y
30413         var i = this.cols;
30414         this.colYs = [];
30415         while (i--) {
30416             this.colYs.push( 0 );
30417         }
30418     
30419         this.maxY = 0;
30420     },
30421
30422     measureColumns : function()
30423     {
30424         this.getContainerWidth();
30425       // if columnWidth is 0, default to outerWidth of first item
30426         if ( !this.columnWidth ) {
30427             var firstItem = this.bricks.first();
30428             Roo.log(firstItem);
30429             this.columnWidth  = this.containerWidth;
30430             if (firstItem && firstItem.attr('originalwidth') ) {
30431                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30432             }
30433             // columnWidth fall back to item of first element
30434             Roo.log("set column width?");
30435                         this.initialColumnWidth = this.columnWidth  ;
30436
30437             // if first elem has no width, default to size of container
30438             
30439         }
30440         
30441         
30442         if (this.initialColumnWidth) {
30443             this.columnWidth = this.initialColumnWidth;
30444         }
30445         
30446         
30447             
30448         // column width is fixed at the top - however if container width get's smaller we should
30449         // reduce it...
30450         
30451         // this bit calcs how man columns..
30452             
30453         var columnWidth = this.columnWidth += this.gutter;
30454       
30455         // calculate columns
30456         var containerWidth = this.containerWidth + this.gutter;
30457         
30458         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30459         // fix rounding errors, typically with gutters
30460         var excess = columnWidth - containerWidth % columnWidth;
30461         
30462         
30463         // if overshoot is less than a pixel, round up, otherwise floor it
30464         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30465         cols = Math[ mathMethod ]( cols );
30466         this.cols = Math.max( cols, 1 );
30467         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30468         
30469          // padding positioning..
30470         var totalColWidth = this.cols * this.columnWidth;
30471         var padavail = this.containerWidth - totalColWidth;
30472         // so for 2 columns - we need 3 'pads'
30473         
30474         var padNeeded = (1+this.cols) * this.padWidth;
30475         
30476         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30477         
30478         this.columnWidth += padExtra
30479         //this.padWidth = Math.floor(padavail /  ( this.cols));
30480         
30481         // adjust colum width so that padding is fixed??
30482         
30483         // we have 3 columns ... total = width * 3
30484         // we have X left over... that should be used by 
30485         
30486         //if (this.expandC) {
30487             
30488         //}
30489         
30490         
30491         
30492     },
30493     
30494     getContainerWidth : function()
30495     {
30496        /* // container is parent if fit width
30497         var container = this.isFitWidth ? this.element.parentNode : this.element;
30498         // check that this.size and size are there
30499         // IE8 triggers resize on body size change, so they might not be
30500         
30501         var size = getSize( container );  //FIXME
30502         this.containerWidth = size && size.innerWidth; //FIXME
30503         */
30504          
30505         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30506         
30507     },
30508     
30509     _getItemLayoutPosition : function( item )  // what is item?
30510     {
30511         // we resize the item to our columnWidth..
30512       
30513         item.setWidth(this.columnWidth);
30514         item.autoBoxAdjust  = false;
30515         
30516         var sz = item.getSize();
30517  
30518         // how many columns does this brick span
30519         var remainder = this.containerWidth % this.columnWidth;
30520         
30521         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30522         // round if off by 1 pixel, otherwise use ceil
30523         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30524         colSpan = Math.min( colSpan, this.cols );
30525         
30526         // normally this should be '1' as we dont' currently allow multi width columns..
30527         
30528         var colGroup = this._getColGroup( colSpan );
30529         // get the minimum Y value from the columns
30530         var minimumY = Math.min.apply( Math, colGroup );
30531         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30532         
30533         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30534          
30535         // position the brick
30536         var position = {
30537             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30538             y: this.currentSize.y + minimumY + this.padHeight
30539         };
30540         
30541         Roo.log(position);
30542         // apply setHeight to necessary columns
30543         var setHeight = minimumY + sz.height + this.padHeight;
30544         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30545         
30546         var setSpan = this.cols + 1 - colGroup.length;
30547         for ( var i = 0; i < setSpan; i++ ) {
30548           this.colYs[ shortColIndex + i ] = setHeight ;
30549         }
30550       
30551         return position;
30552     },
30553     
30554     /**
30555      * @param {Number} colSpan - number of columns the element spans
30556      * @returns {Array} colGroup
30557      */
30558     _getColGroup : function( colSpan )
30559     {
30560         if ( colSpan < 2 ) {
30561           // if brick spans only one column, use all the column Ys
30562           return this.colYs;
30563         }
30564       
30565         var colGroup = [];
30566         // how many different places could this brick fit horizontally
30567         var groupCount = this.cols + 1 - colSpan;
30568         // for each group potential horizontal position
30569         for ( var i = 0; i < groupCount; i++ ) {
30570           // make an array of colY values for that one group
30571           var groupColYs = this.colYs.slice( i, i + colSpan );
30572           // and get the max value of the array
30573           colGroup[i] = Math.max.apply( Math, groupColYs );
30574         }
30575         return colGroup;
30576     },
30577     /*
30578     _manageStamp : function( stamp )
30579     {
30580         var stampSize =  stamp.getSize();
30581         var offset = stamp.getBox();
30582         // get the columns that this stamp affects
30583         var firstX = this.isOriginLeft ? offset.x : offset.right;
30584         var lastX = firstX + stampSize.width;
30585         var firstCol = Math.floor( firstX / this.columnWidth );
30586         firstCol = Math.max( 0, firstCol );
30587         
30588         var lastCol = Math.floor( lastX / this.columnWidth );
30589         // lastCol should not go over if multiple of columnWidth #425
30590         lastCol -= lastX % this.columnWidth ? 0 : 1;
30591         lastCol = Math.min( this.cols - 1, lastCol );
30592         
30593         // set colYs to bottom of the stamp
30594         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30595             stampSize.height;
30596             
30597         for ( var i = firstCol; i <= lastCol; i++ ) {
30598           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30599         }
30600     },
30601     */
30602     
30603     _getContainerSize : function()
30604     {
30605         this.maxY = Math.max.apply( Math, this.colYs );
30606         var size = {
30607             height: this.maxY
30608         };
30609       
30610         if ( this.isFitWidth ) {
30611             size.width = this._getContainerFitWidth();
30612         }
30613       
30614         return size;
30615     },
30616     
30617     _getContainerFitWidth : function()
30618     {
30619         var unusedCols = 0;
30620         // count unused columns
30621         var i = this.cols;
30622         while ( --i ) {
30623           if ( this.colYs[i] !== 0 ) {
30624             break;
30625           }
30626           unusedCols++;
30627         }
30628         // fit container to columns that have been used
30629         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30630     },
30631     
30632     needsResizeLayout : function()
30633     {
30634         var previousWidth = this.containerWidth;
30635         this.getContainerWidth();
30636         return previousWidth !== this.containerWidth;
30637     }
30638  
30639 });
30640
30641  
30642
30643  /*
30644  * - LGPL
30645  *
30646  * element
30647  * 
30648  */
30649
30650 /**
30651  * @class Roo.bootstrap.MasonryBrick
30652  * @extends Roo.bootstrap.Component
30653  * Bootstrap MasonryBrick class
30654  * 
30655  * @constructor
30656  * Create a new MasonryBrick
30657  * @param {Object} config The config object
30658  */
30659
30660 Roo.bootstrap.MasonryBrick = function(config){
30661     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30662     
30663     this.addEvents({
30664         // raw events
30665         /**
30666          * @event click
30667          * When a MasonryBrick is clcik
30668          * @param {Roo.bootstrap.MasonryBrick} this
30669          * @param {Roo.EventObject} e
30670          */
30671         "click" : true
30672     });
30673 };
30674
30675 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30676     
30677     /**
30678      * @cfg {String} title
30679      */   
30680     title : '',
30681     /**
30682      * @cfg {String} html
30683      */   
30684     html : '',
30685     /**
30686      * @cfg {String} bgimage
30687      */   
30688     bgimage : '',
30689     /**
30690      * @cfg {String} videourl
30691      */   
30692     videourl : '',
30693     /**
30694      * @cfg {String} cls
30695      */   
30696     cls : '',
30697     /**
30698      * @cfg {String} href
30699      */   
30700     href : '',
30701     /**
30702      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30703      */   
30704     size : 'xs',
30705     
30706     /**
30707      * @cfg {String} (center|bottom) placetitle
30708      */   
30709     placetitle : '',
30710     
30711     getAutoCreate : function()
30712     {
30713         var cls = 'masonry-brick';
30714         
30715         if(this.href.length){
30716             cls += ' masonry-brick-link';
30717         }
30718         
30719         if(this.bgimage.length){
30720             cls += ' masonry-brick-image';
30721         }
30722         
30723         if(this.size){
30724             cls += ' masonry-' + this.size + '-brick';
30725         }
30726         
30727         if(this.placetitle.length){
30728             
30729             switch (this.placetitle) {
30730                 case 'center' :
30731                     cls += ' masonry-center-title';
30732                     break;
30733                 case 'bottom' :
30734                     cls += ' masonry-bottom-title';
30735                     break;
30736                 default:
30737                     break;
30738             }
30739             
30740         } else {
30741             if(!this.html.length && !this.bgimage.length){
30742                 cls += ' masonry-center-title';
30743             }
30744
30745             if(!this.html.length && this.bgimage.length){
30746                 cls += ' masonry-bottom-title';
30747             }
30748         }
30749         
30750         if(this.cls){
30751             cls += ' ' + this.cls;
30752         }
30753         
30754         var cfg = {
30755             tag: (this.href.length) ? 'a' : 'div',
30756             cls: cls,
30757             cn: [
30758                 {
30759                     tag: 'div',
30760                     cls: 'masonry-brick-paragraph',
30761                     cn: []
30762                 }
30763             ]
30764         };
30765         
30766         if(this.href.length){
30767             cfg.href = this.href;
30768         }
30769         
30770         var cn = cfg.cn[0].cn;
30771         
30772         if(this.title.length){
30773             cn.push({
30774                 tag: 'h4',
30775                 cls: 'masonry-brick-title',
30776                 html: this.title
30777             });
30778         }
30779         
30780         if(this.html.length){
30781             cn.push({
30782                 tag: 'p',
30783                 cls: 'masonry-brick-text',
30784                 html: this.html
30785             });
30786         }  
30787         if (!this.title.length && !this.html.length) {
30788             cfg.cn[0].cls += ' hide';
30789         }
30790         
30791         if(this.bgimage.length){
30792             cfg.cn.push({
30793                 tag: 'img',
30794                 cls: 'masonry-brick-image-view',
30795                 src: this.bgimage
30796             });
30797         }
30798         if(this.videourl.length){
30799             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30800             // youtube support only?
30801             cfg.cn.push({
30802                 tag: 'iframe',
30803                 cls: 'masonry-brick-image-view',
30804                 src: vurl,
30805                 frameborder : 0,
30806                 allowfullscreen : true
30807             });
30808             
30809             
30810         }
30811         return cfg;
30812         
30813     },
30814     
30815     initEvents: function() 
30816     {
30817         switch (this.size) {
30818             case 'xs' :
30819 //                this.intSize = 1;
30820                 this.x = 1;
30821                 this.y = 1;
30822                 break;
30823             case 'sm' :
30824 //                this.intSize = 2;
30825                 this.x = 2;
30826                 this.y = 2;
30827                 break;
30828             case 'md' :
30829             case 'md-left' :
30830             case 'md-right' :
30831 //                this.intSize = 3;
30832                 this.x = 3;
30833                 this.y = 3;
30834                 break;
30835             case 'tall' :
30836 //                this.intSize = 3;
30837                 this.x = 2;
30838                 this.y = 3;
30839                 break;
30840             case 'wide' :
30841 //                this.intSize = 3;
30842                 this.x = 3;
30843                 this.y = 2;
30844                 break;
30845             case 'wide-thin' :
30846 //                this.intSize = 3;
30847                 this.x = 3;
30848                 this.y = 1;
30849                 break;
30850                         
30851             default :
30852                 break;
30853         }
30854         
30855         
30856         
30857         if(Roo.isTouch){
30858             this.el.on('touchstart', this.onTouchStart, this);
30859             this.el.on('touchmove', this.onTouchMove, this);
30860             this.el.on('touchend', this.onTouchEnd, this);
30861             this.el.on('contextmenu', this.onContextMenu, this);
30862         } else {
30863             this.el.on('mouseenter'  ,this.enter, this);
30864             this.el.on('mouseleave', this.leave, this);
30865         }
30866         
30867         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30868             this.parent().bricks.push(this);   
30869         }
30870         
30871     },
30872     
30873     onClick: function(e, el)
30874     {
30875         if(!Roo.isTouch){
30876             return;
30877         }
30878         
30879         var time = this.endTimer - this.startTimer;
30880         
30881         //alert(time);
30882         
30883         if(time < 1000){
30884             return;
30885         }
30886         
30887         e.preventDefault();
30888     },
30889     
30890     enter: function(e, el)
30891     {
30892         e.preventDefault();
30893         
30894         if(this.bgimage.length && this.html.length){
30895             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30896         }
30897     },
30898     
30899     leave: function(e, el)
30900     {
30901         e.preventDefault();
30902         
30903         if(this.bgimage.length && this.html.length){
30904             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30905         }
30906     },
30907     
30908     onTouchStart: function(e, el)
30909     {
30910 //        e.preventDefault();
30911         
30912         this.touchmoved = false;
30913         
30914         if(!this.bgimage.length || !this.html.length){
30915             return;
30916         }
30917         
30918         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30919         
30920         this.timer = new Date().getTime();
30921         
30922     },
30923     
30924     onTouchMove: function(e, el)
30925     {
30926         this.touchmoved = true;
30927     },
30928     
30929     onContextMenu : function(e,el)
30930     {
30931         e.preventDefault();
30932         e.stopPropagation();
30933         return false;
30934     },
30935     
30936     onTouchEnd: function(e, el)
30937     {
30938 //        e.preventDefault();
30939         
30940         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30941         
30942             this.leave(e,el);
30943             
30944             return;
30945         }
30946         
30947         if(!this.bgimage.length || !this.html.length){
30948             
30949             if(this.href.length){
30950                 window.location.href = this.href;
30951             }
30952             
30953             return;
30954         }
30955         
30956         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30957         
30958         window.location.href = this.href;
30959     }
30960     
30961 });
30962
30963  
30964
30965  /*
30966  * - LGPL
30967  *
30968  * element
30969  * 
30970  */
30971
30972 /**
30973  * @class Roo.bootstrap.Brick
30974  * @extends Roo.bootstrap.Component
30975  * Bootstrap Brick class
30976  * 
30977  * @constructor
30978  * Create a new Brick
30979  * @param {Object} config The config object
30980  */
30981
30982 Roo.bootstrap.Brick = function(config){
30983     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30984     
30985     this.addEvents({
30986         // raw events
30987         /**
30988          * @event click
30989          * When a Brick is click
30990          * @param {Roo.bootstrap.Brick} this
30991          * @param {Roo.EventObject} e
30992          */
30993         "click" : true
30994     });
30995 };
30996
30997 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30998     
30999     /**
31000      * @cfg {String} title
31001      */   
31002     title : '',
31003     /**
31004      * @cfg {String} html
31005      */   
31006     html : '',
31007     /**
31008      * @cfg {String} bgimage
31009      */   
31010     bgimage : '',
31011     /**
31012      * @cfg {String} cls
31013      */   
31014     cls : '',
31015     /**
31016      * @cfg {String} href
31017      */   
31018     href : '',
31019     /**
31020      * @cfg {String} video
31021      */   
31022     video : '',
31023     /**
31024      * @cfg {Boolean} square
31025      */   
31026     square : true,
31027     
31028     getAutoCreate : function()
31029     {
31030         var cls = 'roo-brick';
31031         
31032         if(this.href.length){
31033             cls += ' roo-brick-link';
31034         }
31035         
31036         if(this.bgimage.length){
31037             cls += ' roo-brick-image';
31038         }
31039         
31040         if(!this.html.length && !this.bgimage.length){
31041             cls += ' roo-brick-center-title';
31042         }
31043         
31044         if(!this.html.length && this.bgimage.length){
31045             cls += ' roo-brick-bottom-title';
31046         }
31047         
31048         if(this.cls){
31049             cls += ' ' + this.cls;
31050         }
31051         
31052         var cfg = {
31053             tag: (this.href.length) ? 'a' : 'div',
31054             cls: cls,
31055             cn: [
31056                 {
31057                     tag: 'div',
31058                     cls: 'roo-brick-paragraph',
31059                     cn: []
31060                 }
31061             ]
31062         };
31063         
31064         if(this.href.length){
31065             cfg.href = this.href;
31066         }
31067         
31068         var cn = cfg.cn[0].cn;
31069         
31070         if(this.title.length){
31071             cn.push({
31072                 tag: 'h4',
31073                 cls: 'roo-brick-title',
31074                 html: this.title
31075             });
31076         }
31077         
31078         if(this.html.length){
31079             cn.push({
31080                 tag: 'p',
31081                 cls: 'roo-brick-text',
31082                 html: this.html
31083             });
31084         } else {
31085             cn.cls += ' hide';
31086         }
31087         
31088         if(this.bgimage.length){
31089             cfg.cn.push({
31090                 tag: 'img',
31091                 cls: 'roo-brick-image-view',
31092                 src: this.bgimage
31093             });
31094         }
31095         
31096         return cfg;
31097     },
31098     
31099     initEvents: function() 
31100     {
31101         if(this.title.length || this.html.length){
31102             this.el.on('mouseenter'  ,this.enter, this);
31103             this.el.on('mouseleave', this.leave, this);
31104         }
31105         
31106         
31107         Roo.EventManager.onWindowResize(this.resize, this); 
31108         
31109         this.resize();
31110     },
31111     
31112     resize : function()
31113     {
31114         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31115         
31116         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31117 //        paragraph.setHeight(paragraph.getWidth());
31118         
31119         if(this.bgimage.length){
31120             var image = this.el.select('.roo-brick-image-view', true).first();
31121             image.setWidth(paragraph.getWidth());
31122             image.setHeight(paragraph.getWidth());
31123         }
31124         
31125     },
31126     
31127     enter: function(e, el)
31128     {
31129         e.preventDefault();
31130         
31131         if(this.bgimage.length){
31132             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31133             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31134         }
31135     },
31136     
31137     leave: function(e, el)
31138     {
31139         e.preventDefault();
31140         
31141         if(this.bgimage.length){
31142             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31143             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31144         }
31145     }
31146     
31147 });
31148
31149  
31150
31151  /*
31152  * Based on:
31153  * Ext JS Library 1.1.1
31154  * Copyright(c) 2006-2007, Ext JS, LLC.
31155  *
31156  * Originally Released Under LGPL - original licence link has changed is not relivant.
31157  *
31158  * Fork - LGPL
31159  * <script type="text/javascript">
31160  */
31161
31162
31163 /**
31164  * @class Roo.bootstrap.SplitBar
31165  * @extends Roo.util.Observable
31166  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31167  * <br><br>
31168  * Usage:
31169  * <pre><code>
31170 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31171                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31172 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31173 split.minSize = 100;
31174 split.maxSize = 600;
31175 split.animate = true;
31176 split.on('moved', splitterMoved);
31177 </code></pre>
31178  * @constructor
31179  * Create a new SplitBar
31180  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31181  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31182  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31183  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31184                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31185                         position of the SplitBar).
31186  */
31187 Roo.bootstrap.SplitBar = function(cfg){
31188     
31189     /** @private */
31190     
31191     //{
31192     //  dragElement : elm
31193     //  resizingElement: el,
31194         // optional..
31195     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31196     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31197         // existingProxy ???
31198     //}
31199     
31200     this.el = Roo.get(cfg.dragElement, true);
31201     this.el.dom.unselectable = "on";
31202     /** @private */
31203     this.resizingEl = Roo.get(cfg.resizingElement, true);
31204
31205     /**
31206      * @private
31207      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31208      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31209      * @type Number
31210      */
31211     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31212     
31213     /**
31214      * The minimum size of the resizing element. (Defaults to 0)
31215      * @type Number
31216      */
31217     this.minSize = 0;
31218     
31219     /**
31220      * The maximum size of the resizing element. (Defaults to 2000)
31221      * @type Number
31222      */
31223     this.maxSize = 2000;
31224     
31225     /**
31226      * Whether to animate the transition to the new size
31227      * @type Boolean
31228      */
31229     this.animate = false;
31230     
31231     /**
31232      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31233      * @type Boolean
31234      */
31235     this.useShim = false;
31236     
31237     /** @private */
31238     this.shim = null;
31239     
31240     if(!cfg.existingProxy){
31241         /** @private */
31242         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31243     }else{
31244         this.proxy = Roo.get(cfg.existingProxy).dom;
31245     }
31246     /** @private */
31247     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31248     
31249     /** @private */
31250     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31251     
31252     /** @private */
31253     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31254     
31255     /** @private */
31256     this.dragSpecs = {};
31257     
31258     /**
31259      * @private The adapter to use to positon and resize elements
31260      */
31261     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31262     this.adapter.init(this);
31263     
31264     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31265         /** @private */
31266         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31267         this.el.addClass("roo-splitbar-h");
31268     }else{
31269         /** @private */
31270         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31271         this.el.addClass("roo-splitbar-v");
31272     }
31273     
31274     this.addEvents({
31275         /**
31276          * @event resize
31277          * Fires when the splitter is moved (alias for {@link #event-moved})
31278          * @param {Roo.bootstrap.SplitBar} this
31279          * @param {Number} newSize the new width or height
31280          */
31281         "resize" : true,
31282         /**
31283          * @event moved
31284          * Fires when the splitter is moved
31285          * @param {Roo.bootstrap.SplitBar} this
31286          * @param {Number} newSize the new width or height
31287          */
31288         "moved" : true,
31289         /**
31290          * @event beforeresize
31291          * Fires before the splitter is dragged
31292          * @param {Roo.bootstrap.SplitBar} this
31293          */
31294         "beforeresize" : true,
31295
31296         "beforeapply" : true
31297     });
31298
31299     Roo.util.Observable.call(this);
31300 };
31301
31302 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31303     onStartProxyDrag : function(x, y){
31304         this.fireEvent("beforeresize", this);
31305         if(!this.overlay){
31306             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31307             o.unselectable();
31308             o.enableDisplayMode("block");
31309             // all splitbars share the same overlay
31310             Roo.bootstrap.SplitBar.prototype.overlay = o;
31311         }
31312         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31313         this.overlay.show();
31314         Roo.get(this.proxy).setDisplayed("block");
31315         var size = this.adapter.getElementSize(this);
31316         this.activeMinSize = this.getMinimumSize();;
31317         this.activeMaxSize = this.getMaximumSize();;
31318         var c1 = size - this.activeMinSize;
31319         var c2 = Math.max(this.activeMaxSize - size, 0);
31320         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31321             this.dd.resetConstraints();
31322             this.dd.setXConstraint(
31323                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31324                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31325             );
31326             this.dd.setYConstraint(0, 0);
31327         }else{
31328             this.dd.resetConstraints();
31329             this.dd.setXConstraint(0, 0);
31330             this.dd.setYConstraint(
31331                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31332                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31333             );
31334          }
31335         this.dragSpecs.startSize = size;
31336         this.dragSpecs.startPoint = [x, y];
31337         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31338     },
31339     
31340     /** 
31341      * @private Called after the drag operation by the DDProxy
31342      */
31343     onEndProxyDrag : function(e){
31344         Roo.get(this.proxy).setDisplayed(false);
31345         var endPoint = Roo.lib.Event.getXY(e);
31346         if(this.overlay){
31347             this.overlay.hide();
31348         }
31349         var newSize;
31350         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31351             newSize = this.dragSpecs.startSize + 
31352                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31353                     endPoint[0] - this.dragSpecs.startPoint[0] :
31354                     this.dragSpecs.startPoint[0] - endPoint[0]
31355                 );
31356         }else{
31357             newSize = this.dragSpecs.startSize + 
31358                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31359                     endPoint[1] - this.dragSpecs.startPoint[1] :
31360                     this.dragSpecs.startPoint[1] - endPoint[1]
31361                 );
31362         }
31363         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31364         if(newSize != this.dragSpecs.startSize){
31365             if(this.fireEvent('beforeapply', this, newSize) !== false){
31366                 this.adapter.setElementSize(this, newSize);
31367                 this.fireEvent("moved", this, newSize);
31368                 this.fireEvent("resize", this, newSize);
31369             }
31370         }
31371     },
31372     
31373     /**
31374      * Get the adapter this SplitBar uses
31375      * @return The adapter object
31376      */
31377     getAdapter : function(){
31378         return this.adapter;
31379     },
31380     
31381     /**
31382      * Set the adapter this SplitBar uses
31383      * @param {Object} adapter A SplitBar adapter object
31384      */
31385     setAdapter : function(adapter){
31386         this.adapter = adapter;
31387         this.adapter.init(this);
31388     },
31389     
31390     /**
31391      * Gets the minimum size for the resizing element
31392      * @return {Number} The minimum size
31393      */
31394     getMinimumSize : function(){
31395         return this.minSize;
31396     },
31397     
31398     /**
31399      * Sets the minimum size for the resizing element
31400      * @param {Number} minSize The minimum size
31401      */
31402     setMinimumSize : function(minSize){
31403         this.minSize = minSize;
31404     },
31405     
31406     /**
31407      * Gets the maximum size for the resizing element
31408      * @return {Number} The maximum size
31409      */
31410     getMaximumSize : function(){
31411         return this.maxSize;
31412     },
31413     
31414     /**
31415      * Sets the maximum size for the resizing element
31416      * @param {Number} maxSize The maximum size
31417      */
31418     setMaximumSize : function(maxSize){
31419         this.maxSize = maxSize;
31420     },
31421     
31422     /**
31423      * Sets the initialize size for the resizing element
31424      * @param {Number} size The initial size
31425      */
31426     setCurrentSize : function(size){
31427         var oldAnimate = this.animate;
31428         this.animate = false;
31429         this.adapter.setElementSize(this, size);
31430         this.animate = oldAnimate;
31431     },
31432     
31433     /**
31434      * Destroy this splitbar. 
31435      * @param {Boolean} removeEl True to remove the element
31436      */
31437     destroy : function(removeEl){
31438         if(this.shim){
31439             this.shim.remove();
31440         }
31441         this.dd.unreg();
31442         this.proxy.parentNode.removeChild(this.proxy);
31443         if(removeEl){
31444             this.el.remove();
31445         }
31446     }
31447 });
31448
31449 /**
31450  * @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.
31451  */
31452 Roo.bootstrap.SplitBar.createProxy = function(dir){
31453     var proxy = new Roo.Element(document.createElement("div"));
31454     proxy.unselectable();
31455     var cls = 'roo-splitbar-proxy';
31456     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31457     document.body.appendChild(proxy.dom);
31458     return proxy.dom;
31459 };
31460
31461 /** 
31462  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31463  * Default Adapter. It assumes the splitter and resizing element are not positioned
31464  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31465  */
31466 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31467 };
31468
31469 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31470     // do nothing for now
31471     init : function(s){
31472     
31473     },
31474     /**
31475      * Called before drag operations to get the current size of the resizing element. 
31476      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31477      */
31478      getElementSize : function(s){
31479         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31480             return s.resizingEl.getWidth();
31481         }else{
31482             return s.resizingEl.getHeight();
31483         }
31484     },
31485     
31486     /**
31487      * Called after drag operations to set the size of the resizing element.
31488      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31489      * @param {Number} newSize The new size to set
31490      * @param {Function} onComplete A function to be invoked when resizing is complete
31491      */
31492     setElementSize : function(s, newSize, onComplete){
31493         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31494             if(!s.animate){
31495                 s.resizingEl.setWidth(newSize);
31496                 if(onComplete){
31497                     onComplete(s, newSize);
31498                 }
31499             }else{
31500                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31501             }
31502         }else{
31503             
31504             if(!s.animate){
31505                 s.resizingEl.setHeight(newSize);
31506                 if(onComplete){
31507                     onComplete(s, newSize);
31508                 }
31509             }else{
31510                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31511             }
31512         }
31513     }
31514 };
31515
31516 /** 
31517  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31518  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31519  * Adapter that  moves the splitter element to align with the resized sizing element. 
31520  * Used with an absolute positioned SplitBar.
31521  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31522  * document.body, make sure you assign an id to the body element.
31523  */
31524 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31525     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31526     this.container = Roo.get(container);
31527 };
31528
31529 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31530     init : function(s){
31531         this.basic.init(s);
31532     },
31533     
31534     getElementSize : function(s){
31535         return this.basic.getElementSize(s);
31536     },
31537     
31538     setElementSize : function(s, newSize, onComplete){
31539         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31540     },
31541     
31542     moveSplitter : function(s){
31543         var yes = Roo.bootstrap.SplitBar;
31544         switch(s.placement){
31545             case yes.LEFT:
31546                 s.el.setX(s.resizingEl.getRight());
31547                 break;
31548             case yes.RIGHT:
31549                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31550                 break;
31551             case yes.TOP:
31552                 s.el.setY(s.resizingEl.getBottom());
31553                 break;
31554             case yes.BOTTOM:
31555                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31556                 break;
31557         }
31558     }
31559 };
31560
31561 /**
31562  * Orientation constant - Create a vertical SplitBar
31563  * @static
31564  * @type Number
31565  */
31566 Roo.bootstrap.SplitBar.VERTICAL = 1;
31567
31568 /**
31569  * Orientation constant - Create a horizontal SplitBar
31570  * @static
31571  * @type Number
31572  */
31573 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31574
31575 /**
31576  * Placement constant - The resizing element is to the left of the splitter element
31577  * @static
31578  * @type Number
31579  */
31580 Roo.bootstrap.SplitBar.LEFT = 1;
31581
31582 /**
31583  * Placement constant - The resizing element is to the right of the splitter element
31584  * @static
31585  * @type Number
31586  */
31587 Roo.bootstrap.SplitBar.RIGHT = 2;
31588
31589 /**
31590  * Placement constant - The resizing element is positioned above the splitter element
31591  * @static
31592  * @type Number
31593  */
31594 Roo.bootstrap.SplitBar.TOP = 3;
31595
31596 /**
31597  * Placement constant - The resizing element is positioned under splitter element
31598  * @static
31599  * @type Number
31600  */
31601 Roo.bootstrap.SplitBar.BOTTOM = 4;
31602 Roo.namespace("Roo.bootstrap.layout");/*
31603  * Based on:
31604  * Ext JS Library 1.1.1
31605  * Copyright(c) 2006-2007, Ext JS, LLC.
31606  *
31607  * Originally Released Under LGPL - original licence link has changed is not relivant.
31608  *
31609  * Fork - LGPL
31610  * <script type="text/javascript">
31611  */
31612  
31613 /**
31614  * @class Roo.bootstrap.layout.Manager
31615  * @extends Roo.bootstrap.Component
31616  * Base class for layout managers.
31617  */
31618 Roo.bootstrap.layout.Manager = function(config)
31619 {
31620     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31621     
31622     
31623      
31624     
31625     
31626     /** false to disable window resize monitoring @type Boolean */
31627     this.monitorWindowResize = true;
31628     this.regions = {};
31629     this.addEvents({
31630         /**
31631          * @event layout
31632          * Fires when a layout is performed. 
31633          * @param {Roo.LayoutManager} this
31634          */
31635         "layout" : true,
31636         /**
31637          * @event regionresized
31638          * Fires when the user resizes a region. 
31639          * @param {Roo.LayoutRegion} region The resized region
31640          * @param {Number} newSize The new size (width for east/west, height for north/south)
31641          */
31642         "regionresized" : true,
31643         /**
31644          * @event regioncollapsed
31645          * Fires when a region is collapsed. 
31646          * @param {Roo.LayoutRegion} region The collapsed region
31647          */
31648         "regioncollapsed" : true,
31649         /**
31650          * @event regionexpanded
31651          * Fires when a region is expanded.  
31652          * @param {Roo.LayoutRegion} region The expanded region
31653          */
31654         "regionexpanded" : true
31655     });
31656     this.updating = false;
31657     
31658     if (config.el) {
31659         this.el = Roo.get(config.el);
31660         this.initEvents();
31661     }
31662     
31663 };
31664
31665 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31666     
31667     
31668     regions : null,
31669     
31670     monitorWindowResize : true,
31671     
31672     
31673     updating : false,
31674     
31675     
31676     onRender : function(ct, position)
31677     {
31678         if(!this.el){
31679             this.el = Roo.get(ct);
31680             this.initEvents();
31681         }
31682     },
31683     
31684     
31685     initEvents: function()
31686     {
31687         
31688         
31689         // ie scrollbar fix
31690         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31691             document.body.scroll = "no";
31692         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31693             this.el.position('relative');
31694         }
31695         this.id = this.el.id;
31696         this.el.addClass("roo-layout-container");
31697         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31698         if(this.el.dom != document.body ) {
31699             this.el.on('resize', this.layout,this);
31700             this.el.on('show', this.layout,this);
31701         }
31702
31703     },
31704     
31705     /**
31706      * Returns true if this layout is currently being updated
31707      * @return {Boolean}
31708      */
31709     isUpdating : function(){
31710         return this.updating; 
31711     },
31712     
31713     /**
31714      * Suspend the LayoutManager from doing auto-layouts while
31715      * making multiple add or remove calls
31716      */
31717     beginUpdate : function(){
31718         this.updating = true;    
31719     },
31720     
31721     /**
31722      * Restore auto-layouts and optionally disable the manager from performing a layout
31723      * @param {Boolean} noLayout true to disable a layout update 
31724      */
31725     endUpdate : function(noLayout){
31726         this.updating = false;
31727         if(!noLayout){
31728             this.layout();
31729         }    
31730     },
31731     
31732     layout: function(){
31733         // abstract...
31734     },
31735     
31736     onRegionResized : function(region, newSize){
31737         this.fireEvent("regionresized", region, newSize);
31738         this.layout();
31739     },
31740     
31741     onRegionCollapsed : function(region){
31742         this.fireEvent("regioncollapsed", region);
31743     },
31744     
31745     onRegionExpanded : function(region){
31746         this.fireEvent("regionexpanded", region);
31747     },
31748         
31749     /**
31750      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31751      * performs box-model adjustments.
31752      * @return {Object} The size as an object {width: (the width), height: (the height)}
31753      */
31754     getViewSize : function()
31755     {
31756         var size;
31757         if(this.el.dom != document.body){
31758             size = this.el.getSize();
31759         }else{
31760             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31761         }
31762         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31763         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31764         return size;
31765     },
31766     
31767     /**
31768      * Returns the Element this layout is bound to.
31769      * @return {Roo.Element}
31770      */
31771     getEl : function(){
31772         return this.el;
31773     },
31774     
31775     /**
31776      * Returns the specified region.
31777      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31778      * @return {Roo.LayoutRegion}
31779      */
31780     getRegion : function(target){
31781         return this.regions[target.toLowerCase()];
31782     },
31783     
31784     onWindowResize : function(){
31785         if(this.monitorWindowResize){
31786             this.layout();
31787         }
31788     }
31789 });/*
31790  * Based on:
31791  * Ext JS Library 1.1.1
31792  * Copyright(c) 2006-2007, Ext JS, LLC.
31793  *
31794  * Originally Released Under LGPL - original licence link has changed is not relivant.
31795  *
31796  * Fork - LGPL
31797  * <script type="text/javascript">
31798  */
31799 /**
31800  * @class Roo.bootstrap.layout.Border
31801  * @extends Roo.bootstrap.layout.Manager
31802  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31803  * please see: examples/bootstrap/nested.html<br><br>
31804  
31805 <b>The container the layout is rendered into can be either the body element or any other element.
31806 If it is not the body element, the container needs to either be an absolute positioned element,
31807 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31808 the container size if it is not the body element.</b>
31809
31810 * @constructor
31811 * Create a new Border
31812 * @param {Object} config Configuration options
31813  */
31814 Roo.bootstrap.layout.Border = function(config){
31815     config = config || {};
31816     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31817     
31818     
31819     
31820     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31821         if(config[region]){
31822             config[region].region = region;
31823             this.addRegion(config[region]);
31824         }
31825     },this);
31826     
31827 };
31828
31829 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31830
31831 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31832     /**
31833      * Creates and adds a new region if it doesn't already exist.
31834      * @param {String} target The target region key (north, south, east, west or center).
31835      * @param {Object} config The regions config object
31836      * @return {BorderLayoutRegion} The new region
31837      */
31838     addRegion : function(config)
31839     {
31840         if(!this.regions[config.region]){
31841             var r = this.factory(config);
31842             this.bindRegion(r);
31843         }
31844         return this.regions[config.region];
31845     },
31846
31847     // private (kinda)
31848     bindRegion : function(r){
31849         this.regions[r.config.region] = r;
31850         
31851         r.on("visibilitychange",    this.layout, this);
31852         r.on("paneladded",          this.layout, this);
31853         r.on("panelremoved",        this.layout, this);
31854         r.on("invalidated",         this.layout, this);
31855         r.on("resized",             this.onRegionResized, this);
31856         r.on("collapsed",           this.onRegionCollapsed, this);
31857         r.on("expanded",            this.onRegionExpanded, this);
31858     },
31859
31860     /**
31861      * Performs a layout update.
31862      */
31863     layout : function()
31864     {
31865         if(this.updating) {
31866             return;
31867         }
31868         
31869         // render all the rebions if they have not been done alreayd?
31870         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31871             if(this.regions[region] && !this.regions[region].bodyEl){
31872                 this.regions[region].onRender(this.el)
31873             }
31874         },this);
31875         
31876         var size = this.getViewSize();
31877         var w = size.width;
31878         var h = size.height;
31879         var centerW = w;
31880         var centerH = h;
31881         var centerY = 0;
31882         var centerX = 0;
31883         //var x = 0, y = 0;
31884
31885         var rs = this.regions;
31886         var north = rs["north"];
31887         var south = rs["south"]; 
31888         var west = rs["west"];
31889         var east = rs["east"];
31890         var center = rs["center"];
31891         //if(this.hideOnLayout){ // not supported anymore
31892             //c.el.setStyle("display", "none");
31893         //}
31894         if(north && north.isVisible()){
31895             var b = north.getBox();
31896             var m = north.getMargins();
31897             b.width = w - (m.left+m.right);
31898             b.x = m.left;
31899             b.y = m.top;
31900             centerY = b.height + b.y + m.bottom;
31901             centerH -= centerY;
31902             north.updateBox(this.safeBox(b));
31903         }
31904         if(south && south.isVisible()){
31905             var b = south.getBox();
31906             var m = south.getMargins();
31907             b.width = w - (m.left+m.right);
31908             b.x = m.left;
31909             var totalHeight = (b.height + m.top + m.bottom);
31910             b.y = h - totalHeight + m.top;
31911             centerH -= totalHeight;
31912             south.updateBox(this.safeBox(b));
31913         }
31914         if(west && west.isVisible()){
31915             var b = west.getBox();
31916             var m = west.getMargins();
31917             b.height = centerH - (m.top+m.bottom);
31918             b.x = m.left;
31919             b.y = centerY + m.top;
31920             var totalWidth = (b.width + m.left + m.right);
31921             centerX += totalWidth;
31922             centerW -= totalWidth;
31923             west.updateBox(this.safeBox(b));
31924         }
31925         if(east && east.isVisible()){
31926             var b = east.getBox();
31927             var m = east.getMargins();
31928             b.height = centerH - (m.top+m.bottom);
31929             var totalWidth = (b.width + m.left + m.right);
31930             b.x = w - totalWidth + m.left;
31931             b.y = centerY + m.top;
31932             centerW -= totalWidth;
31933             east.updateBox(this.safeBox(b));
31934         }
31935         if(center){
31936             var m = center.getMargins();
31937             var centerBox = {
31938                 x: centerX + m.left,
31939                 y: centerY + m.top,
31940                 width: centerW - (m.left+m.right),
31941                 height: centerH - (m.top+m.bottom)
31942             };
31943             //if(this.hideOnLayout){
31944                 //center.el.setStyle("display", "block");
31945             //}
31946             center.updateBox(this.safeBox(centerBox));
31947         }
31948         this.el.repaint();
31949         this.fireEvent("layout", this);
31950     },
31951
31952     // private
31953     safeBox : function(box){
31954         box.width = Math.max(0, box.width);
31955         box.height = Math.max(0, box.height);
31956         return box;
31957     },
31958
31959     /**
31960      * Adds a ContentPanel (or subclass) to this layout.
31961      * @param {String} target The target region key (north, south, east, west or center).
31962      * @param {Roo.ContentPanel} panel The panel to add
31963      * @return {Roo.ContentPanel} The added panel
31964      */
31965     add : function(target, panel){
31966          
31967         target = target.toLowerCase();
31968         return this.regions[target].add(panel);
31969     },
31970
31971     /**
31972      * Remove a ContentPanel (or subclass) to this layout.
31973      * @param {String} target The target region key (north, south, east, west or center).
31974      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31975      * @return {Roo.ContentPanel} The removed panel
31976      */
31977     remove : function(target, panel){
31978         target = target.toLowerCase();
31979         return this.regions[target].remove(panel);
31980     },
31981
31982     /**
31983      * Searches all regions for a panel with the specified id
31984      * @param {String} panelId
31985      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31986      */
31987     findPanel : function(panelId){
31988         var rs = this.regions;
31989         for(var target in rs){
31990             if(typeof rs[target] != "function"){
31991                 var p = rs[target].getPanel(panelId);
31992                 if(p){
31993                     return p;
31994                 }
31995             }
31996         }
31997         return null;
31998     },
31999
32000     /**
32001      * Searches all regions for a panel with the specified id and activates (shows) it.
32002      * @param {String/ContentPanel} panelId The panels id or the panel itself
32003      * @return {Roo.ContentPanel} The shown panel or null
32004      */
32005     showPanel : function(panelId) {
32006       var rs = this.regions;
32007       for(var target in rs){
32008          var r = rs[target];
32009          if(typeof r != "function"){
32010             if(r.hasPanel(panelId)){
32011                return r.showPanel(panelId);
32012             }
32013          }
32014       }
32015       return null;
32016    },
32017
32018    /**
32019      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32020      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32021      */
32022    /*
32023     restoreState : function(provider){
32024         if(!provider){
32025             provider = Roo.state.Manager;
32026         }
32027         var sm = new Roo.LayoutStateManager();
32028         sm.init(this, provider);
32029     },
32030 */
32031  
32032  
32033     /**
32034      * Adds a xtype elements to the layout.
32035      * <pre><code>
32036
32037 layout.addxtype({
32038        xtype : 'ContentPanel',
32039        region: 'west',
32040        items: [ .... ]
32041    }
32042 );
32043
32044 layout.addxtype({
32045         xtype : 'NestedLayoutPanel',
32046         region: 'west',
32047         layout: {
32048            center: { },
32049            west: { }   
32050         },
32051         items : [ ... list of content panels or nested layout panels.. ]
32052    }
32053 );
32054 </code></pre>
32055      * @param {Object} cfg Xtype definition of item to add.
32056      */
32057     addxtype : function(cfg)
32058     {
32059         // basically accepts a pannel...
32060         // can accept a layout region..!?!?
32061         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32062         
32063         
32064         // theory?  children can only be panels??
32065         
32066         //if (!cfg.xtype.match(/Panel$/)) {
32067         //    return false;
32068         //}
32069         var ret = false;
32070         
32071         if (typeof(cfg.region) == 'undefined') {
32072             Roo.log("Failed to add Panel, region was not set");
32073             Roo.log(cfg);
32074             return false;
32075         }
32076         var region = cfg.region;
32077         delete cfg.region;
32078         
32079           
32080         var xitems = [];
32081         if (cfg.items) {
32082             xitems = cfg.items;
32083             delete cfg.items;
32084         }
32085         var nb = false;
32086         
32087         switch(cfg.xtype) 
32088         {
32089             case 'Content':  // ContentPanel (el, cfg)
32090             case 'Scroll':  // ContentPanel (el, cfg)
32091             case 'View': 
32092                 cfg.autoCreate = true;
32093                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32094                 //} else {
32095                 //    var el = this.el.createChild();
32096                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32097                 //}
32098                 
32099                 this.add(region, ret);
32100                 break;
32101             
32102             /*
32103             case 'TreePanel': // our new panel!
32104                 cfg.el = this.el.createChild();
32105                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32106                 this.add(region, ret);
32107                 break;
32108             */
32109             
32110             case 'Nest': 
32111                 // create a new Layout (which is  a Border Layout...
32112                 
32113                 var clayout = cfg.layout;
32114                 clayout.el  = this.el.createChild();
32115                 clayout.items   = clayout.items  || [];
32116                 
32117                 delete cfg.layout;
32118                 
32119                 // replace this exitems with the clayout ones..
32120                 xitems = clayout.items;
32121                  
32122                 // force background off if it's in center...
32123                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32124                     cfg.background = false;
32125                 }
32126                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
32127                 
32128                 
32129                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32130                 //console.log('adding nested layout panel '  + cfg.toSource());
32131                 this.add(region, ret);
32132                 nb = {}; /// find first...
32133                 break;
32134             
32135             case 'Grid':
32136                 
32137                 // needs grid and region
32138                 
32139                 //var el = this.getRegion(region).el.createChild();
32140                 /*
32141                  *var el = this.el.createChild();
32142                 // create the grid first...
32143                 cfg.grid.container = el;
32144                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32145                 */
32146                 
32147                 if (region == 'center' && this.active ) {
32148                     cfg.background = false;
32149                 }
32150                 
32151                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32152                 
32153                 this.add(region, ret);
32154                 /*
32155                 if (cfg.background) {
32156                     // render grid on panel activation (if panel background)
32157                     ret.on('activate', function(gp) {
32158                         if (!gp.grid.rendered) {
32159                     //        gp.grid.render(el);
32160                         }
32161                     });
32162                 } else {
32163                   //  cfg.grid.render(el);
32164                 }
32165                 */
32166                 break;
32167            
32168            
32169             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32170                 // it was the old xcomponent building that caused this before.
32171                 // espeically if border is the top element in the tree.
32172                 ret = this;
32173                 break; 
32174                 
32175                     
32176                 
32177                 
32178                 
32179             default:
32180                 /*
32181                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32182                     
32183                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32184                     this.add(region, ret);
32185                 } else {
32186                 */
32187                     Roo.log(cfg);
32188                     throw "Can not add '" + cfg.xtype + "' to Border";
32189                     return null;
32190              
32191                                 
32192              
32193         }
32194         this.beginUpdate();
32195         // add children..
32196         var region = '';
32197         var abn = {};
32198         Roo.each(xitems, function(i)  {
32199             region = nb && i.region ? i.region : false;
32200             
32201             var add = ret.addxtype(i);
32202            
32203             if (region) {
32204                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32205                 if (!i.background) {
32206                     abn[region] = nb[region] ;
32207                 }
32208             }
32209             
32210         });
32211         this.endUpdate();
32212
32213         // make the last non-background panel active..
32214         //if (nb) { Roo.log(abn); }
32215         if (nb) {
32216             
32217             for(var r in abn) {
32218                 region = this.getRegion(r);
32219                 if (region) {
32220                     // tried using nb[r], but it does not work..
32221                      
32222                     region.showPanel(abn[r]);
32223                    
32224                 }
32225             }
32226         }
32227         return ret;
32228         
32229     },
32230     
32231     
32232 // private
32233     factory : function(cfg)
32234     {
32235         
32236         var validRegions = Roo.bootstrap.layout.Border.regions;
32237
32238         var target = cfg.region;
32239         cfg.mgr = this;
32240         
32241         var r = Roo.bootstrap.layout;
32242         Roo.log(target);
32243         switch(target){
32244             case "north":
32245                 return new r.North(cfg);
32246             case "south":
32247                 return new r.South(cfg);
32248             case "east":
32249                 return new r.East(cfg);
32250             case "west":
32251                 return new r.West(cfg);
32252             case "center":
32253                 return new r.Center(cfg);
32254         }
32255         throw 'Layout region "'+target+'" not supported.';
32256     }
32257     
32258     
32259 });
32260  /*
32261  * Based on:
32262  * Ext JS Library 1.1.1
32263  * Copyright(c) 2006-2007, Ext JS, LLC.
32264  *
32265  * Originally Released Under LGPL - original licence link has changed is not relivant.
32266  *
32267  * Fork - LGPL
32268  * <script type="text/javascript">
32269  */
32270  
32271 /**
32272  * @class Roo.bootstrap.layout.Basic
32273  * @extends Roo.util.Observable
32274  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32275  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32276  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32277  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32278  * @cfg {string}   region  the region that it inhabits..
32279  * @cfg {bool}   skipConfig skip config?
32280  * 
32281
32282  */
32283 Roo.bootstrap.layout.Basic = function(config){
32284     
32285     this.mgr = config.mgr;
32286     
32287     this.position = config.region;
32288     
32289     var skipConfig = config.skipConfig;
32290     
32291     this.events = {
32292         /**
32293          * @scope Roo.BasicLayoutRegion
32294          */
32295         
32296         /**
32297          * @event beforeremove
32298          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32299          * @param {Roo.LayoutRegion} this
32300          * @param {Roo.ContentPanel} panel The panel
32301          * @param {Object} e The cancel event object
32302          */
32303         "beforeremove" : true,
32304         /**
32305          * @event invalidated
32306          * Fires when the layout for this region is changed.
32307          * @param {Roo.LayoutRegion} this
32308          */
32309         "invalidated" : true,
32310         /**
32311          * @event visibilitychange
32312          * Fires when this region is shown or hidden 
32313          * @param {Roo.LayoutRegion} this
32314          * @param {Boolean} visibility true or false
32315          */
32316         "visibilitychange" : true,
32317         /**
32318          * @event paneladded
32319          * Fires when a panel is added. 
32320          * @param {Roo.LayoutRegion} this
32321          * @param {Roo.ContentPanel} panel The panel
32322          */
32323         "paneladded" : true,
32324         /**
32325          * @event panelremoved
32326          * Fires when a panel is removed. 
32327          * @param {Roo.LayoutRegion} this
32328          * @param {Roo.ContentPanel} panel The panel
32329          */
32330         "panelremoved" : true,
32331         /**
32332          * @event beforecollapse
32333          * Fires when this region before collapse.
32334          * @param {Roo.LayoutRegion} this
32335          */
32336         "beforecollapse" : true,
32337         /**
32338          * @event collapsed
32339          * Fires when this region is collapsed.
32340          * @param {Roo.LayoutRegion} this
32341          */
32342         "collapsed" : true,
32343         /**
32344          * @event expanded
32345          * Fires when this region is expanded.
32346          * @param {Roo.LayoutRegion} this
32347          */
32348         "expanded" : true,
32349         /**
32350          * @event slideshow
32351          * Fires when this region is slid into view.
32352          * @param {Roo.LayoutRegion} this
32353          */
32354         "slideshow" : true,
32355         /**
32356          * @event slidehide
32357          * Fires when this region slides out of view. 
32358          * @param {Roo.LayoutRegion} this
32359          */
32360         "slidehide" : true,
32361         /**
32362          * @event panelactivated
32363          * Fires when a panel is activated. 
32364          * @param {Roo.LayoutRegion} this
32365          * @param {Roo.ContentPanel} panel The activated panel
32366          */
32367         "panelactivated" : true,
32368         /**
32369          * @event resized
32370          * Fires when the user resizes this region. 
32371          * @param {Roo.LayoutRegion} this
32372          * @param {Number} newSize The new size (width for east/west, height for north/south)
32373          */
32374         "resized" : true
32375     };
32376     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32377     this.panels = new Roo.util.MixedCollection();
32378     this.panels.getKey = this.getPanelId.createDelegate(this);
32379     this.box = null;
32380     this.activePanel = null;
32381     // ensure listeners are added...
32382     
32383     if (config.listeners || config.events) {
32384         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32385             listeners : config.listeners || {},
32386             events : config.events || {}
32387         });
32388     }
32389     
32390     if(skipConfig !== true){
32391         this.applyConfig(config);
32392     }
32393 };
32394
32395 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32396 {
32397     getPanelId : function(p){
32398         return p.getId();
32399     },
32400     
32401     applyConfig : function(config){
32402         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32403         this.config = config;
32404         
32405     },
32406     
32407     /**
32408      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32409      * the width, for horizontal (north, south) the height.
32410      * @param {Number} newSize The new width or height
32411      */
32412     resizeTo : function(newSize){
32413         var el = this.el ? this.el :
32414                  (this.activePanel ? this.activePanel.getEl() : null);
32415         if(el){
32416             switch(this.position){
32417                 case "east":
32418                 case "west":
32419                     el.setWidth(newSize);
32420                     this.fireEvent("resized", this, newSize);
32421                 break;
32422                 case "north":
32423                 case "south":
32424                     el.setHeight(newSize);
32425                     this.fireEvent("resized", this, newSize);
32426                 break;                
32427             }
32428         }
32429     },
32430     
32431     getBox : function(){
32432         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32433     },
32434     
32435     getMargins : function(){
32436         return this.margins;
32437     },
32438     
32439     updateBox : function(box){
32440         this.box = box;
32441         var el = this.activePanel.getEl();
32442         el.dom.style.left = box.x + "px";
32443         el.dom.style.top = box.y + "px";
32444         this.activePanel.setSize(box.width, box.height);
32445     },
32446     
32447     /**
32448      * Returns the container element for this region.
32449      * @return {Roo.Element}
32450      */
32451     getEl : function(){
32452         return this.activePanel;
32453     },
32454     
32455     /**
32456      * Returns true if this region is currently visible.
32457      * @return {Boolean}
32458      */
32459     isVisible : function(){
32460         return this.activePanel ? true : false;
32461     },
32462     
32463     setActivePanel : function(panel){
32464         panel = this.getPanel(panel);
32465         if(this.activePanel && this.activePanel != panel){
32466             this.activePanel.setActiveState(false);
32467             this.activePanel.getEl().setLeftTop(-10000,-10000);
32468         }
32469         this.activePanel = panel;
32470         panel.setActiveState(true);
32471         if(this.box){
32472             panel.setSize(this.box.width, this.box.height);
32473         }
32474         this.fireEvent("panelactivated", this, panel);
32475         this.fireEvent("invalidated");
32476     },
32477     
32478     /**
32479      * Show the specified panel.
32480      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32481      * @return {Roo.ContentPanel} The shown panel or null
32482      */
32483     showPanel : function(panel){
32484         panel = this.getPanel(panel);
32485         if(panel){
32486             this.setActivePanel(panel);
32487         }
32488         return panel;
32489     },
32490     
32491     /**
32492      * Get the active panel for this region.
32493      * @return {Roo.ContentPanel} The active panel or null
32494      */
32495     getActivePanel : function(){
32496         return this.activePanel;
32497     },
32498     
32499     /**
32500      * Add the passed ContentPanel(s)
32501      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32502      * @return {Roo.ContentPanel} The panel added (if only one was added)
32503      */
32504     add : function(panel){
32505         if(arguments.length > 1){
32506             for(var i = 0, len = arguments.length; i < len; i++) {
32507                 this.add(arguments[i]);
32508             }
32509             return null;
32510         }
32511         if(this.hasPanel(panel)){
32512             this.showPanel(panel);
32513             return panel;
32514         }
32515         var el = panel.getEl();
32516         if(el.dom.parentNode != this.mgr.el.dom){
32517             this.mgr.el.dom.appendChild(el.dom);
32518         }
32519         if(panel.setRegion){
32520             panel.setRegion(this);
32521         }
32522         this.panels.add(panel);
32523         el.setStyle("position", "absolute");
32524         if(!panel.background){
32525             this.setActivePanel(panel);
32526             if(this.config.initialSize && this.panels.getCount()==1){
32527                 this.resizeTo(this.config.initialSize);
32528             }
32529         }
32530         this.fireEvent("paneladded", this, panel);
32531         return panel;
32532     },
32533     
32534     /**
32535      * Returns true if the panel is in this region.
32536      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32537      * @return {Boolean}
32538      */
32539     hasPanel : function(panel){
32540         if(typeof panel == "object"){ // must be panel obj
32541             panel = panel.getId();
32542         }
32543         return this.getPanel(panel) ? true : false;
32544     },
32545     
32546     /**
32547      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32548      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32549      * @param {Boolean} preservePanel Overrides the config preservePanel option
32550      * @return {Roo.ContentPanel} The panel that was removed
32551      */
32552     remove : function(panel, preservePanel){
32553         panel = this.getPanel(panel);
32554         if(!panel){
32555             return null;
32556         }
32557         var e = {};
32558         this.fireEvent("beforeremove", this, panel, e);
32559         if(e.cancel === true){
32560             return null;
32561         }
32562         var panelId = panel.getId();
32563         this.panels.removeKey(panelId);
32564         return panel;
32565     },
32566     
32567     /**
32568      * Returns the panel specified or null if it's not in this region.
32569      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32570      * @return {Roo.ContentPanel}
32571      */
32572     getPanel : function(id){
32573         if(typeof id == "object"){ // must be panel obj
32574             return id;
32575         }
32576         return this.panels.get(id);
32577     },
32578     
32579     /**
32580      * Returns this regions position (north/south/east/west/center).
32581      * @return {String} 
32582      */
32583     getPosition: function(){
32584         return this.position;    
32585     }
32586 });/*
32587  * Based on:
32588  * Ext JS Library 1.1.1
32589  * Copyright(c) 2006-2007, Ext JS, LLC.
32590  *
32591  * Originally Released Under LGPL - original licence link has changed is not relivant.
32592  *
32593  * Fork - LGPL
32594  * <script type="text/javascript">
32595  */
32596  
32597 /**
32598  * @class Roo.bootstrap.layout.Region
32599  * @extends Roo.bootstrap.layout.Basic
32600  * This class represents a region in a layout manager.
32601  
32602  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32603  * @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})
32604  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32605  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32606  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32607  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32608  * @cfg {String}    title           The title for the region (overrides panel titles)
32609  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32610  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32611  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32612  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32613  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32614  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32615  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32616  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32617  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32618  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32619
32620  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32621  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32622  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32623  * @cfg {Number}    width           For East/West panels
32624  * @cfg {Number}    height          For North/South panels
32625  * @cfg {Boolean}   split           To show the splitter
32626  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32627  * 
32628  * @cfg {string}   cls             Extra CSS classes to add to region
32629  * 
32630  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32631  * @cfg {string}   region  the region that it inhabits..
32632  *
32633
32634  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32635  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32636
32637  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32638  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32639  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32640  */
32641 Roo.bootstrap.layout.Region = function(config)
32642 {
32643     this.applyConfig(config);
32644
32645     var mgr = config.mgr;
32646     var pos = config.region;
32647     config.skipConfig = true;
32648     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32649     
32650     if (mgr.el) {
32651         this.onRender(mgr.el);   
32652     }
32653      
32654     this.visible = true;
32655     this.collapsed = false;
32656     this.unrendered_panels = [];
32657 };
32658
32659 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32660
32661     position: '', // set by wrapper (eg. north/south etc..)
32662     unrendered_panels : null,  // unrendered panels.
32663     createBody : function(){
32664         /** This region's body element 
32665         * @type Roo.Element */
32666         this.bodyEl = this.el.createChild({
32667                 tag: "div",
32668                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32669         });
32670     },
32671
32672     onRender: function(ctr, pos)
32673     {
32674         var dh = Roo.DomHelper;
32675         /** This region's container element 
32676         * @type Roo.Element */
32677         this.el = dh.append(ctr.dom, {
32678                 tag: "div",
32679                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32680             }, true);
32681         /** This region's title element 
32682         * @type Roo.Element */
32683     
32684         this.titleEl = dh.append(this.el.dom,
32685             {
32686                     tag: "div",
32687                     unselectable: "on",
32688                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32689                     children:[
32690                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32691                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32692                     ]}, true);
32693         
32694         this.titleEl.enableDisplayMode();
32695         /** This region's title text element 
32696         * @type HTMLElement */
32697         this.titleTextEl = this.titleEl.dom.firstChild;
32698         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32699         /*
32700         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32701         this.closeBtn.enableDisplayMode();
32702         this.closeBtn.on("click", this.closeClicked, this);
32703         this.closeBtn.hide();
32704     */
32705         this.createBody(this.config);
32706         if(this.config.hideWhenEmpty){
32707             this.hide();
32708             this.on("paneladded", this.validateVisibility, this);
32709             this.on("panelremoved", this.validateVisibility, this);
32710         }
32711         if(this.autoScroll){
32712             this.bodyEl.setStyle("overflow", "auto");
32713         }else{
32714             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32715         }
32716         //if(c.titlebar !== false){
32717             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32718                 this.titleEl.hide();
32719             }else{
32720                 this.titleEl.show();
32721                 if(this.config.title){
32722                     this.titleTextEl.innerHTML = this.config.title;
32723                 }
32724             }
32725         //}
32726         if(this.config.collapsed){
32727             this.collapse(true);
32728         }
32729         if(this.config.hidden){
32730             this.hide();
32731         }
32732         
32733         if (this.unrendered_panels && this.unrendered_panels.length) {
32734             for (var i =0;i< this.unrendered_panels.length; i++) {
32735                 this.add(this.unrendered_panels[i]);
32736             }
32737             this.unrendered_panels = null;
32738             
32739         }
32740         
32741     },
32742     
32743     applyConfig : function(c)
32744     {
32745         /*
32746          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32747             var dh = Roo.DomHelper;
32748             if(c.titlebar !== false){
32749                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32750                 this.collapseBtn.on("click", this.collapse, this);
32751                 this.collapseBtn.enableDisplayMode();
32752                 /*
32753                 if(c.showPin === true || this.showPin){
32754                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32755                     this.stickBtn.enableDisplayMode();
32756                     this.stickBtn.on("click", this.expand, this);
32757                     this.stickBtn.hide();
32758                 }
32759                 
32760             }
32761             */
32762             /** This region's collapsed element
32763             * @type Roo.Element */
32764             /*
32765              *
32766             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32767                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32768             ]}, true);
32769             
32770             if(c.floatable !== false){
32771                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32772                this.collapsedEl.on("click", this.collapseClick, this);
32773             }
32774
32775             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32776                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32777                    id: "message", unselectable: "on", style:{"float":"left"}});
32778                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32779              }
32780             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32781             this.expandBtn.on("click", this.expand, this);
32782             
32783         }
32784         
32785         if(this.collapseBtn){
32786             this.collapseBtn.setVisible(c.collapsible == true);
32787         }
32788         
32789         this.cmargins = c.cmargins || this.cmargins ||
32790                          (this.position == "west" || this.position == "east" ?
32791                              {top: 0, left: 2, right:2, bottom: 0} :
32792                              {top: 2, left: 0, right:0, bottom: 2});
32793         */
32794         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32795         
32796         
32797         this.bottomTabs = c.tabPosition != "top";
32798         
32799         this.autoScroll = c.autoScroll || false;
32800         
32801         
32802        
32803         
32804         this.duration = c.duration || .30;
32805         this.slideDuration = c.slideDuration || .45;
32806         this.config = c;
32807        
32808     },
32809     /**
32810      * Returns true if this region is currently visible.
32811      * @return {Boolean}
32812      */
32813     isVisible : function(){
32814         return this.visible;
32815     },
32816
32817     /**
32818      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32819      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32820      */
32821     //setCollapsedTitle : function(title){
32822     //    title = title || "&#160;";
32823      //   if(this.collapsedTitleTextEl){
32824       //      this.collapsedTitleTextEl.innerHTML = title;
32825        // }
32826     //},
32827
32828     getBox : function(){
32829         var b;
32830       //  if(!this.collapsed){
32831             b = this.el.getBox(false, true);
32832        // }else{
32833           //  b = this.collapsedEl.getBox(false, true);
32834         //}
32835         return b;
32836     },
32837
32838     getMargins : function(){
32839         return this.margins;
32840         //return this.collapsed ? this.cmargins : this.margins;
32841     },
32842 /*
32843     highlight : function(){
32844         this.el.addClass("x-layout-panel-dragover");
32845     },
32846
32847     unhighlight : function(){
32848         this.el.removeClass("x-layout-panel-dragover");
32849     },
32850 */
32851     updateBox : function(box)
32852     {
32853         if (!this.bodyEl) {
32854             return; // not rendered yet..
32855         }
32856         
32857         this.box = box;
32858         if(!this.collapsed){
32859             this.el.dom.style.left = box.x + "px";
32860             this.el.dom.style.top = box.y + "px";
32861             this.updateBody(box.width, box.height);
32862         }else{
32863             this.collapsedEl.dom.style.left = box.x + "px";
32864             this.collapsedEl.dom.style.top = box.y + "px";
32865             this.collapsedEl.setSize(box.width, box.height);
32866         }
32867         if(this.tabs){
32868             this.tabs.autoSizeTabs();
32869         }
32870     },
32871
32872     updateBody : function(w, h)
32873     {
32874         if(w !== null){
32875             this.el.setWidth(w);
32876             w -= this.el.getBorderWidth("rl");
32877             if(this.config.adjustments){
32878                 w += this.config.adjustments[0];
32879             }
32880         }
32881         if(h !== null && h > 0){
32882             this.el.setHeight(h);
32883             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32884             h -= this.el.getBorderWidth("tb");
32885             if(this.config.adjustments){
32886                 h += this.config.adjustments[1];
32887             }
32888             this.bodyEl.setHeight(h);
32889             if(this.tabs){
32890                 h = this.tabs.syncHeight(h);
32891             }
32892         }
32893         if(this.panelSize){
32894             w = w !== null ? w : this.panelSize.width;
32895             h = h !== null ? h : this.panelSize.height;
32896         }
32897         if(this.activePanel){
32898             var el = this.activePanel.getEl();
32899             w = w !== null ? w : el.getWidth();
32900             h = h !== null ? h : el.getHeight();
32901             this.panelSize = {width: w, height: h};
32902             this.activePanel.setSize(w, h);
32903         }
32904         if(Roo.isIE && this.tabs){
32905             this.tabs.el.repaint();
32906         }
32907     },
32908
32909     /**
32910      * Returns the container element for this region.
32911      * @return {Roo.Element}
32912      */
32913     getEl : function(){
32914         return this.el;
32915     },
32916
32917     /**
32918      * Hides this region.
32919      */
32920     hide : function(){
32921         //if(!this.collapsed){
32922             this.el.dom.style.left = "-2000px";
32923             this.el.hide();
32924         //}else{
32925          //   this.collapsedEl.dom.style.left = "-2000px";
32926          //   this.collapsedEl.hide();
32927        // }
32928         this.visible = false;
32929         this.fireEvent("visibilitychange", this, false);
32930     },
32931
32932     /**
32933      * Shows this region if it was previously hidden.
32934      */
32935     show : function(){
32936         //if(!this.collapsed){
32937             this.el.show();
32938         //}else{
32939         //    this.collapsedEl.show();
32940        // }
32941         this.visible = true;
32942         this.fireEvent("visibilitychange", this, true);
32943     },
32944 /*
32945     closeClicked : function(){
32946         if(this.activePanel){
32947             this.remove(this.activePanel);
32948         }
32949     },
32950
32951     collapseClick : function(e){
32952         if(this.isSlid){
32953            e.stopPropagation();
32954            this.slideIn();
32955         }else{
32956            e.stopPropagation();
32957            this.slideOut();
32958         }
32959     },
32960 */
32961     /**
32962      * Collapses this region.
32963      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32964      */
32965     /*
32966     collapse : function(skipAnim, skipCheck = false){
32967         if(this.collapsed) {
32968             return;
32969         }
32970         
32971         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32972             
32973             this.collapsed = true;
32974             if(this.split){
32975                 this.split.el.hide();
32976             }
32977             if(this.config.animate && skipAnim !== true){
32978                 this.fireEvent("invalidated", this);
32979                 this.animateCollapse();
32980             }else{
32981                 this.el.setLocation(-20000,-20000);
32982                 this.el.hide();
32983                 this.collapsedEl.show();
32984                 this.fireEvent("collapsed", this);
32985                 this.fireEvent("invalidated", this);
32986             }
32987         }
32988         
32989     },
32990 */
32991     animateCollapse : function(){
32992         // overridden
32993     },
32994
32995     /**
32996      * Expands this region if it was previously collapsed.
32997      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32998      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32999      */
33000     /*
33001     expand : function(e, skipAnim){
33002         if(e) {
33003             e.stopPropagation();
33004         }
33005         if(!this.collapsed || this.el.hasActiveFx()) {
33006             return;
33007         }
33008         if(this.isSlid){
33009             this.afterSlideIn();
33010             skipAnim = true;
33011         }
33012         this.collapsed = false;
33013         if(this.config.animate && skipAnim !== true){
33014             this.animateExpand();
33015         }else{
33016             this.el.show();
33017             if(this.split){
33018                 this.split.el.show();
33019             }
33020             this.collapsedEl.setLocation(-2000,-2000);
33021             this.collapsedEl.hide();
33022             this.fireEvent("invalidated", this);
33023             this.fireEvent("expanded", this);
33024         }
33025     },
33026 */
33027     animateExpand : function(){
33028         // overridden
33029     },
33030
33031     initTabs : function()
33032     {
33033         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33034         
33035         var ts = new Roo.bootstrap.panel.Tabs({
33036                 el: this.bodyEl.dom,
33037                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33038                 disableTooltips: this.config.disableTabTips,
33039                 toolbar : this.config.toolbar
33040             });
33041         
33042         if(this.config.hideTabs){
33043             ts.stripWrap.setDisplayed(false);
33044         }
33045         this.tabs = ts;
33046         ts.resizeTabs = this.config.resizeTabs === true;
33047         ts.minTabWidth = this.config.minTabWidth || 40;
33048         ts.maxTabWidth = this.config.maxTabWidth || 250;
33049         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33050         ts.monitorResize = false;
33051         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33052         ts.bodyEl.addClass('roo-layout-tabs-body');
33053         this.panels.each(this.initPanelAsTab, this);
33054     },
33055
33056     initPanelAsTab : function(panel){
33057         var ti = this.tabs.addTab(
33058                     panel.getEl().id,
33059                     panel.getTitle(),
33060                     null,
33061                     this.config.closeOnTab && panel.isClosable()
33062             );
33063         if(panel.tabTip !== undefined){
33064             ti.setTooltip(panel.tabTip);
33065         }
33066         ti.on("activate", function(){
33067               this.setActivePanel(panel);
33068         }, this);
33069         
33070         if(this.config.closeOnTab){
33071             ti.on("beforeclose", function(t, e){
33072                 e.cancel = true;
33073                 this.remove(panel);
33074             }, this);
33075         }
33076         return ti;
33077     },
33078
33079     updatePanelTitle : function(panel, title)
33080     {
33081         if(this.activePanel == panel){
33082             this.updateTitle(title);
33083         }
33084         if(this.tabs){
33085             var ti = this.tabs.getTab(panel.getEl().id);
33086             ti.setText(title);
33087             if(panel.tabTip !== undefined){
33088                 ti.setTooltip(panel.tabTip);
33089             }
33090         }
33091     },
33092
33093     updateTitle : function(title){
33094         if(this.titleTextEl && !this.config.title){
33095             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33096         }
33097     },
33098
33099     setActivePanel : function(panel)
33100     {
33101         panel = this.getPanel(panel);
33102         if(this.activePanel && this.activePanel != panel){
33103             this.activePanel.setActiveState(false);
33104         }
33105         this.activePanel = panel;
33106         panel.setActiveState(true);
33107         if(this.panelSize){
33108             panel.setSize(this.panelSize.width, this.panelSize.height);
33109         }
33110         if(this.closeBtn){
33111             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33112         }
33113         this.updateTitle(panel.getTitle());
33114         if(this.tabs){
33115             this.fireEvent("invalidated", this);
33116         }
33117         this.fireEvent("panelactivated", this, panel);
33118     },
33119
33120     /**
33121      * Shows the specified panel.
33122      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33123      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33124      */
33125     showPanel : function(panel)
33126     {
33127         panel = this.getPanel(panel);
33128         if(panel){
33129             if(this.tabs){
33130                 var tab = this.tabs.getTab(panel.getEl().id);
33131                 if(tab.isHidden()){
33132                     this.tabs.unhideTab(tab.id);
33133                 }
33134                 tab.activate();
33135             }else{
33136                 this.setActivePanel(panel);
33137             }
33138         }
33139         return panel;
33140     },
33141
33142     /**
33143      * Get the active panel for this region.
33144      * @return {Roo.ContentPanel} The active panel or null
33145      */
33146     getActivePanel : function(){
33147         return this.activePanel;
33148     },
33149
33150     validateVisibility : function(){
33151         if(this.panels.getCount() < 1){
33152             this.updateTitle("&#160;");
33153             this.closeBtn.hide();
33154             this.hide();
33155         }else{
33156             if(!this.isVisible()){
33157                 this.show();
33158             }
33159         }
33160     },
33161
33162     /**
33163      * Adds the passed ContentPanel(s) to this region.
33164      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33165      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33166      */
33167     add : function(panel)
33168     {
33169         if(arguments.length > 1){
33170             for(var i = 0, len = arguments.length; i < len; i++) {
33171                 this.add(arguments[i]);
33172             }
33173             return null;
33174         }
33175         
33176         // if we have not been rendered yet, then we can not really do much of this..
33177         if (!this.bodyEl) {
33178             this.unrendered_panels.push(panel);
33179             return panel;
33180         }
33181         
33182         
33183         
33184         
33185         if(this.hasPanel(panel)){
33186             this.showPanel(panel);
33187             return panel;
33188         }
33189         panel.setRegion(this);
33190         this.panels.add(panel);
33191        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33192             // sinle panel - no tab...?? would it not be better to render it with the tabs,
33193             // and hide them... ???
33194             this.bodyEl.dom.appendChild(panel.getEl().dom);
33195             if(panel.background !== true){
33196                 this.setActivePanel(panel);
33197             }
33198             this.fireEvent("paneladded", this, panel);
33199             return panel;
33200         }
33201         */
33202         if(!this.tabs){
33203             this.initTabs();
33204         }else{
33205             this.initPanelAsTab(panel);
33206         }
33207         
33208         
33209         if(panel.background !== true){
33210             this.tabs.activate(panel.getEl().id);
33211         }
33212         this.fireEvent("paneladded", this, panel);
33213         return panel;
33214     },
33215
33216     /**
33217      * Hides the tab for the specified panel.
33218      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33219      */
33220     hidePanel : function(panel){
33221         if(this.tabs && (panel = this.getPanel(panel))){
33222             this.tabs.hideTab(panel.getEl().id);
33223         }
33224     },
33225
33226     /**
33227      * Unhides the tab for a previously hidden panel.
33228      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33229      */
33230     unhidePanel : function(panel){
33231         if(this.tabs && (panel = this.getPanel(panel))){
33232             this.tabs.unhideTab(panel.getEl().id);
33233         }
33234     },
33235
33236     clearPanels : function(){
33237         while(this.panels.getCount() > 0){
33238              this.remove(this.panels.first());
33239         }
33240     },
33241
33242     /**
33243      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33244      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33245      * @param {Boolean} preservePanel Overrides the config preservePanel option
33246      * @return {Roo.ContentPanel} The panel that was removed
33247      */
33248     remove : function(panel, preservePanel)
33249     {
33250         panel = this.getPanel(panel);
33251         if(!panel){
33252             return null;
33253         }
33254         var e = {};
33255         this.fireEvent("beforeremove", this, panel, e);
33256         if(e.cancel === true){
33257             return null;
33258         }
33259         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33260         var panelId = panel.getId();
33261         this.panels.removeKey(panelId);
33262         if(preservePanel){
33263             document.body.appendChild(panel.getEl().dom);
33264         }
33265         if(this.tabs){
33266             this.tabs.removeTab(panel.getEl().id);
33267         }else if (!preservePanel){
33268             this.bodyEl.dom.removeChild(panel.getEl().dom);
33269         }
33270         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33271             var p = this.panels.first();
33272             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33273             tempEl.appendChild(p.getEl().dom);
33274             this.bodyEl.update("");
33275             this.bodyEl.dom.appendChild(p.getEl().dom);
33276             tempEl = null;
33277             this.updateTitle(p.getTitle());
33278             this.tabs = null;
33279             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33280             this.setActivePanel(p);
33281         }
33282         panel.setRegion(null);
33283         if(this.activePanel == panel){
33284             this.activePanel = null;
33285         }
33286         if(this.config.autoDestroy !== false && preservePanel !== true){
33287             try{panel.destroy();}catch(e){}
33288         }
33289         this.fireEvent("panelremoved", this, panel);
33290         return panel;
33291     },
33292
33293     /**
33294      * Returns the TabPanel component used by this region
33295      * @return {Roo.TabPanel}
33296      */
33297     getTabs : function(){
33298         return this.tabs;
33299     },
33300
33301     createTool : function(parentEl, className){
33302         var btn = Roo.DomHelper.append(parentEl, {
33303             tag: "div",
33304             cls: "x-layout-tools-button",
33305             children: [ {
33306                 tag: "div",
33307                 cls: "roo-layout-tools-button-inner " + className,
33308                 html: "&#160;"
33309             }]
33310         }, true);
33311         btn.addClassOnOver("roo-layout-tools-button-over");
33312         return btn;
33313     }
33314 });/*
33315  * Based on:
33316  * Ext JS Library 1.1.1
33317  * Copyright(c) 2006-2007, Ext JS, LLC.
33318  *
33319  * Originally Released Under LGPL - original licence link has changed is not relivant.
33320  *
33321  * Fork - LGPL
33322  * <script type="text/javascript">
33323  */
33324  
33325
33326
33327 /**
33328  * @class Roo.SplitLayoutRegion
33329  * @extends Roo.LayoutRegion
33330  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33331  */
33332 Roo.bootstrap.layout.Split = function(config){
33333     this.cursor = config.cursor;
33334     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33335 };
33336
33337 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33338 {
33339     splitTip : "Drag to resize.",
33340     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33341     useSplitTips : false,
33342
33343     applyConfig : function(config){
33344         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33345     },
33346     
33347     onRender : function(ctr,pos) {
33348         
33349         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33350         if(!this.config.split){
33351             return;
33352         }
33353         if(!this.split){
33354             
33355             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33356                             tag: "div",
33357                             id: this.el.id + "-split",
33358                             cls: "roo-layout-split roo-layout-split-"+this.position,
33359                             html: "&#160;"
33360             });
33361             /** The SplitBar for this region 
33362             * @type Roo.SplitBar */
33363             // does not exist yet...
33364             Roo.log([this.position, this.orientation]);
33365             
33366             this.split = new Roo.bootstrap.SplitBar({
33367                 dragElement : splitEl,
33368                 resizingElement: this.el,
33369                 orientation : this.orientation
33370             });
33371             
33372             this.split.on("moved", this.onSplitMove, this);
33373             this.split.useShim = this.config.useShim === true;
33374             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33375             if(this.useSplitTips){
33376                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33377             }
33378             //if(config.collapsible){
33379             //    this.split.el.on("dblclick", this.collapse,  this);
33380             //}
33381         }
33382         if(typeof this.config.minSize != "undefined"){
33383             this.split.minSize = this.config.minSize;
33384         }
33385         if(typeof this.config.maxSize != "undefined"){
33386             this.split.maxSize = this.config.maxSize;
33387         }
33388         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33389             this.hideSplitter();
33390         }
33391         
33392     },
33393
33394     getHMaxSize : function(){
33395          var cmax = this.config.maxSize || 10000;
33396          var center = this.mgr.getRegion("center");
33397          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33398     },
33399
33400     getVMaxSize : function(){
33401          var cmax = this.config.maxSize || 10000;
33402          var center = this.mgr.getRegion("center");
33403          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33404     },
33405
33406     onSplitMove : function(split, newSize){
33407         this.fireEvent("resized", this, newSize);
33408     },
33409     
33410     /** 
33411      * Returns the {@link Roo.SplitBar} for this region.
33412      * @return {Roo.SplitBar}
33413      */
33414     getSplitBar : function(){
33415         return this.split;
33416     },
33417     
33418     hide : function(){
33419         this.hideSplitter();
33420         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33421     },
33422
33423     hideSplitter : function(){
33424         if(this.split){
33425             this.split.el.setLocation(-2000,-2000);
33426             this.split.el.hide();
33427         }
33428     },
33429
33430     show : function(){
33431         if(this.split){
33432             this.split.el.show();
33433         }
33434         Roo.bootstrap.layout.Split.superclass.show.call(this);
33435     },
33436     
33437     beforeSlide: function(){
33438         if(Roo.isGecko){// firefox overflow auto bug workaround
33439             this.bodyEl.clip();
33440             if(this.tabs) {
33441                 this.tabs.bodyEl.clip();
33442             }
33443             if(this.activePanel){
33444                 this.activePanel.getEl().clip();
33445                 
33446                 if(this.activePanel.beforeSlide){
33447                     this.activePanel.beforeSlide();
33448                 }
33449             }
33450         }
33451     },
33452     
33453     afterSlide : function(){
33454         if(Roo.isGecko){// firefox overflow auto bug workaround
33455             this.bodyEl.unclip();
33456             if(this.tabs) {
33457                 this.tabs.bodyEl.unclip();
33458             }
33459             if(this.activePanel){
33460                 this.activePanel.getEl().unclip();
33461                 if(this.activePanel.afterSlide){
33462                     this.activePanel.afterSlide();
33463                 }
33464             }
33465         }
33466     },
33467
33468     initAutoHide : function(){
33469         if(this.autoHide !== false){
33470             if(!this.autoHideHd){
33471                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33472                 this.autoHideHd = {
33473                     "mouseout": function(e){
33474                         if(!e.within(this.el, true)){
33475                             st.delay(500);
33476                         }
33477                     },
33478                     "mouseover" : function(e){
33479                         st.cancel();
33480                     },
33481                     scope : this
33482                 };
33483             }
33484             this.el.on(this.autoHideHd);
33485         }
33486     },
33487
33488     clearAutoHide : function(){
33489         if(this.autoHide !== false){
33490             this.el.un("mouseout", this.autoHideHd.mouseout);
33491             this.el.un("mouseover", this.autoHideHd.mouseover);
33492         }
33493     },
33494
33495     clearMonitor : function(){
33496         Roo.get(document).un("click", this.slideInIf, this);
33497     },
33498
33499     // these names are backwards but not changed for compat
33500     slideOut : function(){
33501         if(this.isSlid || this.el.hasActiveFx()){
33502             return;
33503         }
33504         this.isSlid = true;
33505         if(this.collapseBtn){
33506             this.collapseBtn.hide();
33507         }
33508         this.closeBtnState = this.closeBtn.getStyle('display');
33509         this.closeBtn.hide();
33510         if(this.stickBtn){
33511             this.stickBtn.show();
33512         }
33513         this.el.show();
33514         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33515         this.beforeSlide();
33516         this.el.setStyle("z-index", 10001);
33517         this.el.slideIn(this.getSlideAnchor(), {
33518             callback: function(){
33519                 this.afterSlide();
33520                 this.initAutoHide();
33521                 Roo.get(document).on("click", this.slideInIf, this);
33522                 this.fireEvent("slideshow", this);
33523             },
33524             scope: this,
33525             block: true
33526         });
33527     },
33528
33529     afterSlideIn : function(){
33530         this.clearAutoHide();
33531         this.isSlid = false;
33532         this.clearMonitor();
33533         this.el.setStyle("z-index", "");
33534         if(this.collapseBtn){
33535             this.collapseBtn.show();
33536         }
33537         this.closeBtn.setStyle('display', this.closeBtnState);
33538         if(this.stickBtn){
33539             this.stickBtn.hide();
33540         }
33541         this.fireEvent("slidehide", this);
33542     },
33543
33544     slideIn : function(cb){
33545         if(!this.isSlid || this.el.hasActiveFx()){
33546             Roo.callback(cb);
33547             return;
33548         }
33549         this.isSlid = false;
33550         this.beforeSlide();
33551         this.el.slideOut(this.getSlideAnchor(), {
33552             callback: function(){
33553                 this.el.setLeftTop(-10000, -10000);
33554                 this.afterSlide();
33555                 this.afterSlideIn();
33556                 Roo.callback(cb);
33557             },
33558             scope: this,
33559             block: true
33560         });
33561     },
33562     
33563     slideInIf : function(e){
33564         if(!e.within(this.el)){
33565             this.slideIn();
33566         }
33567     },
33568
33569     animateCollapse : function(){
33570         this.beforeSlide();
33571         this.el.setStyle("z-index", 20000);
33572         var anchor = this.getSlideAnchor();
33573         this.el.slideOut(anchor, {
33574             callback : function(){
33575                 this.el.setStyle("z-index", "");
33576                 this.collapsedEl.slideIn(anchor, {duration:.3});
33577                 this.afterSlide();
33578                 this.el.setLocation(-10000,-10000);
33579                 this.el.hide();
33580                 this.fireEvent("collapsed", this);
33581             },
33582             scope: this,
33583             block: true
33584         });
33585     },
33586
33587     animateExpand : function(){
33588         this.beforeSlide();
33589         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33590         this.el.setStyle("z-index", 20000);
33591         this.collapsedEl.hide({
33592             duration:.1
33593         });
33594         this.el.slideIn(this.getSlideAnchor(), {
33595             callback : function(){
33596                 this.el.setStyle("z-index", "");
33597                 this.afterSlide();
33598                 if(this.split){
33599                     this.split.el.show();
33600                 }
33601                 this.fireEvent("invalidated", this);
33602                 this.fireEvent("expanded", this);
33603             },
33604             scope: this,
33605             block: true
33606         });
33607     },
33608
33609     anchors : {
33610         "west" : "left",
33611         "east" : "right",
33612         "north" : "top",
33613         "south" : "bottom"
33614     },
33615
33616     sanchors : {
33617         "west" : "l",
33618         "east" : "r",
33619         "north" : "t",
33620         "south" : "b"
33621     },
33622
33623     canchors : {
33624         "west" : "tl-tr",
33625         "east" : "tr-tl",
33626         "north" : "tl-bl",
33627         "south" : "bl-tl"
33628     },
33629
33630     getAnchor : function(){
33631         return this.anchors[this.position];
33632     },
33633
33634     getCollapseAnchor : function(){
33635         return this.canchors[this.position];
33636     },
33637
33638     getSlideAnchor : function(){
33639         return this.sanchors[this.position];
33640     },
33641
33642     getAlignAdj : function(){
33643         var cm = this.cmargins;
33644         switch(this.position){
33645             case "west":
33646                 return [0, 0];
33647             break;
33648             case "east":
33649                 return [0, 0];
33650             break;
33651             case "north":
33652                 return [0, 0];
33653             break;
33654             case "south":
33655                 return [0, 0];
33656             break;
33657         }
33658     },
33659
33660     getExpandAdj : function(){
33661         var c = this.collapsedEl, cm = this.cmargins;
33662         switch(this.position){
33663             case "west":
33664                 return [-(cm.right+c.getWidth()+cm.left), 0];
33665             break;
33666             case "east":
33667                 return [cm.right+c.getWidth()+cm.left, 0];
33668             break;
33669             case "north":
33670                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33671             break;
33672             case "south":
33673                 return [0, cm.top+cm.bottom+c.getHeight()];
33674             break;
33675         }
33676     }
33677 });/*
33678  * Based on:
33679  * Ext JS Library 1.1.1
33680  * Copyright(c) 2006-2007, Ext JS, LLC.
33681  *
33682  * Originally Released Under LGPL - original licence link has changed is not relivant.
33683  *
33684  * Fork - LGPL
33685  * <script type="text/javascript">
33686  */
33687 /*
33688  * These classes are private internal classes
33689  */
33690 Roo.bootstrap.layout.Center = function(config){
33691     config.region = "center";
33692     Roo.bootstrap.layout.Region.call(this, config);
33693     this.visible = true;
33694     this.minWidth = config.minWidth || 20;
33695     this.minHeight = config.minHeight || 20;
33696 };
33697
33698 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33699     hide : function(){
33700         // center panel can't be hidden
33701     },
33702     
33703     show : function(){
33704         // center panel can't be hidden
33705     },
33706     
33707     getMinWidth: function(){
33708         return this.minWidth;
33709     },
33710     
33711     getMinHeight: function(){
33712         return this.minHeight;
33713     }
33714 });
33715
33716
33717
33718
33719  
33720
33721
33722
33723
33724
33725 Roo.bootstrap.layout.North = function(config)
33726 {
33727     config.region = 'north';
33728     config.cursor = 'n-resize';
33729     
33730     Roo.bootstrap.layout.Split.call(this, config);
33731     
33732     
33733     if(this.split){
33734         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33735         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33736         this.split.el.addClass("roo-layout-split-v");
33737     }
33738     var size = config.initialSize || config.height;
33739     if(typeof size != "undefined"){
33740         this.el.setHeight(size);
33741     }
33742 };
33743 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33744 {
33745     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33746     
33747     
33748     
33749     getBox : function(){
33750         if(this.collapsed){
33751             return this.collapsedEl.getBox();
33752         }
33753         var box = this.el.getBox();
33754         if(this.split){
33755             box.height += this.split.el.getHeight();
33756         }
33757         return box;
33758     },
33759     
33760     updateBox : function(box){
33761         if(this.split && !this.collapsed){
33762             box.height -= this.split.el.getHeight();
33763             this.split.el.setLeft(box.x);
33764             this.split.el.setTop(box.y+box.height);
33765             this.split.el.setWidth(box.width);
33766         }
33767         if(this.collapsed){
33768             this.updateBody(box.width, null);
33769         }
33770         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33771     }
33772 });
33773
33774
33775
33776
33777
33778 Roo.bootstrap.layout.South = function(config){
33779     config.region = 'south';
33780     config.cursor = 's-resize';
33781     Roo.bootstrap.layout.Split.call(this, config);
33782     if(this.split){
33783         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33784         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33785         this.split.el.addClass("roo-layout-split-v");
33786     }
33787     var size = config.initialSize || config.height;
33788     if(typeof size != "undefined"){
33789         this.el.setHeight(size);
33790     }
33791 };
33792
33793 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33794     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33795     getBox : function(){
33796         if(this.collapsed){
33797             return this.collapsedEl.getBox();
33798         }
33799         var box = this.el.getBox();
33800         if(this.split){
33801             var sh = this.split.el.getHeight();
33802             box.height += sh;
33803             box.y -= sh;
33804         }
33805         return box;
33806     },
33807     
33808     updateBox : function(box){
33809         if(this.split && !this.collapsed){
33810             var sh = this.split.el.getHeight();
33811             box.height -= sh;
33812             box.y += sh;
33813             this.split.el.setLeft(box.x);
33814             this.split.el.setTop(box.y-sh);
33815             this.split.el.setWidth(box.width);
33816         }
33817         if(this.collapsed){
33818             this.updateBody(box.width, null);
33819         }
33820         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33821     }
33822 });
33823
33824 Roo.bootstrap.layout.East = function(config){
33825     config.region = "east";
33826     config.cursor = "e-resize";
33827     Roo.bootstrap.layout.Split.call(this, config);
33828     if(this.split){
33829         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33830         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33831         this.split.el.addClass("roo-layout-split-h");
33832     }
33833     var size = config.initialSize || config.width;
33834     if(typeof size != "undefined"){
33835         this.el.setWidth(size);
33836     }
33837 };
33838 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33839     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33840     getBox : function(){
33841         if(this.collapsed){
33842             return this.collapsedEl.getBox();
33843         }
33844         var box = this.el.getBox();
33845         if(this.split){
33846             var sw = this.split.el.getWidth();
33847             box.width += sw;
33848             box.x -= sw;
33849         }
33850         return box;
33851     },
33852
33853     updateBox : function(box){
33854         if(this.split && !this.collapsed){
33855             var sw = this.split.el.getWidth();
33856             box.width -= sw;
33857             this.split.el.setLeft(box.x);
33858             this.split.el.setTop(box.y);
33859             this.split.el.setHeight(box.height);
33860             box.x += sw;
33861         }
33862         if(this.collapsed){
33863             this.updateBody(null, box.height);
33864         }
33865         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33866     }
33867 });
33868
33869 Roo.bootstrap.layout.West = function(config){
33870     config.region = "west";
33871     config.cursor = "w-resize";
33872     
33873     Roo.bootstrap.layout.Split.call(this, config);
33874     if(this.split){
33875         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33876         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33877         this.split.el.addClass("roo-layout-split-h");
33878     }
33879     
33880 };
33881 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33882     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33883     
33884     onRender: function(ctr, pos)
33885     {
33886         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33887         var size = this.config.initialSize || this.config.width;
33888         if(typeof size != "undefined"){
33889             this.el.setWidth(size);
33890         }
33891     },
33892     
33893     getBox : function(){
33894         if(this.collapsed){
33895             return this.collapsedEl.getBox();
33896         }
33897         var box = this.el.getBox();
33898         if(this.split){
33899             box.width += this.split.el.getWidth();
33900         }
33901         return box;
33902     },
33903     
33904     updateBox : function(box){
33905         if(this.split && !this.collapsed){
33906             var sw = this.split.el.getWidth();
33907             box.width -= sw;
33908             this.split.el.setLeft(box.x+box.width);
33909             this.split.el.setTop(box.y);
33910             this.split.el.setHeight(box.height);
33911         }
33912         if(this.collapsed){
33913             this.updateBody(null, box.height);
33914         }
33915         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33916     }
33917 });
33918 Roo.namespace("Roo.bootstrap.panel");/*
33919  * Based on:
33920  * Ext JS Library 1.1.1
33921  * Copyright(c) 2006-2007, Ext JS, LLC.
33922  *
33923  * Originally Released Under LGPL - original licence link has changed is not relivant.
33924  *
33925  * Fork - LGPL
33926  * <script type="text/javascript">
33927  */
33928 /**
33929  * @class Roo.ContentPanel
33930  * @extends Roo.util.Observable
33931  * A basic ContentPanel element.
33932  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33933  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33934  * @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
33935  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33936  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33937  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33938  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33939  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33940  * @cfg {String} title          The title for this panel
33941  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33942  * @cfg {String} url            Calls {@link #setUrl} with this value
33943  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33944  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33945  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33946  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33947
33948  * @constructor
33949  * Create a new ContentPanel.
33950  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33951  * @param {String/Object} config A string to set only the title or a config object
33952  * @param {String} content (optional) Set the HTML content for this panel
33953  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33954  */
33955 Roo.bootstrap.panel.Content = function( config){
33956     
33957     var el = config.el;
33958     var content = config.content;
33959
33960     if(config.autoCreate){ // xtype is available if this is called from factory
33961         el = Roo.id();
33962     }
33963     this.el = Roo.get(el);
33964     if(!this.el && config && config.autoCreate){
33965         if(typeof config.autoCreate == "object"){
33966             if(!config.autoCreate.id){
33967                 config.autoCreate.id = config.id||el;
33968             }
33969             this.el = Roo.DomHelper.append(document.body,
33970                         config.autoCreate, true);
33971         }else{
33972             var elcfg =  {   tag: "div",
33973                             cls: "roo-layout-inactive-content",
33974                             id: config.id||el
33975                             };
33976             if (config.html) {
33977                 elcfg.html = config.html;
33978                 
33979             }
33980                         
33981             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33982         }
33983     } 
33984     this.closable = false;
33985     this.loaded = false;
33986     this.active = false;
33987    
33988       
33989     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33990         
33991         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33992         
33993         this.wrapEl = this.el.wrap();
33994         var ti = [];
33995         if (config.toolbar.items) {
33996             ti = config.toolbar.items ;
33997             delete config.toolbar.items ;
33998         }
33999         
34000         var nitems = [];
34001         this.toolbar.render(this.wrapEl, 'before');
34002         for(var i =0;i < ti.length;i++) {
34003           //  Roo.log(['add child', items[i]]);
34004             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34005         }
34006         this.toolbar.items = nitems;
34007         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34008         delete config.toolbar;
34009         
34010     }
34011     /*
34012     // xtype created footer. - not sure if will work as we normally have to render first..
34013     if (this.footer && !this.footer.el && this.footer.xtype) {
34014         if (!this.wrapEl) {
34015             this.wrapEl = this.el.wrap();
34016         }
34017     
34018         this.footer.container = this.wrapEl.createChild();
34019          
34020         this.footer = Roo.factory(this.footer, Roo);
34021         
34022     }
34023     */
34024     
34025      if(typeof config == "string"){
34026         this.title = config;
34027     }else{
34028         Roo.apply(this, config);
34029     }
34030     
34031     if(this.resizeEl){
34032         this.resizeEl = Roo.get(this.resizeEl, true);
34033     }else{
34034         this.resizeEl = this.el;
34035     }
34036     // handle view.xtype
34037     
34038  
34039     
34040     
34041     this.addEvents({
34042         /**
34043          * @event activate
34044          * Fires when this panel is activated. 
34045          * @param {Roo.ContentPanel} this
34046          */
34047         "activate" : true,
34048         /**
34049          * @event deactivate
34050          * Fires when this panel is activated. 
34051          * @param {Roo.ContentPanel} this
34052          */
34053         "deactivate" : true,
34054
34055         /**
34056          * @event resize
34057          * Fires when this panel is resized if fitToFrame is true.
34058          * @param {Roo.ContentPanel} this
34059          * @param {Number} width The width after any component adjustments
34060          * @param {Number} height The height after any component adjustments
34061          */
34062         "resize" : true,
34063         
34064          /**
34065          * @event render
34066          * Fires when this tab is created
34067          * @param {Roo.ContentPanel} this
34068          */
34069         "render" : true
34070         
34071         
34072         
34073     });
34074     
34075
34076     
34077     
34078     if(this.autoScroll){
34079         this.resizeEl.setStyle("overflow", "auto");
34080     } else {
34081         // fix randome scrolling
34082         //this.el.on('scroll', function() {
34083         //    Roo.log('fix random scolling');
34084         //    this.scrollTo('top',0); 
34085         //});
34086     }
34087     content = content || this.content;
34088     if(content){
34089         this.setContent(content);
34090     }
34091     if(config && config.url){
34092         this.setUrl(this.url, this.params, this.loadOnce);
34093     }
34094     
34095     
34096     
34097     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34098     
34099     if (this.view && typeof(this.view.xtype) != 'undefined') {
34100         this.view.el = this.el.appendChild(document.createElement("div"));
34101         this.view = Roo.factory(this.view); 
34102         this.view.render  &&  this.view.render(false, '');  
34103     }
34104     
34105     
34106     this.fireEvent('render', this);
34107 };
34108
34109 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34110     tabTip:'',
34111     setRegion : function(region){
34112         this.region = region;
34113         if(region){
34114            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34115         }else{
34116            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34117         } 
34118     },
34119     
34120     /**
34121      * Returns the toolbar for this Panel if one was configured. 
34122      * @return {Roo.Toolbar} 
34123      */
34124     getToolbar : function(){
34125         return this.toolbar;
34126     },
34127     
34128     setActiveState : function(active){
34129         this.active = active;
34130         if(!active){
34131             this.fireEvent("deactivate", this);
34132         }else{
34133             this.fireEvent("activate", this);
34134         }
34135     },
34136     /**
34137      * Updates this panel's element
34138      * @param {String} content The new content
34139      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34140     */
34141     setContent : function(content, loadScripts){
34142         this.el.update(content, loadScripts);
34143     },
34144
34145     ignoreResize : function(w, h){
34146         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34147             return true;
34148         }else{
34149             this.lastSize = {width: w, height: h};
34150             return false;
34151         }
34152     },
34153     /**
34154      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34155      * @return {Roo.UpdateManager} The UpdateManager
34156      */
34157     getUpdateManager : function(){
34158         return this.el.getUpdateManager();
34159     },
34160      /**
34161      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34162      * @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:
34163 <pre><code>
34164 panel.load({
34165     url: "your-url.php",
34166     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34167     callback: yourFunction,
34168     scope: yourObject, //(optional scope)
34169     discardUrl: false,
34170     nocache: false,
34171     text: "Loading...",
34172     timeout: 30,
34173     scripts: false
34174 });
34175 </code></pre>
34176      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34177      * 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.
34178      * @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}
34179      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34180      * @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.
34181      * @return {Roo.ContentPanel} this
34182      */
34183     load : function(){
34184         var um = this.el.getUpdateManager();
34185         um.update.apply(um, arguments);
34186         return this;
34187     },
34188
34189
34190     /**
34191      * 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.
34192      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34193      * @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)
34194      * @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)
34195      * @return {Roo.UpdateManager} The UpdateManager
34196      */
34197     setUrl : function(url, params, loadOnce){
34198         if(this.refreshDelegate){
34199             this.removeListener("activate", this.refreshDelegate);
34200         }
34201         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34202         this.on("activate", this.refreshDelegate);
34203         return this.el.getUpdateManager();
34204     },
34205     
34206     _handleRefresh : function(url, params, loadOnce){
34207         if(!loadOnce || !this.loaded){
34208             var updater = this.el.getUpdateManager();
34209             updater.update(url, params, this._setLoaded.createDelegate(this));
34210         }
34211     },
34212     
34213     _setLoaded : function(){
34214         this.loaded = true;
34215     }, 
34216     
34217     /**
34218      * Returns this panel's id
34219      * @return {String} 
34220      */
34221     getId : function(){
34222         return this.el.id;
34223     },
34224     
34225     /** 
34226      * Returns this panel's element - used by regiosn to add.
34227      * @return {Roo.Element} 
34228      */
34229     getEl : function(){
34230         return this.wrapEl || this.el;
34231     },
34232     
34233    
34234     
34235     adjustForComponents : function(width, height)
34236     {
34237         //Roo.log('adjustForComponents ');
34238         if(this.resizeEl != this.el){
34239             width -= this.el.getFrameWidth('lr');
34240             height -= this.el.getFrameWidth('tb');
34241         }
34242         if(this.toolbar){
34243             var te = this.toolbar.getEl();
34244             height -= te.getHeight();
34245             te.setWidth(width);
34246         }
34247         if(this.footer){
34248             var te = this.footer.getEl();
34249             Roo.log("footer:" + te.getHeight());
34250             
34251             height -= te.getHeight();
34252             te.setWidth(width);
34253         }
34254         
34255         
34256         if(this.adjustments){
34257             width += this.adjustments[0];
34258             height += this.adjustments[1];
34259         }
34260         return {"width": width, "height": height};
34261     },
34262     
34263     setSize : function(width, height){
34264         if(this.fitToFrame && !this.ignoreResize(width, height)){
34265             if(this.fitContainer && this.resizeEl != this.el){
34266                 this.el.setSize(width, height);
34267             }
34268             var size = this.adjustForComponents(width, height);
34269             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34270             this.fireEvent('resize', this, size.width, size.height);
34271         }
34272     },
34273     
34274     /**
34275      * Returns this panel's title
34276      * @return {String} 
34277      */
34278     getTitle : function(){
34279         return this.title;
34280     },
34281     
34282     /**
34283      * Set this panel's title
34284      * @param {String} title
34285      */
34286     setTitle : function(title){
34287         this.title = title;
34288         if(this.region){
34289             this.region.updatePanelTitle(this, title);
34290         }
34291     },
34292     
34293     /**
34294      * Returns true is this panel was configured to be closable
34295      * @return {Boolean} 
34296      */
34297     isClosable : function(){
34298         return this.closable;
34299     },
34300     
34301     beforeSlide : function(){
34302         this.el.clip();
34303         this.resizeEl.clip();
34304     },
34305     
34306     afterSlide : function(){
34307         this.el.unclip();
34308         this.resizeEl.unclip();
34309     },
34310     
34311     /**
34312      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34313      *   Will fail silently if the {@link #setUrl} method has not been called.
34314      *   This does not activate the panel, just updates its content.
34315      */
34316     refresh : function(){
34317         if(this.refreshDelegate){
34318            this.loaded = false;
34319            this.refreshDelegate();
34320         }
34321     },
34322     
34323     /**
34324      * Destroys this panel
34325      */
34326     destroy : function(){
34327         this.el.removeAllListeners();
34328         var tempEl = document.createElement("span");
34329         tempEl.appendChild(this.el.dom);
34330         tempEl.innerHTML = "";
34331         this.el.remove();
34332         this.el = null;
34333     },
34334     
34335     /**
34336      * form - if the content panel contains a form - this is a reference to it.
34337      * @type {Roo.form.Form}
34338      */
34339     form : false,
34340     /**
34341      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34342      *    This contains a reference to it.
34343      * @type {Roo.View}
34344      */
34345     view : false,
34346     
34347       /**
34348      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34349      * <pre><code>
34350
34351 layout.addxtype({
34352        xtype : 'Form',
34353        items: [ .... ]
34354    }
34355 );
34356
34357 </code></pre>
34358      * @param {Object} cfg Xtype definition of item to add.
34359      */
34360     
34361     
34362     getChildContainer: function () {
34363         return this.getEl();
34364     }
34365     
34366     
34367     /*
34368         var  ret = new Roo.factory(cfg);
34369         return ret;
34370         
34371         
34372         // add form..
34373         if (cfg.xtype.match(/^Form$/)) {
34374             
34375             var el;
34376             //if (this.footer) {
34377             //    el = this.footer.container.insertSibling(false, 'before');
34378             //} else {
34379                 el = this.el.createChild();
34380             //}
34381
34382             this.form = new  Roo.form.Form(cfg);
34383             
34384             
34385             if ( this.form.allItems.length) {
34386                 this.form.render(el.dom);
34387             }
34388             return this.form;
34389         }
34390         // should only have one of theses..
34391         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34392             // views.. should not be just added - used named prop 'view''
34393             
34394             cfg.el = this.el.appendChild(document.createElement("div"));
34395             // factory?
34396             
34397             var ret = new Roo.factory(cfg);
34398              
34399              ret.render && ret.render(false, ''); // render blank..
34400             this.view = ret;
34401             return ret;
34402         }
34403         return false;
34404     }
34405     \*/
34406 });
34407  
34408 /**
34409  * @class Roo.bootstrap.panel.Grid
34410  * @extends Roo.bootstrap.panel.Content
34411  * @constructor
34412  * Create a new GridPanel.
34413  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34414  * @param {Object} config A the config object
34415   
34416  */
34417
34418
34419
34420 Roo.bootstrap.panel.Grid = function(config)
34421 {
34422     
34423       
34424     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34425         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34426
34427     config.el = this.wrapper;
34428     //this.el = this.wrapper;
34429     
34430       if (config.container) {
34431         // ctor'ed from a Border/panel.grid
34432         
34433         
34434         this.wrapper.setStyle("overflow", "hidden");
34435         this.wrapper.addClass('roo-grid-container');
34436
34437     }
34438     
34439     
34440     if(config.toolbar){
34441         var tool_el = this.wrapper.createChild();    
34442         this.toolbar = Roo.factory(config.toolbar);
34443         var ti = [];
34444         if (config.toolbar.items) {
34445             ti = config.toolbar.items ;
34446             delete config.toolbar.items ;
34447         }
34448         
34449         var nitems = [];
34450         this.toolbar.render(tool_el);
34451         for(var i =0;i < ti.length;i++) {
34452           //  Roo.log(['add child', items[i]]);
34453             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34454         }
34455         this.toolbar.items = nitems;
34456         
34457         delete config.toolbar;
34458     }
34459     
34460     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34461     config.grid.scrollBody = true;;
34462     config.grid.monitorWindowResize = false; // turn off autosizing
34463     config.grid.autoHeight = false;
34464     config.grid.autoWidth = false;
34465     
34466     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34467     
34468     if (config.background) {
34469         // render grid on panel activation (if panel background)
34470         this.on('activate', function(gp) {
34471             if (!gp.grid.rendered) {
34472                 gp.grid.render(el);
34473                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34474
34475             }
34476         });
34477             
34478     } else {
34479         this.grid.render(this.wrapper);
34480         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34481
34482     }
34483     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34484     // ??? needed ??? config.el = this.wrapper;
34485     
34486     
34487     
34488   
34489     // xtype created footer. - not sure if will work as we normally have to render first..
34490     if (this.footer && !this.footer.el && this.footer.xtype) {
34491         
34492         var ctr = this.grid.getView().getFooterPanel(true);
34493         this.footer.dataSource = this.grid.dataSource;
34494         this.footer = Roo.factory(this.footer, Roo);
34495         this.footer.render(ctr);
34496         
34497     }
34498     
34499     
34500     
34501     
34502      
34503 };
34504
34505 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34506     getId : function(){
34507         return this.grid.id;
34508     },
34509     
34510     /**
34511      * Returns the grid for this panel
34512      * @return {Roo.bootstrap.Table} 
34513      */
34514     getGrid : function(){
34515         return this.grid;    
34516     },
34517     
34518     setSize : function(width, height){
34519         if(!this.ignoreResize(width, height)){
34520             var grid = this.grid;
34521             var size = this.adjustForComponents(width, height);
34522             var gridel = grid.getGridEl();
34523             gridel.setSize(size.width, size.height);
34524             /*
34525             var thd = grid.getGridEl().select('thead',true).first();
34526             var tbd = grid.getGridEl().select('tbody', true).first();
34527             if (tbd) {
34528                 tbd.setSize(width, height - thd.getHeight());
34529             }
34530             */
34531             grid.autoSize();
34532         }
34533     },
34534      
34535     
34536     
34537     beforeSlide : function(){
34538         this.grid.getView().scroller.clip();
34539     },
34540     
34541     afterSlide : function(){
34542         this.grid.getView().scroller.unclip();
34543     },
34544     
34545     destroy : function(){
34546         this.grid.destroy();
34547         delete this.grid;
34548         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34549     }
34550 });
34551
34552 /**
34553  * @class Roo.bootstrap.panel.Nest
34554  * @extends Roo.bootstrap.panel.Content
34555  * @constructor
34556  * Create a new Panel, that can contain a layout.Border.
34557  * 
34558  * 
34559  * @param {Roo.BorderLayout} layout The layout for this panel
34560  * @param {String/Object} config A string to set only the title or a config object
34561  */
34562 Roo.bootstrap.panel.Nest = function(config)
34563 {
34564     // construct with only one argument..
34565     /* FIXME - implement nicer consturctors
34566     if (layout.layout) {
34567         config = layout;
34568         layout = config.layout;
34569         delete config.layout;
34570     }
34571     if (layout.xtype && !layout.getEl) {
34572         // then layout needs constructing..
34573         layout = Roo.factory(layout, Roo);
34574     }
34575     */
34576     
34577     config.el =  config.layout.getEl();
34578     
34579     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34580     
34581     config.layout.monitorWindowResize = false; // turn off autosizing
34582     this.layout = config.layout;
34583     this.layout.getEl().addClass("roo-layout-nested-layout");
34584     
34585     
34586     
34587     
34588 };
34589
34590 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34591
34592     setSize : function(width, height){
34593         if(!this.ignoreResize(width, height)){
34594             var size = this.adjustForComponents(width, height);
34595             var el = this.layout.getEl();
34596             if (size.height < 1) {
34597                 el.setWidth(size.width);   
34598             } else {
34599                 el.setSize(size.width, size.height);
34600             }
34601             var touch = el.dom.offsetWidth;
34602             this.layout.layout();
34603             // ie requires a double layout on the first pass
34604             if(Roo.isIE && !this.initialized){
34605                 this.initialized = true;
34606                 this.layout.layout();
34607             }
34608         }
34609     },
34610     
34611     // activate all subpanels if not currently active..
34612     
34613     setActiveState : function(active){
34614         this.active = active;
34615         if(!active){
34616             this.fireEvent("deactivate", this);
34617             return;
34618         }
34619         
34620         this.fireEvent("activate", this);
34621         // not sure if this should happen before or after..
34622         if (!this.layout) {
34623             return; // should not happen..
34624         }
34625         var reg = false;
34626         for (var r in this.layout.regions) {
34627             reg = this.layout.getRegion(r);
34628             if (reg.getActivePanel()) {
34629                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34630                 reg.setActivePanel(reg.getActivePanel());
34631                 continue;
34632             }
34633             if (!reg.panels.length) {
34634                 continue;
34635             }
34636             reg.showPanel(reg.getPanel(0));
34637         }
34638         
34639         
34640         
34641         
34642     },
34643     
34644     /**
34645      * Returns the nested BorderLayout for this panel
34646      * @return {Roo.BorderLayout} 
34647      */
34648     getLayout : function(){
34649         return this.layout;
34650     },
34651     
34652      /**
34653      * Adds a xtype elements to the layout of the nested panel
34654      * <pre><code>
34655
34656 panel.addxtype({
34657        xtype : 'ContentPanel',
34658        region: 'west',
34659        items: [ .... ]
34660    }
34661 );
34662
34663 panel.addxtype({
34664         xtype : 'NestedLayoutPanel',
34665         region: 'west',
34666         layout: {
34667            center: { },
34668            west: { }   
34669         },
34670         items : [ ... list of content panels or nested layout panels.. ]
34671    }
34672 );
34673 </code></pre>
34674      * @param {Object} cfg Xtype definition of item to add.
34675      */
34676     addxtype : function(cfg) {
34677         return this.layout.addxtype(cfg);
34678     
34679     }
34680 });        /*
34681  * Based on:
34682  * Ext JS Library 1.1.1
34683  * Copyright(c) 2006-2007, Ext JS, LLC.
34684  *
34685  * Originally Released Under LGPL - original licence link has changed is not relivant.
34686  *
34687  * Fork - LGPL
34688  * <script type="text/javascript">
34689  */
34690 /**
34691  * @class Roo.TabPanel
34692  * @extends Roo.util.Observable
34693  * A lightweight tab container.
34694  * <br><br>
34695  * Usage:
34696  * <pre><code>
34697 // basic tabs 1, built from existing content
34698 var tabs = new Roo.TabPanel("tabs1");
34699 tabs.addTab("script", "View Script");
34700 tabs.addTab("markup", "View Markup");
34701 tabs.activate("script");
34702
34703 // more advanced tabs, built from javascript
34704 var jtabs = new Roo.TabPanel("jtabs");
34705 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34706
34707 // set up the UpdateManager
34708 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34709 var updater = tab2.getUpdateManager();
34710 updater.setDefaultUrl("ajax1.htm");
34711 tab2.on('activate', updater.refresh, updater, true);
34712
34713 // Use setUrl for Ajax loading
34714 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34715 tab3.setUrl("ajax2.htm", null, true);
34716
34717 // Disabled tab
34718 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34719 tab4.disable();
34720
34721 jtabs.activate("jtabs-1");
34722  * </code></pre>
34723  * @constructor
34724  * Create a new TabPanel.
34725  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34726  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34727  */
34728 Roo.bootstrap.panel.Tabs = function(config){
34729     /**
34730     * The container element for this TabPanel.
34731     * @type Roo.Element
34732     */
34733     this.el = Roo.get(config.el);
34734     delete config.el;
34735     if(config){
34736         if(typeof config == "boolean"){
34737             this.tabPosition = config ? "bottom" : "top";
34738         }else{
34739             Roo.apply(this, config);
34740         }
34741     }
34742     
34743     if(this.tabPosition == "bottom"){
34744         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34745         this.el.addClass("roo-tabs-bottom");
34746     }
34747     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34748     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34749     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34750     if(Roo.isIE){
34751         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34752     }
34753     if(this.tabPosition != "bottom"){
34754         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34755          * @type Roo.Element
34756          */
34757         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34758         this.el.addClass("roo-tabs-top");
34759     }
34760     this.items = [];
34761
34762     this.bodyEl.setStyle("position", "relative");
34763
34764     this.active = null;
34765     this.activateDelegate = this.activate.createDelegate(this);
34766
34767     this.addEvents({
34768         /**
34769          * @event tabchange
34770          * Fires when the active tab changes
34771          * @param {Roo.TabPanel} this
34772          * @param {Roo.TabPanelItem} activePanel The new active tab
34773          */
34774         "tabchange": true,
34775         /**
34776          * @event beforetabchange
34777          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34778          * @param {Roo.TabPanel} this
34779          * @param {Object} e Set cancel to true on this object to cancel the tab change
34780          * @param {Roo.TabPanelItem} tab The tab being changed to
34781          */
34782         "beforetabchange" : true
34783     });
34784
34785     Roo.EventManager.onWindowResize(this.onResize, this);
34786     this.cpad = this.el.getPadding("lr");
34787     this.hiddenCount = 0;
34788
34789
34790     // toolbar on the tabbar support...
34791     if (this.toolbar) {
34792         alert("no toolbar support yet");
34793         this.toolbar  = false;
34794         /*
34795         var tcfg = this.toolbar;
34796         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34797         this.toolbar = new Roo.Toolbar(tcfg);
34798         if (Roo.isSafari) {
34799             var tbl = tcfg.container.child('table', true);
34800             tbl.setAttribute('width', '100%');
34801         }
34802         */
34803         
34804     }
34805    
34806
34807
34808     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34809 };
34810
34811 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34812     /*
34813      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34814      */
34815     tabPosition : "top",
34816     /*
34817      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34818      */
34819     currentTabWidth : 0,
34820     /*
34821      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34822      */
34823     minTabWidth : 40,
34824     /*
34825      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34826      */
34827     maxTabWidth : 250,
34828     /*
34829      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34830      */
34831     preferredTabWidth : 175,
34832     /*
34833      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34834      */
34835     resizeTabs : false,
34836     /*
34837      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34838      */
34839     monitorResize : true,
34840     /*
34841      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34842      */
34843     toolbar : false,
34844
34845     /**
34846      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34847      * @param {String} id The id of the div to use <b>or create</b>
34848      * @param {String} text The text for the tab
34849      * @param {String} content (optional) Content to put in the TabPanelItem body
34850      * @param {Boolean} closable (optional) True to create a close icon on the tab
34851      * @return {Roo.TabPanelItem} The created TabPanelItem
34852      */
34853     addTab : function(id, text, content, closable)
34854     {
34855         var item = new Roo.bootstrap.panel.TabItem({
34856             panel: this,
34857             id : id,
34858             text : text,
34859             closable : closable
34860         });
34861         this.addTabItem(item);
34862         if(content){
34863             item.setContent(content);
34864         }
34865         return item;
34866     },
34867
34868     /**
34869      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34870      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34871      * @return {Roo.TabPanelItem}
34872      */
34873     getTab : function(id){
34874         return this.items[id];
34875     },
34876
34877     /**
34878      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34879      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34880      */
34881     hideTab : function(id){
34882         var t = this.items[id];
34883         if(!t.isHidden()){
34884            t.setHidden(true);
34885            this.hiddenCount++;
34886            this.autoSizeTabs();
34887         }
34888     },
34889
34890     /**
34891      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34892      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34893      */
34894     unhideTab : function(id){
34895         var t = this.items[id];
34896         if(t.isHidden()){
34897            t.setHidden(false);
34898            this.hiddenCount--;
34899            this.autoSizeTabs();
34900         }
34901     },
34902
34903     /**
34904      * Adds an existing {@link Roo.TabPanelItem}.
34905      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34906      */
34907     addTabItem : function(item){
34908         this.items[item.id] = item;
34909         this.items.push(item);
34910       //  if(this.resizeTabs){
34911     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34912   //         this.autoSizeTabs();
34913 //        }else{
34914 //            item.autoSize();
34915        // }
34916     },
34917
34918     /**
34919      * Removes a {@link Roo.TabPanelItem}.
34920      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34921      */
34922     removeTab : function(id){
34923         var items = this.items;
34924         var tab = items[id];
34925         if(!tab) { return; }
34926         var index = items.indexOf(tab);
34927         if(this.active == tab && items.length > 1){
34928             var newTab = this.getNextAvailable(index);
34929             if(newTab) {
34930                 newTab.activate();
34931             }
34932         }
34933         this.stripEl.dom.removeChild(tab.pnode.dom);
34934         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34935             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34936         }
34937         items.splice(index, 1);
34938         delete this.items[tab.id];
34939         tab.fireEvent("close", tab);
34940         tab.purgeListeners();
34941         this.autoSizeTabs();
34942     },
34943
34944     getNextAvailable : function(start){
34945         var items = this.items;
34946         var index = start;
34947         // look for a next tab that will slide over to
34948         // replace the one being removed
34949         while(index < items.length){
34950             var item = items[++index];
34951             if(item && !item.isHidden()){
34952                 return item;
34953             }
34954         }
34955         // if one isn't found select the previous tab (on the left)
34956         index = start;
34957         while(index >= 0){
34958             var item = items[--index];
34959             if(item && !item.isHidden()){
34960                 return item;
34961             }
34962         }
34963         return null;
34964     },
34965
34966     /**
34967      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34968      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34969      */
34970     disableTab : function(id){
34971         var tab = this.items[id];
34972         if(tab && this.active != tab){
34973             tab.disable();
34974         }
34975     },
34976
34977     /**
34978      * Enables a {@link Roo.TabPanelItem} that is disabled.
34979      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34980      */
34981     enableTab : function(id){
34982         var tab = this.items[id];
34983         tab.enable();
34984     },
34985
34986     /**
34987      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34988      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34989      * @return {Roo.TabPanelItem} The TabPanelItem.
34990      */
34991     activate : function(id){
34992         var tab = this.items[id];
34993         if(!tab){
34994             return null;
34995         }
34996         if(tab == this.active || tab.disabled){
34997             return tab;
34998         }
34999         var e = {};
35000         this.fireEvent("beforetabchange", this, e, tab);
35001         if(e.cancel !== true && !tab.disabled){
35002             if(this.active){
35003                 this.active.hide();
35004             }
35005             this.active = this.items[id];
35006             this.active.show();
35007             this.fireEvent("tabchange", this, this.active);
35008         }
35009         return tab;
35010     },
35011
35012     /**
35013      * Gets the active {@link Roo.TabPanelItem}.
35014      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35015      */
35016     getActiveTab : function(){
35017         return this.active;
35018     },
35019
35020     /**
35021      * Updates the tab body element to fit the height of the container element
35022      * for overflow scrolling
35023      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35024      */
35025     syncHeight : function(targetHeight){
35026         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35027         var bm = this.bodyEl.getMargins();
35028         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35029         this.bodyEl.setHeight(newHeight);
35030         return newHeight;
35031     },
35032
35033     onResize : function(){
35034         if(this.monitorResize){
35035             this.autoSizeTabs();
35036         }
35037     },
35038
35039     /**
35040      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35041      */
35042     beginUpdate : function(){
35043         this.updating = true;
35044     },
35045
35046     /**
35047      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35048      */
35049     endUpdate : function(){
35050         this.updating = false;
35051         this.autoSizeTabs();
35052     },
35053
35054     /**
35055      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35056      */
35057     autoSizeTabs : function(){
35058         var count = this.items.length;
35059         var vcount = count - this.hiddenCount;
35060         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35061             return;
35062         }
35063         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35064         var availWidth = Math.floor(w / vcount);
35065         var b = this.stripBody;
35066         if(b.getWidth() > w){
35067             var tabs = this.items;
35068             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35069             if(availWidth < this.minTabWidth){
35070                 /*if(!this.sleft){    // incomplete scrolling code
35071                     this.createScrollButtons();
35072                 }
35073                 this.showScroll();
35074                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35075             }
35076         }else{
35077             if(this.currentTabWidth < this.preferredTabWidth){
35078                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35079             }
35080         }
35081     },
35082
35083     /**
35084      * Returns the number of tabs in this TabPanel.
35085      * @return {Number}
35086      */
35087      getCount : function(){
35088          return this.items.length;
35089      },
35090
35091     /**
35092      * Resizes all the tabs to the passed width
35093      * @param {Number} The new width
35094      */
35095     setTabWidth : function(width){
35096         this.currentTabWidth = width;
35097         for(var i = 0, len = this.items.length; i < len; i++) {
35098                 if(!this.items[i].isHidden()) {
35099                 this.items[i].setWidth(width);
35100             }
35101         }
35102     },
35103
35104     /**
35105      * Destroys this TabPanel
35106      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35107      */
35108     destroy : function(removeEl){
35109         Roo.EventManager.removeResizeListener(this.onResize, this);
35110         for(var i = 0, len = this.items.length; i < len; i++){
35111             this.items[i].purgeListeners();
35112         }
35113         if(removeEl === true){
35114             this.el.update("");
35115             this.el.remove();
35116         }
35117     },
35118     
35119     createStrip : function(container)
35120     {
35121         var strip = document.createElement("nav");
35122         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35123         container.appendChild(strip);
35124         return strip;
35125     },
35126     
35127     createStripList : function(strip)
35128     {
35129         // div wrapper for retard IE
35130         // returns the "tr" element.
35131         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35132         //'<div class="x-tabs-strip-wrap">'+
35133           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35134           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35135         return strip.firstChild; //.firstChild.firstChild.firstChild;
35136     },
35137     createBody : function(container)
35138     {
35139         var body = document.createElement("div");
35140         Roo.id(body, "tab-body");
35141         //Roo.fly(body).addClass("x-tabs-body");
35142         Roo.fly(body).addClass("tab-content");
35143         container.appendChild(body);
35144         return body;
35145     },
35146     createItemBody :function(bodyEl, id){
35147         var body = Roo.getDom(id);
35148         if(!body){
35149             body = document.createElement("div");
35150             body.id = id;
35151         }
35152         //Roo.fly(body).addClass("x-tabs-item-body");
35153         Roo.fly(body).addClass("tab-pane");
35154          bodyEl.insertBefore(body, bodyEl.firstChild);
35155         return body;
35156     },
35157     /** @private */
35158     createStripElements :  function(stripEl, text, closable)
35159     {
35160         var td = document.createElement("li"); // was td..
35161         
35162         
35163         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35164         
35165         
35166         stripEl.appendChild(td);
35167         /*if(closable){
35168             td.className = "x-tabs-closable";
35169             if(!this.closeTpl){
35170                 this.closeTpl = new Roo.Template(
35171                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35172                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35173                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
35174                 );
35175             }
35176             var el = this.closeTpl.overwrite(td, {"text": text});
35177             var close = el.getElementsByTagName("div")[0];
35178             var inner = el.getElementsByTagName("em")[0];
35179             return {"el": el, "close": close, "inner": inner};
35180         } else {
35181         */
35182         // not sure what this is..
35183             if(!this.tabTpl){
35184                 //this.tabTpl = new Roo.Template(
35185                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35186                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35187                 //);
35188                 this.tabTpl = new Roo.Template(
35189                    '<a href="#">' +
35190                    '<span unselectable="on"' +
35191                             (this.disableTooltips ? '' : ' title="{text}"') +
35192                             ' >{text}</span></span></a>'
35193                 );
35194                 
35195             }
35196             var el = this.tabTpl.overwrite(td, {"text": text});
35197             var inner = el.getElementsByTagName("span")[0];
35198             return {"el": el, "inner": inner};
35199         //}
35200     }
35201         
35202     
35203 });
35204
35205 /**
35206  * @class Roo.TabPanelItem
35207  * @extends Roo.util.Observable
35208  * Represents an individual item (tab plus body) in a TabPanel.
35209  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35210  * @param {String} id The id of this TabPanelItem
35211  * @param {String} text The text for the tab of this TabPanelItem
35212  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35213  */
35214 Roo.bootstrap.panel.TabItem = function(config){
35215     /**
35216      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35217      * @type Roo.TabPanel
35218      */
35219     this.tabPanel = config.panel;
35220     /**
35221      * The id for this TabPanelItem
35222      * @type String
35223      */
35224     this.id = config.id;
35225     /** @private */
35226     this.disabled = false;
35227     /** @private */
35228     this.text = config.text;
35229     /** @private */
35230     this.loaded = false;
35231     this.closable = config.closable;
35232
35233     /**
35234      * The body element for this TabPanelItem.
35235      * @type Roo.Element
35236      */
35237     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35238     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35239     this.bodyEl.setStyle("display", "block");
35240     this.bodyEl.setStyle("zoom", "1");
35241     //this.hideAction();
35242
35243     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35244     /** @private */
35245     this.el = Roo.get(els.el);
35246     this.inner = Roo.get(els.inner, true);
35247     this.textEl = Roo.get(this.el.dom.firstChild, true);
35248     this.pnode = Roo.get(els.el.parentNode, true);
35249     this.el.on("mousedown", this.onTabMouseDown, this);
35250     this.el.on("click", this.onTabClick, this);
35251     /** @private */
35252     if(config.closable){
35253         var c = Roo.get(els.close, true);
35254         c.dom.title = this.closeText;
35255         c.addClassOnOver("close-over");
35256         c.on("click", this.closeClick, this);
35257      }
35258
35259     this.addEvents({
35260          /**
35261          * @event activate
35262          * Fires when this tab becomes the active tab.
35263          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35264          * @param {Roo.TabPanelItem} this
35265          */
35266         "activate": true,
35267         /**
35268          * @event beforeclose
35269          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35270          * @param {Roo.TabPanelItem} this
35271          * @param {Object} e Set cancel to true on this object to cancel the close.
35272          */
35273         "beforeclose": true,
35274         /**
35275          * @event close
35276          * Fires when this tab is closed.
35277          * @param {Roo.TabPanelItem} this
35278          */
35279          "close": true,
35280         /**
35281          * @event deactivate
35282          * Fires when this tab is no longer the active tab.
35283          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35284          * @param {Roo.TabPanelItem} this
35285          */
35286          "deactivate" : true
35287     });
35288     this.hidden = false;
35289
35290     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35291 };
35292
35293 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35294            {
35295     purgeListeners : function(){
35296        Roo.util.Observable.prototype.purgeListeners.call(this);
35297        this.el.removeAllListeners();
35298     },
35299     /**
35300      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35301      */
35302     show : function(){
35303         this.pnode.addClass("active");
35304         this.showAction();
35305         if(Roo.isOpera){
35306             this.tabPanel.stripWrap.repaint();
35307         }
35308         this.fireEvent("activate", this.tabPanel, this);
35309     },
35310
35311     /**
35312      * Returns true if this tab is the active tab.
35313      * @return {Boolean}
35314      */
35315     isActive : function(){
35316         return this.tabPanel.getActiveTab() == this;
35317     },
35318
35319     /**
35320      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35321      */
35322     hide : function(){
35323         this.pnode.removeClass("active");
35324         this.hideAction();
35325         this.fireEvent("deactivate", this.tabPanel, this);
35326     },
35327
35328     hideAction : function(){
35329         this.bodyEl.hide();
35330         this.bodyEl.setStyle("position", "absolute");
35331         this.bodyEl.setLeft("-20000px");
35332         this.bodyEl.setTop("-20000px");
35333     },
35334
35335     showAction : function(){
35336         this.bodyEl.setStyle("position", "relative");
35337         this.bodyEl.setTop("");
35338         this.bodyEl.setLeft("");
35339         this.bodyEl.show();
35340     },
35341
35342     /**
35343      * Set the tooltip for the tab.
35344      * @param {String} tooltip The tab's tooltip
35345      */
35346     setTooltip : function(text){
35347         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35348             this.textEl.dom.qtip = text;
35349             this.textEl.dom.removeAttribute('title');
35350         }else{
35351             this.textEl.dom.title = text;
35352         }
35353     },
35354
35355     onTabClick : function(e){
35356         e.preventDefault();
35357         this.tabPanel.activate(this.id);
35358     },
35359
35360     onTabMouseDown : function(e){
35361         e.preventDefault();
35362         this.tabPanel.activate(this.id);
35363     },
35364 /*
35365     getWidth : function(){
35366         return this.inner.getWidth();
35367     },
35368
35369     setWidth : function(width){
35370         var iwidth = width - this.pnode.getPadding("lr");
35371         this.inner.setWidth(iwidth);
35372         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35373         this.pnode.setWidth(width);
35374     },
35375 */
35376     /**
35377      * Show or hide the tab
35378      * @param {Boolean} hidden True to hide or false to show.
35379      */
35380     setHidden : function(hidden){
35381         this.hidden = hidden;
35382         this.pnode.setStyle("display", hidden ? "none" : "");
35383     },
35384
35385     /**
35386      * Returns true if this tab is "hidden"
35387      * @return {Boolean}
35388      */
35389     isHidden : function(){
35390         return this.hidden;
35391     },
35392
35393     /**
35394      * Returns the text for this tab
35395      * @return {String}
35396      */
35397     getText : function(){
35398         return this.text;
35399     },
35400     /*
35401     autoSize : function(){
35402         //this.el.beginMeasure();
35403         this.textEl.setWidth(1);
35404         /*
35405          *  #2804 [new] Tabs in Roojs
35406          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35407          */
35408         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35409         //this.el.endMeasure();
35410     //},
35411
35412     /**
35413      * Sets the text for the tab (Note: this also sets the tooltip text)
35414      * @param {String} text The tab's text and tooltip
35415      */
35416     setText : function(text){
35417         this.text = text;
35418         this.textEl.update(text);
35419         this.setTooltip(text);
35420         //if(!this.tabPanel.resizeTabs){
35421         //    this.autoSize();
35422         //}
35423     },
35424     /**
35425      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35426      */
35427     activate : function(){
35428         this.tabPanel.activate(this.id);
35429     },
35430
35431     /**
35432      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35433      */
35434     disable : function(){
35435         if(this.tabPanel.active != this){
35436             this.disabled = true;
35437             this.pnode.addClass("disabled");
35438         }
35439     },
35440
35441     /**
35442      * Enables this TabPanelItem if it was previously disabled.
35443      */
35444     enable : function(){
35445         this.disabled = false;
35446         this.pnode.removeClass("disabled");
35447     },
35448
35449     /**
35450      * Sets the content for this TabPanelItem.
35451      * @param {String} content The content
35452      * @param {Boolean} loadScripts true to look for and load scripts
35453      */
35454     setContent : function(content, loadScripts){
35455         this.bodyEl.update(content, loadScripts);
35456     },
35457
35458     /**
35459      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35460      * @return {Roo.UpdateManager} The UpdateManager
35461      */
35462     getUpdateManager : function(){
35463         return this.bodyEl.getUpdateManager();
35464     },
35465
35466     /**
35467      * Set a URL to be used to load the content for this TabPanelItem.
35468      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35469      * @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)
35470      * @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)
35471      * @return {Roo.UpdateManager} The UpdateManager
35472      */
35473     setUrl : function(url, params, loadOnce){
35474         if(this.refreshDelegate){
35475             this.un('activate', this.refreshDelegate);
35476         }
35477         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35478         this.on("activate", this.refreshDelegate);
35479         return this.bodyEl.getUpdateManager();
35480     },
35481
35482     /** @private */
35483     _handleRefresh : function(url, params, loadOnce){
35484         if(!loadOnce || !this.loaded){
35485             var updater = this.bodyEl.getUpdateManager();
35486             updater.update(url, params, this._setLoaded.createDelegate(this));
35487         }
35488     },
35489
35490     /**
35491      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35492      *   Will fail silently if the setUrl method has not been called.
35493      *   This does not activate the panel, just updates its content.
35494      */
35495     refresh : function(){
35496         if(this.refreshDelegate){
35497            this.loaded = false;
35498            this.refreshDelegate();
35499         }
35500     },
35501
35502     /** @private */
35503     _setLoaded : function(){
35504         this.loaded = true;
35505     },
35506
35507     /** @private */
35508     closeClick : function(e){
35509         var o = {};
35510         e.stopEvent();
35511         this.fireEvent("beforeclose", this, o);
35512         if(o.cancel !== true){
35513             this.tabPanel.removeTab(this.id);
35514         }
35515     },
35516     /**
35517      * The text displayed in the tooltip for the close icon.
35518      * @type String
35519      */
35520     closeText : "Close this tab"
35521 });