Roo/bootstrap/panel/Grid.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     addxtypeChild : function (tree, cntr, is_body)
234     {
235         Roo.debug && Roo.log('addxtypeChild:' + cntr);
236         var cn = this;
237         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
238         
239         
240         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
241                     (typeof(tree['flexy:foreach']) != 'undefined');
242           
243         
244         
245          skip_children = false;
246         // render the element if it's not BODY.
247         if (!is_body) {
248            
249             cn = Roo.factory(tree);
250            
251             cn.parentType = this.xtype; //??
252             cn.parentId = this.id;
253             
254             var build_from_html =  Roo.XComponent.build_from_html;
255             
256             
257             // does the container contain child eleemnts with 'xtype' attributes.
258             // that match this xtype..
259             // note - when we render we create these as well..
260             // so we should check to see if body has xtype set.
261             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
262                
263                 var self_cntr_el = Roo.get(this[cntr](false));
264                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
265                 if (echild) { 
266                     //Roo.log(Roo.XComponent.build_from_html);
267                     //Roo.log("got echild:");
268                     //Roo.log(echild);
269                 }
270                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
271                 // and are not displayed -this causes this to use up the wrong element when matching.
272                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
273                 
274                 
275                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
276                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
277                   
278                   
279                   
280                     cn.el = echild;
281                   //  Roo.log("GOT");
282                     //echild.dom.removeAttribute('xtype');
283                 } else {
284                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
285                     Roo.debug && Roo.log(self_cntr_el);
286                     Roo.debug && Roo.log(echild);
287                     Roo.debug && Roo.log(cn);
288                 }
289             }
290            
291             
292            
293             // if object has flexy:if - then it may or may not be rendered.
294             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
295                 // skip a flexy if element.
296                 Roo.debug && Roo.log('skipping render');
297                 Roo.debug && Roo.log(tree);
298                 if (!cn.el) {
299                     Roo.debug && Roo.log('skipping all children');
300                     skip_children = true;
301                 }
302                 
303              } else {
304                  
305                 // actually if flexy:foreach is found, we really want to create 
306                 // multiple copies here...
307                 //Roo.log('render');
308                 //Roo.log(this[cntr]());
309                 // some elements do not have render methods.. like the layouts...
310                 cn.render && cn.render(this[cntr](true));
311              }
312             // then add the element..
313         }
314         
315         
316         // handle the kids..
317         
318         var nitems = [];
319         /*
320         if (typeof (tree.menu) != 'undefined') {
321             tree.menu.parentType = cn.xtype;
322             tree.menu.triggerEl = cn.el;
323             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
324             
325         }
326         */
327         if (!tree.items || !tree.items.length) {
328             cn.items = nitems;
329             //Roo.log(["no children", this]);
330             
331             return cn;
332         }
333          
334         var items = tree.items;
335         delete tree.items;
336         
337         //Roo.log(items.length);
338             // add the items..
339         if (!skip_children) {    
340             for(var i =0;i < items.length;i++) {
341               //  Roo.log(['add child', items[i]]);
342                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
343             }
344         }
345         
346         cn.items = nitems;
347         
348         //Roo.log("fire childrenrendered");
349         
350         cn.fireEvent('childrenrendered', this);
351         
352         return cn;
353     },
354     /**
355      * Show a component - removes 'hidden' class
356      */
357     show : function()
358     {
359         if (this.el) {
360             this.el.removeClass('hidden');
361         }
362     },
363     /**
364      * Hide a component - adds 'hidden' class
365      */
366     hide: function()
367     {
368         if (this.el && !this.el.hasClass('hidden')) {
369             this.el.addClass('hidden');
370         }
371         
372     }
373 });
374
375  /*
376  * - LGPL
377  *
378  * Body
379  * 
380  */
381
382 /**
383  * @class Roo.bootstrap.Body
384  * @extends Roo.bootstrap.Component
385  * Bootstrap Body class
386  * 
387  * @constructor
388  * Create a new body
389  * @param {Object} config The config object
390  */
391
392 Roo.bootstrap.Body = function(config){
393     Roo.bootstrap.Body.superclass.constructor.call(this, config);
394     this.el = Roo.get(document.body);
395     if (this.cls && this.cls.length) {
396         Roo.get(document.body).addClass(this.cls);
397     }
398 };
399
400 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
401     
402     is_body : true,// just to make sure it's constructed?
403     
404         autoCreate : {
405         cls: 'container'
406     },
407     onRender : function(ct, position)
408     {
409        /* Roo.log("Roo.bootstrap.Body - onRender");
410         if (this.cls && this.cls.length) {
411             Roo.get(document.body).addClass(this.cls);
412         }
413         // style??? xttr???
414         */
415     }
416     
417     
418  
419    
420 });
421
422  /*
423  * - LGPL
424  *
425  * button group
426  * 
427  */
428
429
430 /**
431  * @class Roo.bootstrap.ButtonGroup
432  * @extends Roo.bootstrap.Component
433  * Bootstrap ButtonGroup class
434  * @cfg {String} size lg | sm | xs (default empty normal)
435  * @cfg {String} align vertical | justified  (default none)
436  * @cfg {String} direction up | down (default down)
437  * @cfg {Boolean} toolbar false | true
438  * @cfg {Boolean} btn true | false
439  * 
440  * 
441  * @constructor
442  * Create a new Input
443  * @param {Object} config The config object
444  */
445
446 Roo.bootstrap.ButtonGroup = function(config){
447     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
448 };
449
450 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
451     
452     size: '',
453     align: '',
454     direction: '',
455     toolbar: false,
456     btn: true,
457
458     getAutoCreate : function(){
459         var cfg = {
460             cls: 'btn-group',
461             html : null
462         };
463         
464         cfg.html = this.html || cfg.html;
465         
466         if (this.toolbar) {
467             cfg = {
468                 cls: 'btn-toolbar',
469                 html: null
470             };
471             
472             return cfg;
473         }
474         
475         if (['vertical','justified'].indexOf(this.align)!==-1) {
476             cfg.cls = 'btn-group-' + this.align;
477             
478             if (this.align == 'justified') {
479                 console.log(this.items);
480             }
481         }
482         
483         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
484             cfg.cls += ' btn-group-' + this.size;
485         }
486         
487         if (this.direction == 'up') {
488             cfg.cls += ' dropup' ;
489         }
490         
491         return cfg;
492     }
493    
494 });
495
496  /*
497  * - LGPL
498  *
499  * button
500  * 
501  */
502
503 /**
504  * @class Roo.bootstrap.Button
505  * @extends Roo.bootstrap.Component
506  * Bootstrap Button class
507  * @cfg {String} html The button content
508  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
509  * @cfg {String} size ( lg | sm | xs)
510  * @cfg {String} tag ( a | input | submit)
511  * @cfg {String} href empty or href
512  * @cfg {Boolean} disabled default false;
513  * @cfg {Boolean} isClose default false;
514  * @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)
515  * @cfg {String} badge text for badge
516  * @cfg {String} theme default 
517  * @cfg {Boolean} inverse 
518  * @cfg {Boolean} toggle 
519  * @cfg {String} ontext text for on toggle state
520  * @cfg {String} offtext text for off toggle state
521  * @cfg {Boolean} defaulton 
522  * @cfg {Boolean} preventDefault  default true
523  * @cfg {Boolean} removeClass remove the standard class..
524  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
525  * 
526  * @constructor
527  * Create a new button
528  * @param {Object} config The config object
529  */
530
531
532 Roo.bootstrap.Button = function(config){
533     Roo.bootstrap.Button.superclass.constructor.call(this, config);
534     this.addEvents({
535         // raw events
536         /**
537          * @event click
538          * When a butotn is pressed
539          * @param {Roo.bootstrap.Button} this
540          * @param {Roo.EventObject} e
541          */
542         "click" : true,
543          /**
544          * @event toggle
545          * After the button has been toggles
546          * @param {Roo.EventObject} e
547          * @param {boolean} pressed (also available as button.pressed)
548          */
549         "toggle" : true
550     });
551 };
552
553 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
554     html: false,
555     active: false,
556     weight: '',
557     size: '',
558     tag: 'button',
559     href: '',
560     disabled: false,
561     isClose: false,
562     glyphicon: '',
563     badge: '',
564     theme: 'default',
565     inverse: false,
566     
567     toggle: false,
568     ontext: 'ON',
569     offtext: 'OFF',
570     defaulton: true,
571     preventDefault: true,
572     removeClass: false,
573     name: false,
574     target: false,
575     
576     
577     pressed : null,
578      
579     
580     getAutoCreate : function(){
581         
582         var cfg = {
583             tag : 'button',
584             cls : 'roo-button',
585             html: ''
586         };
587         
588         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
589             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
590             this.tag = 'button';
591         } else {
592             cfg.tag = this.tag;
593         }
594         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
595         
596         if (this.toggle == true) {
597             cfg={
598                 tag: 'div',
599                 cls: 'slider-frame roo-button',
600                 cn: [
601                     {
602                         tag: 'span',
603                         'data-on-text':'ON',
604                         'data-off-text':'OFF',
605                         cls: 'slider-button',
606                         html: this.offtext
607                     }
608                 ]
609             };
610             
611             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
612                 cfg.cls += ' '+this.weight;
613             }
614             
615             return cfg;
616         }
617         
618         if (this.isClose) {
619             cfg.cls += ' close';
620             
621             cfg["aria-hidden"] = true;
622             
623             cfg.html = "&times;";
624             
625             return cfg;
626         }
627         
628          
629         if (this.theme==='default') {
630             cfg.cls = 'btn roo-button';
631             
632             //if (this.parentType != 'Navbar') {
633             this.weight = this.weight.length ?  this.weight : 'default';
634             //}
635             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
636                 
637                 cfg.cls += ' btn-' + this.weight;
638             }
639         } else if (this.theme==='glow') {
640             
641             cfg.tag = 'a';
642             cfg.cls = 'btn-glow roo-button';
643             
644             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
645                 
646                 cfg.cls += ' ' + this.weight;
647             }
648         }
649    
650         
651         if (this.inverse) {
652             this.cls += ' inverse';
653         }
654         
655         
656         if (this.active) {
657             cfg.cls += ' active';
658         }
659         
660         if (this.disabled) {
661             cfg.disabled = 'disabled';
662         }
663         
664         if (this.items) {
665             Roo.log('changing to ul' );
666             cfg.tag = 'ul';
667             this.glyphicon = 'caret';
668         }
669         
670         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
671          
672         //gsRoo.log(this.parentType);
673         if (this.parentType === 'Navbar' && !this.parent().bar) {
674             Roo.log('changing to li?');
675             
676             cfg.tag = 'li';
677             
678             cfg.cls = '';
679             cfg.cn =  [{
680                 tag : 'a',
681                 cls : 'roo-button',
682                 html : this.html,
683                 href : this.href || '#'
684             }];
685             if (this.menu) {
686                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
687                 cfg.cls += ' dropdown';
688             }   
689             
690             delete cfg.html;
691             
692         }
693         
694        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
695         
696         if (this.glyphicon) {
697             cfg.html = ' ' + cfg.html;
698             
699             cfg.cn = [
700                 {
701                     tag: 'span',
702                     cls: 'glyphicon glyphicon-' + this.glyphicon
703                 }
704             ];
705         }
706         
707         if (this.badge) {
708             cfg.html += ' ';
709             
710             cfg.tag = 'a';
711             
712 //            cfg.cls='btn roo-button';
713             
714             cfg.href=this.href;
715             
716             var value = cfg.html;
717             
718             if(this.glyphicon){
719                 value = {
720                             tag: 'span',
721                             cls: 'glyphicon glyphicon-' + this.glyphicon,
722                             html: this.html
723                         };
724                 
725             }
726             
727             cfg.cn = [
728                 value,
729                 {
730                     tag: 'span',
731                     cls: 'badge',
732                     html: this.badge
733                 }
734             ];
735             
736             cfg.html='';
737         }
738         
739         if (this.menu) {
740             cfg.cls += ' dropdown';
741             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
742         }
743         
744         if (cfg.tag !== 'a' && this.href !== '') {
745             throw "Tag must be a to set href.";
746         } else if (this.href.length > 0) {
747             cfg.href = this.href;
748         }
749         
750         if(this.removeClass){
751             cfg.cls = '';
752         }
753         
754         if(this.target){
755             cfg.target = this.target;
756         }
757         
758         return cfg;
759     },
760     initEvents: function() {
761        // Roo.log('init events?');
762 //        Roo.log(this.el.dom);
763         // add the menu...
764         
765         if (typeof (this.menu) != 'undefined') {
766             this.menu.parentType = this.xtype;
767             this.menu.triggerEl = this.el;
768             this.addxtype(Roo.apply({}, this.menu));
769         }
770
771
772        if (this.el.hasClass('roo-button')) {
773             this.el.on('click', this.onClick, this);
774        } else {
775             this.el.select('.roo-button').on('click', this.onClick, this);
776        }
777        
778        if(this.removeClass){
779            this.el.on('click', this.onClick, this);
780        }
781        
782        this.el.enableDisplayMode();
783         
784     },
785     onClick : function(e)
786     {
787         if (this.disabled) {
788             return;
789         }
790         
791         
792         Roo.log('button on click ');
793         if(this.preventDefault){
794             e.preventDefault();
795         }
796         if (this.pressed === true || this.pressed === false) {
797             this.pressed = !this.pressed;
798             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
799             this.fireEvent('toggle', this, e, this.pressed);
800         }
801         
802         
803         this.fireEvent('click', this, e);
804     },
805     
806     /**
807      * Enables this button
808      */
809     enable : function()
810     {
811         this.disabled = false;
812         this.el.removeClass('disabled');
813     },
814     
815     /**
816      * Disable this button
817      */
818     disable : function()
819     {
820         this.disabled = true;
821         this.el.addClass('disabled');
822     },
823      /**
824      * sets the active state on/off, 
825      * @param {Boolean} state (optional) Force a particular state
826      */
827     setActive : function(v) {
828         
829         this.el[v ? 'addClass' : 'removeClass']('active');
830     },
831      /**
832      * toggles the current active state 
833      */
834     toggleActive : function()
835     {
836        var active = this.el.hasClass('active');
837        this.setActive(!active);
838        
839         
840     },
841     setText : function(str)
842     {
843         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
844     },
845     getText : function()
846     {
847         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
848     },
849     hide: function() {
850        
851      
852         this.el.hide();   
853     },
854     show: function() {
855        
856         this.el.show();   
857     }
858     
859     
860 });
861
862  /*
863  * - LGPL
864  *
865  * column
866  * 
867  */
868
869 /**
870  * @class Roo.bootstrap.Column
871  * @extends Roo.bootstrap.Component
872  * Bootstrap Column class
873  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
874  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
875  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
876  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
877  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
878  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
879  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
880  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
881  *
882  * 
883  * @cfg {Boolean} hidden (true|false) hide the element
884  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
885  * @cfg {String} fa (ban|check|...) font awesome icon
886  * @cfg {Number} fasize (1|2|....) font awsome size
887
888  * @cfg {String} icon (info-sign|check|...) glyphicon name
889
890  * @cfg {String} html content of column.
891  * 
892  * @constructor
893  * Create a new Column
894  * @param {Object} config The config object
895  */
896
897 Roo.bootstrap.Column = function(config){
898     Roo.bootstrap.Column.superclass.constructor.call(this, config);
899 };
900
901 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
902     
903     xs: false,
904     sm: false,
905     md: false,
906     lg: false,
907     xsoff: false,
908     smoff: false,
909     mdoff: false,
910     lgoff: false,
911     html: '',
912     offset: 0,
913     alert: false,
914     fa: false,
915     icon : false,
916     hidden : false,
917     fasize : 1,
918     
919     getAutoCreate : function(){
920         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
921         
922         cfg = {
923             tag: 'div',
924             cls: 'column'
925         };
926         
927         var settings=this;
928         ['xs','sm','md','lg'].map(function(size){
929             //Roo.log( size + ':' + settings[size]);
930             
931             if (settings[size+'off'] !== false) {
932                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
933             }
934             
935             if (settings[size] === false) {
936                 return;
937             }
938             
939             if (!settings[size]) { // 0 = hidden
940                 cfg.cls += ' hidden-' + size;
941                 return;
942             }
943             cfg.cls += ' col-' + size + '-' + settings[size];
944             
945         });
946         
947         if (this.hidden) {
948             cfg.cls += ' hidden';
949         }
950         
951         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
952             cfg.cls +=' alert alert-' + this.alert;
953         }
954         
955         
956         if (this.html.length) {
957             cfg.html = this.html;
958         }
959         if (this.fa) {
960             var fasize = '';
961             if (this.fasize > 1) {
962                 fasize = ' fa-' + this.fasize + 'x';
963             }
964             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
965             
966             
967         }
968         if (this.icon) {
969             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
970         }
971         
972         return cfg;
973     }
974    
975 });
976
977  
978
979  /*
980  * - LGPL
981  *
982  * page container.
983  * 
984  */
985
986
987 /**
988  * @class Roo.bootstrap.Container
989  * @extends Roo.bootstrap.Component
990  * Bootstrap Container class
991  * @cfg {Boolean} jumbotron is it a jumbotron element
992  * @cfg {String} html content of element
993  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
994  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
995  * @cfg {String} header content of header (for panel)
996  * @cfg {String} footer content of footer (for panel)
997  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
998  * @cfg {String} tag (header|aside|section) type of HTML tag.
999  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1000  * @cfg {String} fa font awesome icon
1001  * @cfg {String} icon (info-sign|check|...) glyphicon name
1002  * @cfg {Boolean} hidden (true|false) hide the element
1003  * @cfg {Boolean} expandable (true|false) default false
1004  * @cfg {Boolean} expanded (true|false) default true
1005  * @cfg {String} rheader contet on the right of header
1006  * @cfg {Boolean} clickable (true|false) default false
1007
1008  *     
1009  * @constructor
1010  * Create a new Container
1011  * @param {Object} config The config object
1012  */
1013
1014 Roo.bootstrap.Container = function(config){
1015     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019          /**
1020          * @event expand
1021          * After the panel has been expand
1022          * 
1023          * @param {Roo.bootstrap.Container} this
1024          */
1025         "expand" : true,
1026         /**
1027          * @event collapse
1028          * After the panel has been collapsed
1029          * 
1030          * @param {Roo.bootstrap.Container} this
1031          */
1032         "collapse" : true,
1033         /**
1034          * @event click
1035          * When a element is chick
1036          * @param {Roo.bootstrap.Container} this
1037          * @param {Roo.EventObject} e
1038          */
1039         "click" : true
1040     });
1041 };
1042
1043 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1044     
1045     jumbotron : false,
1046     well: '',
1047     panel : '',
1048     header: '',
1049     footer : '',
1050     sticky: '',
1051     tag : false,
1052     alert : false,
1053     fa: false,
1054     icon : false,
1055     expandable : false,
1056     rheader : '',
1057     expanded : true,
1058     clickable: false,
1059   
1060      
1061     getChildContainer : function() {
1062         
1063         if(!this.el){
1064             return false;
1065         }
1066         
1067         if (this.panel.length) {
1068             return this.el.select('.panel-body',true).first();
1069         }
1070         
1071         return this.el;
1072     },
1073     
1074     
1075     getAutoCreate : function(){
1076         
1077         var cfg = {
1078             tag : this.tag || 'div',
1079             html : '',
1080             cls : ''
1081         };
1082         if (this.jumbotron) {
1083             cfg.cls = 'jumbotron';
1084         }
1085         
1086         
1087         
1088         // - this is applied by the parent..
1089         //if (this.cls) {
1090         //    cfg.cls = this.cls + '';
1091         //}
1092         
1093         if (this.sticky.length) {
1094             
1095             var bd = Roo.get(document.body);
1096             if (!bd.hasClass('bootstrap-sticky')) {
1097                 bd.addClass('bootstrap-sticky');
1098                 Roo.select('html',true).setStyle('height', '100%');
1099             }
1100              
1101             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1102         }
1103         
1104         
1105         if (this.well.length) {
1106             switch (this.well) {
1107                 case 'lg':
1108                 case 'sm':
1109                     cfg.cls +=' well well-' +this.well;
1110                     break;
1111                 default:
1112                     cfg.cls +=' well';
1113                     break;
1114             }
1115         }
1116         
1117         if (this.hidden) {
1118             cfg.cls += ' hidden';
1119         }
1120         
1121         
1122         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1123             cfg.cls +=' alert alert-' + this.alert;
1124         }
1125         
1126         var body = cfg;
1127         
1128         if (this.panel.length) {
1129             cfg.cls += ' panel panel-' + this.panel;
1130             cfg.cn = [];
1131             if (this.header.length) {
1132                 
1133                 var h = [];
1134                 
1135                 if(this.expandable){
1136                     
1137                     cfg.cls = cfg.cls + ' expandable';
1138                     
1139                     h.push({
1140                         tag: 'i',
1141                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1142                     });
1143                     
1144                 }
1145                 
1146                 h.push(
1147                     {
1148                         tag: 'span',
1149                         cls : 'panel-title',
1150                         html : (this.expandable ? '&nbsp;' : '') + this.header
1151                     },
1152                     {
1153                         tag: 'span',
1154                         cls: 'panel-header-right',
1155                         html: this.rheader
1156                     }
1157                 );
1158                 
1159                 cfg.cn.push({
1160                     cls : 'panel-heading',
1161                     style : this.expandable ? 'cursor: pointer' : '',
1162                     cn : h
1163                 });
1164                 
1165             }
1166             
1167             body = false;
1168             cfg.cn.push({
1169                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1170                 html : this.html
1171             });
1172             
1173             
1174             if (this.footer.length) {
1175                 cfg.cn.push({
1176                     cls : 'panel-footer',
1177                     html : this.footer
1178                     
1179                 });
1180             }
1181             
1182         }
1183         
1184         if (body) {
1185             body.html = this.html || cfg.html;
1186             // prefix with the icons..
1187             if (this.fa) {
1188                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1189             }
1190             if (this.icon) {
1191                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1192             }
1193             
1194             
1195         }
1196         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1197             cfg.cls =  'container';
1198         }
1199         
1200         return cfg;
1201     },
1202     
1203     initEvents: function() 
1204     {
1205         if(this.expandable){
1206             var headerEl = this.headerEl();
1207         
1208             if(headerEl){
1209                 headerEl.on('click', this.onToggleClick, this);
1210             }
1211         }
1212         
1213         if(this.clickable){
1214             this.el.on('click', this.onClick, this);
1215         }
1216         
1217     },
1218     
1219     onToggleClick : function()
1220     {
1221         var headerEl = this.headerEl();
1222         
1223         if(!headerEl){
1224             return;
1225         }
1226         
1227         if(this.expanded){
1228             this.collapse();
1229             return;
1230         }
1231         
1232         this.expand();
1233     },
1234     
1235     expand : function()
1236     {
1237         if(this.fireEvent('expand', this)) {
1238             
1239             this.expanded = true;
1240             
1241             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1242             
1243             this.el.select('.panel-body',true).first().removeClass('hide');
1244             
1245             var toggleEl = this.toggleEl();
1246
1247             if(!toggleEl){
1248                 return;
1249             }
1250
1251             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1252         }
1253         
1254     },
1255     
1256     collapse : function()
1257     {
1258         if(this.fireEvent('collapse', this)) {
1259             
1260             this.expanded = false;
1261             
1262             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1263             this.el.select('.panel-body',true).first().addClass('hide');
1264         
1265             var toggleEl = this.toggleEl();
1266
1267             if(!toggleEl){
1268                 return;
1269             }
1270
1271             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1272         }
1273     },
1274     
1275     toggleEl : function()
1276     {
1277         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1278             return;
1279         }
1280         
1281         return this.el.select('.panel-heading .fa',true).first();
1282     },
1283     
1284     headerEl : function()
1285     {
1286         if(!this.el || !this.panel.length || !this.header.length){
1287             return;
1288         }
1289         
1290         return this.el.select('.panel-heading',true).first()
1291     },
1292     
1293     titleEl : function()
1294     {
1295         if(!this.el || !this.panel.length || !this.header.length){
1296             return;
1297         }
1298         
1299         return this.el.select('.panel-title',true).first();
1300     },
1301     
1302     setTitle : function(v)
1303     {
1304         var titleEl = this.titleEl();
1305         
1306         if(!titleEl){
1307             return;
1308         }
1309         
1310         titleEl.dom.innerHTML = v;
1311     },
1312     
1313     getTitle : function()
1314     {
1315         
1316         var titleEl = this.titleEl();
1317         
1318         if(!titleEl){
1319             return '';
1320         }
1321         
1322         return titleEl.dom.innerHTML;
1323     },
1324     
1325     setRightTitle : function(v)
1326     {
1327         var t = this.el.select('.panel-header-right',true).first();
1328         
1329         if(!t){
1330             return;
1331         }
1332         
1333         t.dom.innerHTML = v;
1334     },
1335     
1336     onClick : function(e)
1337     {
1338         e.preventDefault();
1339         
1340         this.fireEvent('click', this, e);
1341     }
1342    
1343 });
1344
1345  /*
1346  * - LGPL
1347  *
1348  * image
1349  * 
1350  */
1351
1352
1353 /**
1354  * @class Roo.bootstrap.Img
1355  * @extends Roo.bootstrap.Component
1356  * Bootstrap Img class
1357  * @cfg {Boolean} imgResponsive false | true
1358  * @cfg {String} border rounded | circle | thumbnail
1359  * @cfg {String} src image source
1360  * @cfg {String} alt image alternative text
1361  * @cfg {String} href a tag href
1362  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1363  * @cfg {String} xsUrl xs image source
1364  * @cfg {String} smUrl sm image source
1365  * @cfg {String} mdUrl md image source
1366  * @cfg {String} lgUrl lg image source
1367  * 
1368  * @constructor
1369  * Create a new Input
1370  * @param {Object} config The config object
1371  */
1372
1373 Roo.bootstrap.Img = function(config){
1374     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1375     
1376     this.addEvents({
1377         // img events
1378         /**
1379          * @event click
1380          * The img click event for the img.
1381          * @param {Roo.EventObject} e
1382          */
1383         "click" : true
1384     });
1385 };
1386
1387 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1388     
1389     imgResponsive: true,
1390     border: '',
1391     src: 'about:blank',
1392     href: false,
1393     target: false,
1394     xsUrl: '',
1395     smUrl: '',
1396     mdUrl: '',
1397     lgUrl: '',
1398
1399     getAutoCreate : function()
1400     {   
1401         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1402             return this.createSingleImg();
1403         }
1404         
1405         var cfg = {
1406             tag: 'div',
1407             cls: 'roo-image-responsive-group',
1408             cn: []
1409         };
1410         var _this = this;
1411         
1412         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1413             
1414             if(!_this[size + 'Url']){
1415                 return;
1416             }
1417             
1418             var img = {
1419                 tag: 'img',
1420                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1421                 html: _this.html || cfg.html,
1422                 src: _this[size + 'Url']
1423             };
1424             
1425             img.cls += ' roo-image-responsive-' + size;
1426             
1427             var s = ['xs', 'sm', 'md', 'lg'];
1428             
1429             s.splice(s.indexOf(size), 1);
1430             
1431             Roo.each(s, function(ss){
1432                 img.cls += ' hidden-' + ss;
1433             });
1434             
1435             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1436                 cfg.cls += ' img-' + _this.border;
1437             }
1438             
1439             if(_this.alt){
1440                 cfg.alt = _this.alt;
1441             }
1442             
1443             if(_this.href){
1444                 var a = {
1445                     tag: 'a',
1446                     href: _this.href,
1447                     cn: [
1448                         img
1449                     ]
1450                 };
1451
1452                 if(this.target){
1453                     a.target = _this.target;
1454                 }
1455             }
1456             
1457             cfg.cn.push((_this.href) ? a : img);
1458             
1459         });
1460         
1461         return cfg;
1462     },
1463     
1464     createSingleImg : function()
1465     {
1466         var cfg = {
1467             tag: 'img',
1468             cls: (this.imgResponsive) ? 'img-responsive' : '',
1469             html : null,
1470             src : 'about:blank'  // just incase src get's set to undefined?!?
1471         };
1472         
1473         cfg.html = this.html || cfg.html;
1474         
1475         cfg.src = this.src || cfg.src;
1476         
1477         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1478             cfg.cls += ' img-' + this.border;
1479         }
1480         
1481         if(this.alt){
1482             cfg.alt = this.alt;
1483         }
1484         
1485         if(this.href){
1486             var a = {
1487                 tag: 'a',
1488                 href: this.href,
1489                 cn: [
1490                     cfg
1491                 ]
1492             };
1493             
1494             if(this.target){
1495                 a.target = this.target;
1496             }
1497             
1498         }
1499         
1500         return (this.href) ? a : cfg;
1501     },
1502     
1503     initEvents: function() 
1504     {
1505         if(!this.href){
1506             this.el.on('click', this.onClick, this);
1507         }
1508         
1509     },
1510     
1511     onClick : function(e)
1512     {
1513         Roo.log('img onclick');
1514         this.fireEvent('click', this, e);
1515     }
1516    
1517 });
1518
1519  /*
1520  * - LGPL
1521  *
1522  * image
1523  * 
1524  */
1525
1526
1527 /**
1528  * @class Roo.bootstrap.Link
1529  * @extends Roo.bootstrap.Component
1530  * Bootstrap Link Class
1531  * @cfg {String} alt image alternative text
1532  * @cfg {String} href a tag href
1533  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1534  * @cfg {String} html the content of the link.
1535  * @cfg {String} anchor name for the anchor link
1536  * @cfg {String} fa - favicon
1537
1538  * @cfg {Boolean} preventDefault (true | false) default false
1539
1540  * 
1541  * @constructor
1542  * Create a new Input
1543  * @param {Object} config The config object
1544  */
1545
1546 Roo.bootstrap.Link = function(config){
1547     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1548     
1549     this.addEvents({
1550         // img events
1551         /**
1552          * @event click
1553          * The img click event for the img.
1554          * @param {Roo.EventObject} e
1555          */
1556         "click" : true
1557     });
1558 };
1559
1560 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1561     
1562     href: false,
1563     target: false,
1564     preventDefault: false,
1565     anchor : false,
1566     alt : false,
1567     fa: false,
1568
1569
1570     getAutoCreate : function()
1571     {
1572         var html = this.html || '';
1573         
1574         if (this.fa !== false) {
1575             html = '<i class="fa fa-' + this.fa + '"></i>';
1576         }
1577         var cfg = {
1578             tag: 'a'
1579         };
1580         // anchor's do not require html/href...
1581         if (this.anchor === false) {
1582             cfg.html = html;
1583             cfg.href = this.href || '#';
1584         } else {
1585             cfg.name = this.anchor;
1586             if (this.html !== false || this.fa !== false) {
1587                 cfg.html = html;
1588             }
1589             if (this.href !== false) {
1590                 cfg.href = this.href;
1591             }
1592         }
1593         
1594         if(this.alt !== false){
1595             cfg.alt = this.alt;
1596         }
1597         
1598         
1599         if(this.target !== false) {
1600             cfg.target = this.target;
1601         }
1602         
1603         return cfg;
1604     },
1605     
1606     initEvents: function() {
1607         
1608         if(!this.href || this.preventDefault){
1609             this.el.on('click', this.onClick, this);
1610         }
1611     },
1612     
1613     onClick : function(e)
1614     {
1615         if(this.preventDefault){
1616             e.preventDefault();
1617         }
1618         //Roo.log('img onclick');
1619         this.fireEvent('click', this, e);
1620     }
1621    
1622 });
1623
1624  /*
1625  * - LGPL
1626  *
1627  * header
1628  * 
1629  */
1630
1631 /**
1632  * @class Roo.bootstrap.Header
1633  * @extends Roo.bootstrap.Component
1634  * Bootstrap Header class
1635  * @cfg {String} html content of header
1636  * @cfg {Number} level (1|2|3|4|5|6) default 1
1637  * 
1638  * @constructor
1639  * Create a new Header
1640  * @param {Object} config The config object
1641  */
1642
1643
1644 Roo.bootstrap.Header  = function(config){
1645     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1649     
1650     //href : false,
1651     html : false,
1652     level : 1,
1653     
1654     
1655     
1656     getAutoCreate : function(){
1657         
1658         
1659         
1660         var cfg = {
1661             tag: 'h' + (1 *this.level),
1662             html: this.html || ''
1663         } ;
1664         
1665         return cfg;
1666     }
1667    
1668 });
1669
1670  
1671
1672  /*
1673  * Based on:
1674  * Ext JS Library 1.1.1
1675  * Copyright(c) 2006-2007, Ext JS, LLC.
1676  *
1677  * Originally Released Under LGPL - original licence link has changed is not relivant.
1678  *
1679  * Fork - LGPL
1680  * <script type="text/javascript">
1681  */
1682  
1683 /**
1684  * @class Roo.bootstrap.MenuMgr
1685  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1686  * @singleton
1687  */
1688 Roo.bootstrap.MenuMgr = function(){
1689    var menus, active, groups = {}, attached = false, lastShow = new Date();
1690
1691    // private - called when first menu is created
1692    function init(){
1693        menus = {};
1694        active = new Roo.util.MixedCollection();
1695        Roo.get(document).addKeyListener(27, function(){
1696            if(active.length > 0){
1697                hideAll();
1698            }
1699        });
1700    }
1701
1702    // private
1703    function hideAll(){
1704        if(active && active.length > 0){
1705            var c = active.clone();
1706            c.each(function(m){
1707                m.hide();
1708            });
1709        }
1710    }
1711
1712    // private
1713    function onHide(m){
1714        active.remove(m);
1715        if(active.length < 1){
1716            Roo.get(document).un("mouseup", onMouseDown);
1717             
1718            attached = false;
1719        }
1720    }
1721
1722    // private
1723    function onShow(m){
1724        var last = active.last();
1725        lastShow = new Date();
1726        active.add(m);
1727        if(!attached){
1728           Roo.get(document).on("mouseup", onMouseDown);
1729            
1730            attached = true;
1731        }
1732        if(m.parentMenu){
1733           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1734           m.parentMenu.activeChild = m;
1735        }else if(last && last.isVisible()){
1736           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1737        }
1738    }
1739
1740    // private
1741    function onBeforeHide(m){
1742        if(m.activeChild){
1743            m.activeChild.hide();
1744        }
1745        if(m.autoHideTimer){
1746            clearTimeout(m.autoHideTimer);
1747            delete m.autoHideTimer;
1748        }
1749    }
1750
1751    // private
1752    function onBeforeShow(m){
1753        var pm = m.parentMenu;
1754        if(!pm && !m.allowOtherMenus){
1755            hideAll();
1756        }else if(pm && pm.activeChild && active != m){
1757            pm.activeChild.hide();
1758        }
1759    }
1760
1761    // private this should really trigger on mouseup..
1762    function onMouseDown(e){
1763         Roo.log("on Mouse Up");
1764         
1765         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1766             Roo.log("MenuManager hideAll");
1767             hideAll();
1768             e.stopEvent();
1769         }
1770         
1771         
1772    }
1773
1774    // private
1775    function onBeforeCheck(mi, state){
1776        if(state){
1777            var g = groups[mi.group];
1778            for(var i = 0, l = g.length; i < l; i++){
1779                if(g[i] != mi){
1780                    g[i].setChecked(false);
1781                }
1782            }
1783        }
1784    }
1785
1786    return {
1787
1788        /**
1789         * Hides all menus that are currently visible
1790         */
1791        hideAll : function(){
1792             hideAll();  
1793        },
1794
1795        // private
1796        register : function(menu){
1797            if(!menus){
1798                init();
1799            }
1800            menus[menu.id] = menu;
1801            menu.on("beforehide", onBeforeHide);
1802            menu.on("hide", onHide);
1803            menu.on("beforeshow", onBeforeShow);
1804            menu.on("show", onShow);
1805            var g = menu.group;
1806            if(g && menu.events["checkchange"]){
1807                if(!groups[g]){
1808                    groups[g] = [];
1809                }
1810                groups[g].push(menu);
1811                menu.on("checkchange", onCheck);
1812            }
1813        },
1814
1815         /**
1816          * Returns a {@link Roo.menu.Menu} object
1817          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1818          * be used to generate and return a new Menu instance.
1819          */
1820        get : function(menu){
1821            if(typeof menu == "string"){ // menu id
1822                return menus[menu];
1823            }else if(menu.events){  // menu instance
1824                return menu;
1825            }
1826            /*else if(typeof menu.length == 'number'){ // array of menu items?
1827                return new Roo.bootstrap.Menu({items:menu});
1828            }else{ // otherwise, must be a config
1829                return new Roo.bootstrap.Menu(menu);
1830            }
1831            */
1832            return false;
1833        },
1834
1835        // private
1836        unregister : function(menu){
1837            delete menus[menu.id];
1838            menu.un("beforehide", onBeforeHide);
1839            menu.un("hide", onHide);
1840            menu.un("beforeshow", onBeforeShow);
1841            menu.un("show", onShow);
1842            var g = menu.group;
1843            if(g && menu.events["checkchange"]){
1844                groups[g].remove(menu);
1845                menu.un("checkchange", onCheck);
1846            }
1847        },
1848
1849        // private
1850        registerCheckable : function(menuItem){
1851            var g = menuItem.group;
1852            if(g){
1853                if(!groups[g]){
1854                    groups[g] = [];
1855                }
1856                groups[g].push(menuItem);
1857                menuItem.on("beforecheckchange", onBeforeCheck);
1858            }
1859        },
1860
1861        // private
1862        unregisterCheckable : function(menuItem){
1863            var g = menuItem.group;
1864            if(g){
1865                groups[g].remove(menuItem);
1866                menuItem.un("beforecheckchange", onBeforeCheck);
1867            }
1868        }
1869    };
1870 }();/*
1871  * - LGPL
1872  *
1873  * menu
1874  * 
1875  */
1876
1877 /**
1878  * @class Roo.bootstrap.Menu
1879  * @extends Roo.bootstrap.Component
1880  * Bootstrap Menu class - container for MenuItems
1881  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1882  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1883  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1884  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1885  * 
1886  * @constructor
1887  * Create a new Menu
1888  * @param {Object} config The config object
1889  */
1890
1891
1892 Roo.bootstrap.Menu = function(config){
1893     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1894     if (this.registerMenu && this.type != 'treeview')  {
1895         Roo.bootstrap.MenuMgr.register(this);
1896     }
1897     this.addEvents({
1898         /**
1899          * @event beforeshow
1900          * Fires before this menu is displayed
1901          * @param {Roo.menu.Menu} this
1902          */
1903         beforeshow : true,
1904         /**
1905          * @event beforehide
1906          * Fires before this menu is hidden
1907          * @param {Roo.menu.Menu} this
1908          */
1909         beforehide : true,
1910         /**
1911          * @event show
1912          * Fires after this menu is displayed
1913          * @param {Roo.menu.Menu} this
1914          */
1915         show : true,
1916         /**
1917          * @event hide
1918          * Fires after this menu is hidden
1919          * @param {Roo.menu.Menu} this
1920          */
1921         hide : true,
1922         /**
1923          * @event click
1924          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1925          * @param {Roo.menu.Menu} this
1926          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1927          * @param {Roo.EventObject} e
1928          */
1929         click : true,
1930         /**
1931          * @event mouseover
1932          * Fires when the mouse is hovering over this menu
1933          * @param {Roo.menu.Menu} this
1934          * @param {Roo.EventObject} e
1935          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1936          */
1937         mouseover : true,
1938         /**
1939          * @event mouseout
1940          * Fires when the mouse exits this menu
1941          * @param {Roo.menu.Menu} this
1942          * @param {Roo.EventObject} e
1943          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1944          */
1945         mouseout : true,
1946         /**
1947          * @event itemclick
1948          * Fires when a menu item contained in this menu is clicked
1949          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1950          * @param {Roo.EventObject} e
1951          */
1952         itemclick: true
1953     });
1954     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1955 };
1956
1957 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1958     
1959    /// html : false,
1960     //align : '',
1961     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1962     type: false,
1963     /**
1964      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1965      */
1966     registerMenu : true,
1967     
1968     menuItems :false, // stores the menu items..
1969     
1970     hidden:true,
1971         
1972     parentMenu : false,
1973     
1974     stopEvent : true,
1975     
1976     isLink : false,
1977     
1978     getChildContainer : function() {
1979         return this.el;  
1980     },
1981     
1982     getAutoCreate : function(){
1983          
1984         //if (['right'].indexOf(this.align)!==-1) {
1985         //    cfg.cn[1].cls += ' pull-right'
1986         //}
1987         
1988         
1989         var cfg = {
1990             tag : 'ul',
1991             cls : 'dropdown-menu' ,
1992             style : 'z-index:1000'
1993             
1994         };
1995         
1996         if (this.type === 'submenu') {
1997             cfg.cls = 'submenu active';
1998         }
1999         if (this.type === 'treeview') {
2000             cfg.cls = 'treeview-menu';
2001         }
2002         
2003         return cfg;
2004     },
2005     initEvents : function() {
2006         
2007        // Roo.log("ADD event");
2008        // Roo.log(this.triggerEl.dom);
2009         
2010         this.triggerEl.on('click', this.onTriggerClick, this);
2011         
2012         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2013         
2014         this.triggerEl.addClass('dropdown-toggle');
2015         
2016         if (Roo.isTouch) {
2017             this.el.on('touchstart'  , this.onTouch, this);
2018         }
2019         this.el.on('click' , this.onClick, this);
2020
2021         this.el.on("mouseover", this.onMouseOver, this);
2022         this.el.on("mouseout", this.onMouseOut, this);
2023         
2024     },
2025     
2026     findTargetItem : function(e)
2027     {
2028         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2029         if(!t){
2030             return false;
2031         }
2032         //Roo.log(t);         Roo.log(t.id);
2033         if(t && t.id){
2034             //Roo.log(this.menuitems);
2035             return this.menuitems.get(t.id);
2036             
2037             //return this.items.get(t.menuItemId);
2038         }
2039         
2040         return false;
2041     },
2042     
2043     onTouch : function(e) 
2044     {
2045         Roo.log("menu.onTouch");
2046         //e.stopEvent(); this make the user popdown broken
2047         this.onClick(e);
2048     },
2049     
2050     onClick : function(e)
2051     {
2052         Roo.log("menu.onClick");
2053         
2054         var t = this.findTargetItem(e);
2055         if(!t || t.isContainer){
2056             return;
2057         }
2058         Roo.log(e);
2059         /*
2060         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2061             if(t == this.activeItem && t.shouldDeactivate(e)){
2062                 this.activeItem.deactivate();
2063                 delete this.activeItem;
2064                 return;
2065             }
2066             if(t.canActivate){
2067                 this.setActiveItem(t, true);
2068             }
2069             return;
2070             
2071             
2072         }
2073         */
2074        
2075         Roo.log('pass click event');
2076         
2077         t.onClick(e);
2078         
2079         this.fireEvent("click", this, t, e);
2080         
2081         var _this = this;
2082         
2083         (function() { _this.hide(); }).defer(500);
2084     },
2085     
2086     onMouseOver : function(e){
2087         var t  = this.findTargetItem(e);
2088         //Roo.log(t);
2089         //if(t){
2090         //    if(t.canActivate && !t.disabled){
2091         //        this.setActiveItem(t, true);
2092         //    }
2093         //}
2094         
2095         this.fireEvent("mouseover", this, e, t);
2096     },
2097     isVisible : function(){
2098         return !this.hidden;
2099     },
2100      onMouseOut : function(e){
2101         var t  = this.findTargetItem(e);
2102         
2103         //if(t ){
2104         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2105         //        this.activeItem.deactivate();
2106         //        delete this.activeItem;
2107         //    }
2108         //}
2109         this.fireEvent("mouseout", this, e, t);
2110     },
2111     
2112     
2113     /**
2114      * Displays this menu relative to another element
2115      * @param {String/HTMLElement/Roo.Element} element The element to align to
2116      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2117      * the element (defaults to this.defaultAlign)
2118      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2119      */
2120     show : function(el, pos, parentMenu){
2121         this.parentMenu = parentMenu;
2122         if(!this.el){
2123             this.render();
2124         }
2125         this.fireEvent("beforeshow", this);
2126         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2127     },
2128      /**
2129      * Displays this menu at a specific xy position
2130      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2131      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2132      */
2133     showAt : function(xy, parentMenu, /* private: */_e){
2134         this.parentMenu = parentMenu;
2135         if(!this.el){
2136             this.render();
2137         }
2138         if(_e !== false){
2139             this.fireEvent("beforeshow", this);
2140             //xy = this.el.adjustForConstraints(xy);
2141         }
2142         
2143         //this.el.show();
2144         this.hideMenuItems();
2145         this.hidden = false;
2146         this.triggerEl.addClass('open');
2147         
2148         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2149             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2150         }
2151         
2152         if(this.el.getStyle('top').slice(-1) != "%"){
2153             this.el.setXY(xy);
2154         }
2155         
2156         this.focus();
2157         this.fireEvent("show", this);
2158     },
2159     
2160     focus : function(){
2161         return;
2162         if(!this.hidden){
2163             this.doFocus.defer(50, this);
2164         }
2165     },
2166
2167     doFocus : function(){
2168         if(!this.hidden){
2169             this.focusEl.focus();
2170         }
2171     },
2172
2173     /**
2174      * Hides this menu and optionally all parent menus
2175      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2176      */
2177     hide : function(deep)
2178     {
2179         
2180         this.hideMenuItems();
2181         if(this.el && this.isVisible()){
2182             this.fireEvent("beforehide", this);
2183             if(this.activeItem){
2184                 this.activeItem.deactivate();
2185                 this.activeItem = null;
2186             }
2187             this.triggerEl.removeClass('open');;
2188             this.hidden = true;
2189             this.fireEvent("hide", this);
2190         }
2191         if(deep === true && this.parentMenu){
2192             this.parentMenu.hide(true);
2193         }
2194     },
2195     
2196     onTriggerClick : function(e)
2197     {
2198         Roo.log('trigger click');
2199         
2200         var target = e.getTarget();
2201         
2202         Roo.log(target.nodeName.toLowerCase());
2203         
2204         if(target.nodeName.toLowerCase() === 'i'){
2205             e.preventDefault();
2206         }
2207         
2208     },
2209     
2210     onTriggerPress  : function(e)
2211     {
2212         Roo.log('trigger press');
2213         //Roo.log(e.getTarget());
2214        // Roo.log(this.triggerEl.dom);
2215        
2216         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2217         var pel = Roo.get(e.getTarget());
2218         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2219             Roo.log('is treeview or dropdown?');
2220             return;
2221         }
2222         
2223         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2224             return;
2225         }
2226         
2227         if (this.isVisible()) {
2228             Roo.log('hide');
2229             this.hide();
2230         } else {
2231             Roo.log('show');
2232             this.show(this.triggerEl, false, false);
2233         }
2234         
2235         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2236             e.stopEvent();
2237         }
2238         
2239     },
2240        
2241     
2242     hideMenuItems : function()
2243     {
2244         Roo.log("hide Menu Items");
2245         if (!this.el) { 
2246             return;
2247         }
2248         //$(backdrop).remove()
2249         this.el.select('.open',true).each(function(aa) {
2250             
2251             aa.removeClass('open');
2252           //var parent = getParent($(this))
2253           //var relatedTarget = { relatedTarget: this }
2254           
2255            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2256           //if (e.isDefaultPrevented()) return
2257            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2258         });
2259     },
2260     addxtypeChild : function (tree, cntr) {
2261         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2262           
2263         this.menuitems.add(comp);
2264         return comp;
2265
2266     },
2267     getEl : function()
2268     {
2269         Roo.log(this.el);
2270         return this.el;
2271     }
2272 });
2273
2274  
2275  /*
2276  * - LGPL
2277  *
2278  * menu item
2279  * 
2280  */
2281
2282
2283 /**
2284  * @class Roo.bootstrap.MenuItem
2285  * @extends Roo.bootstrap.Component
2286  * Bootstrap MenuItem class
2287  * @cfg {String} html the menu label
2288  * @cfg {String} href the link
2289  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2290  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2291  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2292  * @cfg {String} fa favicon to show on left of menu item.
2293  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2294  * 
2295  * 
2296  * @constructor
2297  * Create a new MenuItem
2298  * @param {Object} config The config object
2299  */
2300
2301
2302 Roo.bootstrap.MenuItem = function(config){
2303     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2304     this.addEvents({
2305         // raw events
2306         /**
2307          * @event click
2308          * The raw click event for the entire grid.
2309          * @param {Roo.bootstrap.MenuItem} this
2310          * @param {Roo.EventObject} e
2311          */
2312         "click" : true
2313     });
2314 };
2315
2316 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2317     
2318     href : false,
2319     html : false,
2320     preventDefault: true,
2321     isContainer : false,
2322     active : false,
2323     fa: false,
2324     
2325     getAutoCreate : function(){
2326         
2327         if(this.isContainer){
2328             return {
2329                 tag: 'li',
2330                 cls: 'dropdown-menu-item'
2331             };
2332         }
2333         var ctag = {
2334             tag: 'span',
2335             html: 'Link'
2336         };
2337         
2338         var anc = {
2339             tag : 'a',
2340             href : '#',
2341             cn : [  ]
2342         };
2343         
2344         if (this.fa !== false) {
2345             anc.cn.push({
2346                 tag : 'i',
2347                 cls : 'fa fa-' + this.fa
2348             });
2349         }
2350         
2351         anc.cn.push(ctag);
2352         
2353         
2354         var cfg= {
2355             tag: 'li',
2356             cls: 'dropdown-menu-item',
2357             cn: [ anc ]
2358         };
2359         if (this.parent().type == 'treeview') {
2360             cfg.cls = 'treeview-menu';
2361         }
2362         if (this.active) {
2363             cfg.cls += ' active';
2364         }
2365         
2366         
2367         
2368         anc.href = this.href || cfg.cn[0].href ;
2369         ctag.html = this.html || cfg.cn[0].html ;
2370         return cfg;
2371     },
2372     
2373     initEvents: function()
2374     {
2375         if (this.parent().type == 'treeview') {
2376             this.el.select('a').on('click', this.onClick, this);
2377         }
2378         if (this.menu) {
2379             this.menu.parentType = this.xtype;
2380             this.menu.triggerEl = this.el;
2381             this.menu = this.addxtype(Roo.apply({}, this.menu));
2382         }
2383         
2384     },
2385     onClick : function(e)
2386     {
2387         Roo.log('item on click ');
2388         //if(this.preventDefault){
2389         //    e.preventDefault();
2390         //}
2391         //this.parent().hideMenuItems();
2392         
2393         this.fireEvent('click', this, e);
2394     },
2395     getEl : function()
2396     {
2397         return this.el;
2398     } 
2399 });
2400
2401  
2402
2403  /*
2404  * - LGPL
2405  *
2406  * menu separator
2407  * 
2408  */
2409
2410
2411 /**
2412  * @class Roo.bootstrap.MenuSeparator
2413  * @extends Roo.bootstrap.Component
2414  * Bootstrap MenuSeparator class
2415  * 
2416  * @constructor
2417  * Create a new MenuItem
2418  * @param {Object} config The config object
2419  */
2420
2421
2422 Roo.bootstrap.MenuSeparator = function(config){
2423     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2424 };
2425
2426 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2427     
2428     getAutoCreate : function(){
2429         var cfg = {
2430             cls: 'divider',
2431             tag : 'li'
2432         };
2433         
2434         return cfg;
2435     }
2436    
2437 });
2438
2439  
2440
2441  
2442 /*
2443 * Licence: LGPL
2444 */
2445
2446 /**
2447  * @class Roo.bootstrap.Modal
2448  * @extends Roo.bootstrap.Component
2449  * Bootstrap Modal class
2450  * @cfg {String} title Title of dialog
2451  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2452  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2453  * @cfg {Boolean} specificTitle default false
2454  * @cfg {Array} buttons Array of buttons or standard button set..
2455  * @cfg {String} buttonPosition (left|right|center) default right
2456  * @cfg {Boolean} animate default true
2457  * @cfg {Boolean} allow_close default true
2458  * 
2459  * @constructor
2460  * Create a new Modal Dialog
2461  * @param {Object} config The config object
2462  */
2463
2464 Roo.bootstrap.Modal = function(config){
2465     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2466     this.addEvents({
2467         // raw events
2468         /**
2469          * @event btnclick
2470          * The raw btnclick event for the button
2471          * @param {Roo.EventObject} e
2472          */
2473         "btnclick" : true
2474     });
2475     this.buttons = this.buttons || [];
2476      
2477     if (this.tmpl) {
2478         this.tmpl = Roo.factory(this.tmpl);
2479     }
2480     
2481 };
2482
2483 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2484     
2485     title : 'test dialog',
2486    
2487     buttons : false,
2488     
2489     // set on load...
2490      
2491     html: false,
2492     
2493     tmp: false,
2494     
2495     specificTitle: false,
2496     
2497     buttonPosition: 'right',
2498     
2499     allow_close : true,
2500     
2501     animate : true,
2502     
2503     
2504      // private
2505     bodyEl:  false,
2506     footerEl:  false,
2507     titleEl:  false,
2508     closeEl:  false,
2509     
2510     
2511     onRender : function(ct, position)
2512     {
2513         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2514      
2515         if(!this.el){
2516             var cfg = Roo.apply({},  this.getAutoCreate());
2517             cfg.id = Roo.id();
2518             //if(!cfg.name){
2519             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2520             //}
2521             //if (!cfg.name.length) {
2522             //    delete cfg.name;
2523            // }
2524             if (this.cls) {
2525                 cfg.cls += ' ' + this.cls;
2526             }
2527             if (this.style) {
2528                 cfg.style = this.style;
2529             }
2530             this.el = Roo.get(document.body).createChild(cfg, position);
2531         }
2532         //var type = this.el.dom.type;
2533         
2534         
2535         if(this.tabIndex !== undefined){
2536             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2537         }
2538         
2539         
2540         this.bodyEl = this.el.select('.modal-body',true).first();
2541         this.closeEl = this.el.select('.modal-header .close', true).first();
2542         this.footerEl = this.el.select('.modal-footer',true).first();
2543         this.titleEl = this.el.select('.modal-title',true).first();
2544         
2545         
2546          
2547         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2548         this.maskEl.enableDisplayMode("block");
2549         this.maskEl.hide();
2550         //this.el.addClass("x-dlg-modal");
2551     
2552         if (this.buttons.length) {
2553             Roo.each(this.buttons, function(bb) {
2554                 var b = Roo.apply({}, bb);
2555                 b.xns = b.xns || Roo.bootstrap;
2556                 b.xtype = b.xtype || 'Button';
2557                 if (typeof(b.listeners) == 'undefined') {
2558                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2559                 }
2560                 
2561                 var btn = Roo.factory(b);
2562                 
2563                 btn.render(this.el.select('.modal-footer div').first());
2564                 
2565             },this);
2566         }
2567         // render the children.
2568         var nitems = [];
2569         
2570         if(typeof(this.items) != 'undefined'){
2571             var items = this.items;
2572             delete this.items;
2573
2574             for(var i =0;i < items.length;i++) {
2575                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2576             }
2577         }
2578         
2579         this.items = nitems;
2580         
2581         // where are these used - they used to be body/close/footer
2582         
2583        
2584         this.initEvents();
2585         //this.el.addClass([this.fieldClass, this.cls]);
2586         
2587     },
2588     
2589     getAutoCreate : function(){
2590         
2591         
2592         var bdy = {
2593                 cls : 'modal-body',
2594                 html : this.html || ''
2595         };
2596         
2597         var title = {
2598             tag: 'h4',
2599             cls : 'modal-title',
2600             html : this.title
2601         };
2602         
2603         if(this.specificTitle){
2604             title = this.title;
2605             
2606         };
2607         
2608         var header = [];
2609         if (this.allow_close) {
2610             header.push({
2611                 tag: 'button',
2612                 cls : 'close',
2613                 html : '&times'
2614             });
2615         }
2616         header.push(title);
2617         
2618         var modal = {
2619             cls: "modal",
2620             style : 'display: none',
2621             cn : [
2622                 {
2623                     cls: "modal-dialog",
2624                     cn : [
2625                         {
2626                             cls : "modal-content",
2627                             cn : [
2628                                 {
2629                                     cls : 'modal-header',
2630                                     cn : header
2631                                 },
2632                                 bdy,
2633                                 {
2634                                     cls : 'modal-footer',
2635                                     cn : [
2636                                         {
2637                                             tag: 'div',
2638                                             cls: 'btn-' + this.buttonPosition
2639                                         }
2640                                     ]
2641                                     
2642                                 }
2643                                 
2644                                 
2645                             ]
2646                             
2647                         }
2648                     ]
2649                         
2650                 }
2651             ]
2652         };
2653         
2654         if(this.animate){
2655             modal.cls += ' fade';
2656         }
2657         
2658         return modal;
2659           
2660     },
2661     getChildContainer : function() {
2662          
2663          return this.bodyEl;
2664         
2665     },
2666     getButtonContainer : function() {
2667          return this.el.select('.modal-footer div',true).first();
2668         
2669     },
2670     initEvents : function()
2671     {
2672         if (this.allow_close) {
2673             this.closeEl.on('click', this.hide, this);
2674         }
2675         
2676         var _this = this;
2677         
2678         window.addEventListener("resize", function() { _this.resize(); } );
2679
2680     },
2681     
2682     resize : function()
2683     {
2684         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2685     },
2686     
2687     show : function() {
2688         
2689         if (!this.rendered) {
2690             this.render();
2691         }
2692         
2693         this.el.setStyle('display', 'block');
2694         
2695         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2696             var _this = this;
2697             (function(){
2698                 this.el.addClass('in');
2699             }).defer(50, this);
2700         }else{
2701             this.el.addClass('in');
2702             
2703         }
2704         
2705         // not sure how we can show data in here.. 
2706         //if (this.tmpl) {
2707         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2708         //}
2709         
2710         Roo.get(document.body).addClass("x-body-masked");
2711         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2712         this.maskEl.show();
2713         this.el.setStyle('zIndex', '10001');
2714        
2715         this.fireEvent('show', this);
2716          
2717         
2718         
2719     },
2720     hide : function()
2721     {
2722         this.maskEl.hide();
2723         Roo.get(document.body).removeClass("x-body-masked");
2724         this.el.removeClass('in');
2725         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2726         
2727         if(this.animate){ // why
2728             var _this = this;
2729             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2730         }else{
2731             this.el.setStyle('display', 'none');
2732         }
2733         
2734         this.fireEvent('hide', this);
2735     },
2736     
2737     addButton : function(str, cb)
2738     {
2739          
2740         
2741         var b = Roo.apply({}, { html : str } );
2742         b.xns = b.xns || Roo.bootstrap;
2743         b.xtype = b.xtype || 'Button';
2744         if (typeof(b.listeners) == 'undefined') {
2745             b.listeners = { click : cb.createDelegate(this)  };
2746         }
2747         
2748         var btn = Roo.factory(b);
2749            
2750         btn.render(this.el.select('.modal-footer div').first());
2751         
2752         return btn;   
2753        
2754     },
2755     
2756     setDefaultButton : function(btn)
2757     {
2758         //this.el.select('.modal-footer').()
2759     },
2760     diff : false,
2761     
2762     resizeTo: function(w,h)
2763     {
2764         // skip.. ?? why??
2765         
2766         this.el.select('.modal-dialog',true).first().setWidth(w);
2767         if (this.diff === false) {
2768             this.diff = this.el.select('.modal-dialog',true).first().getHeight() - this.el.select('.modal-body',true).first().getHeight();
2769         }
2770         
2771         this.el.select('.modal-body',true).first().setHeight(h-this.diff);
2772         
2773         
2774     },
2775     setContentSize  : function(w, h)
2776     {
2777         
2778     },
2779     onButtonClick: function(btn,e)
2780     {
2781         //Roo.log([a,b,c]);
2782         this.fireEvent('btnclick', btn.name, e);
2783     },
2784      /**
2785      * Set the title of the Dialog
2786      * @param {String} str new Title
2787      */
2788     setTitle: function(str) {
2789         this.titleEl.dom.innerHTML = str;    
2790     },
2791     /**
2792      * Set the body of the Dialog
2793      * @param {String} str new Title
2794      */
2795     setBody: function(str) {
2796         this.bodyEl.dom.innerHTML = str;    
2797     },
2798     /**
2799      * Set the body of the Dialog using the template
2800      * @param {Obj} data - apply this data to the template and replace the body contents.
2801      */
2802     applyBody: function(obj)
2803     {
2804         if (!this.tmpl) {
2805             Roo.log("Error - using apply Body without a template");
2806             //code
2807         }
2808         this.tmpl.overwrite(this.bodyEl, obj);
2809     }
2810     
2811 });
2812
2813
2814 Roo.apply(Roo.bootstrap.Modal,  {
2815     /**
2816          * Button config that displays a single OK button
2817          * @type Object
2818          */
2819         OK :  [{
2820             name : 'ok',
2821             weight : 'primary',
2822             html : 'OK'
2823         }], 
2824         /**
2825          * Button config that displays Yes and No buttons
2826          * @type Object
2827          */
2828         YESNO : [
2829             {
2830                 name  : 'no',
2831                 html : 'No'
2832             },
2833             {
2834                 name  :'yes',
2835                 weight : 'primary',
2836                 html : 'Yes'
2837             }
2838         ],
2839         
2840         /**
2841          * Button config that displays OK and Cancel buttons
2842          * @type Object
2843          */
2844         OKCANCEL : [
2845             {
2846                name : 'cancel',
2847                 html : 'Cancel'
2848             },
2849             {
2850                 name : 'ok',
2851                 weight : 'primary',
2852                 html : 'OK'
2853             }
2854         ],
2855         /**
2856          * Button config that displays Yes, No and Cancel buttons
2857          * @type Object
2858          */
2859         YESNOCANCEL : [
2860             {
2861                 name : 'yes',
2862                 weight : 'primary',
2863                 html : 'Yes'
2864             },
2865             {
2866                 name : 'no',
2867                 html : 'No'
2868             },
2869             {
2870                 name : 'cancel',
2871                 html : 'Cancel'
2872             }
2873         ]
2874 });
2875  
2876  /*
2877  * - LGPL
2878  *
2879  * messagebox - can be used as a replace
2880  * 
2881  */
2882 /**
2883  * @class Roo.MessageBox
2884  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2885  * Example usage:
2886  *<pre><code>
2887 // Basic alert:
2888 Roo.Msg.alert('Status', 'Changes saved successfully.');
2889
2890 // Prompt for user data:
2891 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2892     if (btn == 'ok'){
2893         // process text value...
2894     }
2895 });
2896
2897 // Show a dialog using config options:
2898 Roo.Msg.show({
2899    title:'Save Changes?',
2900    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2901    buttons: Roo.Msg.YESNOCANCEL,
2902    fn: processResult,
2903    animEl: 'elId'
2904 });
2905 </code></pre>
2906  * @singleton
2907  */
2908 Roo.bootstrap.MessageBox = function(){
2909     var dlg, opt, mask, waitTimer;
2910     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2911     var buttons, activeTextEl, bwidth;
2912
2913     
2914     // private
2915     var handleButton = function(button){
2916         dlg.hide();
2917         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2918     };
2919
2920     // private
2921     var handleHide = function(){
2922         if(opt && opt.cls){
2923             dlg.el.removeClass(opt.cls);
2924         }
2925         //if(waitTimer){
2926         //    Roo.TaskMgr.stop(waitTimer);
2927         //    waitTimer = null;
2928         //}
2929     };
2930
2931     // private
2932     var updateButtons = function(b){
2933         var width = 0;
2934         if(!b){
2935             buttons["ok"].hide();
2936             buttons["cancel"].hide();
2937             buttons["yes"].hide();
2938             buttons["no"].hide();
2939             //dlg.footer.dom.style.display = 'none';
2940             return width;
2941         }
2942         dlg.footerEl.dom.style.display = '';
2943         for(var k in buttons){
2944             if(typeof buttons[k] != "function"){
2945                 if(b[k]){
2946                     buttons[k].show();
2947                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2948                     width += buttons[k].el.getWidth()+15;
2949                 }else{
2950                     buttons[k].hide();
2951                 }
2952             }
2953         }
2954         return width;
2955     };
2956
2957     // private
2958     var handleEsc = function(d, k, e){
2959         if(opt && opt.closable !== false){
2960             dlg.hide();
2961         }
2962         if(e){
2963             e.stopEvent();
2964         }
2965     };
2966
2967     return {
2968         /**
2969          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2970          * @return {Roo.BasicDialog} The BasicDialog element
2971          */
2972         getDialog : function(){
2973            if(!dlg){
2974                 dlg = new Roo.bootstrap.Modal( {
2975                     //draggable: true,
2976                     //resizable:false,
2977                     //constraintoviewport:false,
2978                     //fixedcenter:true,
2979                     //collapsible : false,
2980                     //shim:true,
2981                     //modal: true,
2982                   //  width:400,
2983                   //  height:100,
2984                     //buttonAlign:"center",
2985                     closeClick : function(){
2986                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2987                             handleButton("no");
2988                         }else{
2989                             handleButton("cancel");
2990                         }
2991                     }
2992                 });
2993                 dlg.render();
2994                 dlg.on("hide", handleHide);
2995                 mask = dlg.mask;
2996                 //dlg.addKeyListener(27, handleEsc);
2997                 buttons = {};
2998                 this.buttons = buttons;
2999                 var bt = this.buttonText;
3000                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3001                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3002                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3003                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3004                 //Roo.log(buttons);
3005                 bodyEl = dlg.bodyEl.createChild({
3006
3007                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3008                         '<textarea class="roo-mb-textarea"></textarea>' +
3009                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3010                 });
3011                 msgEl = bodyEl.dom.firstChild;
3012                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3013                 textboxEl.enableDisplayMode();
3014                 textboxEl.addKeyListener([10,13], function(){
3015                     if(dlg.isVisible() && opt && opt.buttons){
3016                         if(opt.buttons.ok){
3017                             handleButton("ok");
3018                         }else if(opt.buttons.yes){
3019                             handleButton("yes");
3020                         }
3021                     }
3022                 });
3023                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3024                 textareaEl.enableDisplayMode();
3025                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3026                 progressEl.enableDisplayMode();
3027                 var pf = progressEl.dom.firstChild;
3028                 if (pf) {
3029                     pp = Roo.get(pf.firstChild);
3030                     pp.setHeight(pf.offsetHeight);
3031                 }
3032                 
3033             }
3034             return dlg;
3035         },
3036
3037         /**
3038          * Updates the message box body text
3039          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3040          * the XHTML-compliant non-breaking space character '&amp;#160;')
3041          * @return {Roo.MessageBox} This message box
3042          */
3043         updateText : function(text){
3044             if(!dlg.isVisible() && !opt.width){
3045                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3046             }
3047             msgEl.innerHTML = text || '&#160;';
3048       
3049             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3050             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3051             var w = Math.max(
3052                     Math.min(opt.width || cw , this.maxWidth), 
3053                     Math.max(opt.minWidth || this.minWidth, bwidth)
3054             );
3055             if(opt.prompt){
3056                 activeTextEl.setWidth(w);
3057             }
3058             if(dlg.isVisible()){
3059                 dlg.fixedcenter = false;
3060             }
3061             // to big, make it scroll. = But as usual stupid IE does not support
3062             // !important..
3063             
3064             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3065                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3066                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3067             } else {
3068                 bodyEl.dom.style.height = '';
3069                 bodyEl.dom.style.overflowY = '';
3070             }
3071             if (cw > w) {
3072                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3073             } else {
3074                 bodyEl.dom.style.overflowX = '';
3075             }
3076             
3077             dlg.setContentSize(w, bodyEl.getHeight());
3078             if(dlg.isVisible()){
3079                 dlg.fixedcenter = true;
3080             }
3081             return this;
3082         },
3083
3084         /**
3085          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3086          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3087          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3088          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3089          * @return {Roo.MessageBox} This message box
3090          */
3091         updateProgress : function(value, text){
3092             if(text){
3093                 this.updateText(text);
3094             }
3095             if (pp) { // weird bug on my firefox - for some reason this is not defined
3096                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3097             }
3098             return this;
3099         },        
3100
3101         /**
3102          * Returns true if the message box is currently displayed
3103          * @return {Boolean} True if the message box is visible, else false
3104          */
3105         isVisible : function(){
3106             return dlg && dlg.isVisible();  
3107         },
3108
3109         /**
3110          * Hides the message box if it is displayed
3111          */
3112         hide : function(){
3113             if(this.isVisible()){
3114                 dlg.hide();
3115             }  
3116         },
3117
3118         /**
3119          * Displays a new message box, or reinitializes an existing message box, based on the config options
3120          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3121          * The following config object properties are supported:
3122          * <pre>
3123 Property    Type             Description
3124 ----------  ---------------  ------------------------------------------------------------------------------------
3125 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3126                                    closes (defaults to undefined)
3127 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3128                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3129 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3130                                    progress and wait dialogs will ignore this property and always hide the
3131                                    close button as they can only be closed programmatically.
3132 cls               String           A custom CSS class to apply to the message box element
3133 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3134                                    displayed (defaults to 75)
3135 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3136                                    function will be btn (the name of the button that was clicked, if applicable,
3137                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3138                                    Progress and wait dialogs will ignore this option since they do not respond to
3139                                    user actions and can only be closed programmatically, so any required function
3140                                    should be called by the same code after it closes the dialog.
3141 icon              String           A CSS class that provides a background image to be used as an icon for
3142                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3143 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3144 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3145 modal             Boolean          False to allow user interaction with the page while the message box is
3146                                    displayed (defaults to true)
3147 msg               String           A string that will replace the existing message box body text (defaults
3148                                    to the XHTML-compliant non-breaking space character '&#160;')
3149 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3150 progress          Boolean          True to display a progress bar (defaults to false)
3151 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3152 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3153 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3154 title             String           The title text
3155 value             String           The string value to set into the active textbox element if displayed
3156 wait              Boolean          True to display a progress bar (defaults to false)
3157 width             Number           The width of the dialog in pixels
3158 </pre>
3159          *
3160          * Example usage:
3161          * <pre><code>
3162 Roo.Msg.show({
3163    title: 'Address',
3164    msg: 'Please enter your address:',
3165    width: 300,
3166    buttons: Roo.MessageBox.OKCANCEL,
3167    multiline: true,
3168    fn: saveAddress,
3169    animEl: 'addAddressBtn'
3170 });
3171 </code></pre>
3172          * @param {Object} config Configuration options
3173          * @return {Roo.MessageBox} This message box
3174          */
3175         show : function(options)
3176         {
3177             
3178             // this causes nightmares if you show one dialog after another
3179             // especially on callbacks..
3180              
3181             if(this.isVisible()){
3182                 
3183                 this.hide();
3184                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3185                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3186                 Roo.log("New Dialog Message:" +  options.msg )
3187                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3188                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3189                 
3190             }
3191             var d = this.getDialog();
3192             opt = options;
3193             d.setTitle(opt.title || "&#160;");
3194             d.closeEl.setDisplayed(opt.closable !== false);
3195             activeTextEl = textboxEl;
3196             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3197             if(opt.prompt){
3198                 if(opt.multiline){
3199                     textboxEl.hide();
3200                     textareaEl.show();
3201                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3202                         opt.multiline : this.defaultTextHeight);
3203                     activeTextEl = textareaEl;
3204                 }else{
3205                     textboxEl.show();
3206                     textareaEl.hide();
3207                 }
3208             }else{
3209                 textboxEl.hide();
3210                 textareaEl.hide();
3211             }
3212             progressEl.setDisplayed(opt.progress === true);
3213             this.updateProgress(0);
3214             activeTextEl.dom.value = opt.value || "";
3215             if(opt.prompt){
3216                 dlg.setDefaultButton(activeTextEl);
3217             }else{
3218                 var bs = opt.buttons;
3219                 var db = null;
3220                 if(bs && bs.ok){
3221                     db = buttons["ok"];
3222                 }else if(bs && bs.yes){
3223                     db = buttons["yes"];
3224                 }
3225                 dlg.setDefaultButton(db);
3226             }
3227             bwidth = updateButtons(opt.buttons);
3228             this.updateText(opt.msg);
3229             if(opt.cls){
3230                 d.el.addClass(opt.cls);
3231             }
3232             d.proxyDrag = opt.proxyDrag === true;
3233             d.modal = opt.modal !== false;
3234             d.mask = opt.modal !== false ? mask : false;
3235             if(!d.isVisible()){
3236                 // force it to the end of the z-index stack so it gets a cursor in FF
3237                 document.body.appendChild(dlg.el.dom);
3238                 d.animateTarget = null;
3239                 d.show(options.animEl);
3240             }
3241             return this;
3242         },
3243
3244         /**
3245          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3246          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3247          * and closing the message box when the process is complete.
3248          * @param {String} title The title bar text
3249          * @param {String} msg The message box body text
3250          * @return {Roo.MessageBox} This message box
3251          */
3252         progress : function(title, msg){
3253             this.show({
3254                 title : title,
3255                 msg : msg,
3256                 buttons: false,
3257                 progress:true,
3258                 closable:false,
3259                 minWidth: this.minProgressWidth,
3260                 modal : true
3261             });
3262             return this;
3263         },
3264
3265         /**
3266          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3267          * If a callback function is passed it will be called after the user clicks the button, and the
3268          * id of the button that was clicked will be passed as the only parameter to the callback
3269          * (could also be the top-right close button).
3270          * @param {String} title The title bar text
3271          * @param {String} msg The message box body text
3272          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3273          * @param {Object} scope (optional) The scope of the callback function
3274          * @return {Roo.MessageBox} This message box
3275          */
3276         alert : function(title, msg, fn, scope){
3277             this.show({
3278                 title : title,
3279                 msg : msg,
3280                 buttons: this.OK,
3281                 fn: fn,
3282                 scope : scope,
3283                 modal : true
3284             });
3285             return this;
3286         },
3287
3288         /**
3289          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3290          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3291          * You are responsible for closing the message box when the process is complete.
3292          * @param {String} msg The message box body text
3293          * @param {String} title (optional) The title bar text
3294          * @return {Roo.MessageBox} This message box
3295          */
3296         wait : function(msg, title){
3297             this.show({
3298                 title : title,
3299                 msg : msg,
3300                 buttons: false,
3301                 closable:false,
3302                 progress:true,
3303                 modal:true,
3304                 width:300,
3305                 wait:true
3306             });
3307             waitTimer = Roo.TaskMgr.start({
3308                 run: function(i){
3309                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3310                 },
3311                 interval: 1000
3312             });
3313             return this;
3314         },
3315
3316         /**
3317          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3318          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3319          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3320          * @param {String} title The title bar text
3321          * @param {String} msg The message box body text
3322          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3323          * @param {Object} scope (optional) The scope of the callback function
3324          * @return {Roo.MessageBox} This message box
3325          */
3326         confirm : function(title, msg, fn, scope){
3327             this.show({
3328                 title : title,
3329                 msg : msg,
3330                 buttons: this.YESNO,
3331                 fn: fn,
3332                 scope : scope,
3333                 modal : true
3334             });
3335             return this;
3336         },
3337
3338         /**
3339          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3340          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3341          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3342          * (could also be the top-right close button) and the text that was entered will be passed as the two
3343          * parameters to the callback.
3344          * @param {String} title The title bar text
3345          * @param {String} msg The message box body text
3346          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3347          * @param {Object} scope (optional) The scope of the callback function
3348          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3349          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3350          * @return {Roo.MessageBox} This message box
3351          */
3352         prompt : function(title, msg, fn, scope, multiline){
3353             this.show({
3354                 title : title,
3355                 msg : msg,
3356                 buttons: this.OKCANCEL,
3357                 fn: fn,
3358                 minWidth:250,
3359                 scope : scope,
3360                 prompt:true,
3361                 multiline: multiline,
3362                 modal : true
3363             });
3364             return this;
3365         },
3366
3367         /**
3368          * Button config that displays a single OK button
3369          * @type Object
3370          */
3371         OK : {ok:true},
3372         /**
3373          * Button config that displays Yes and No buttons
3374          * @type Object
3375          */
3376         YESNO : {yes:true, no:true},
3377         /**
3378          * Button config that displays OK and Cancel buttons
3379          * @type Object
3380          */
3381         OKCANCEL : {ok:true, cancel:true},
3382         /**
3383          * Button config that displays Yes, No and Cancel buttons
3384          * @type Object
3385          */
3386         YESNOCANCEL : {yes:true, no:true, cancel:true},
3387
3388         /**
3389          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3390          * @type Number
3391          */
3392         defaultTextHeight : 75,
3393         /**
3394          * The maximum width in pixels of the message box (defaults to 600)
3395          * @type Number
3396          */
3397         maxWidth : 600,
3398         /**
3399          * The minimum width in pixels of the message box (defaults to 100)
3400          * @type Number
3401          */
3402         minWidth : 100,
3403         /**
3404          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3405          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3406          * @type Number
3407          */
3408         minProgressWidth : 250,
3409         /**
3410          * An object containing the default button text strings that can be overriden for localized language support.
3411          * Supported properties are: ok, cancel, yes and no.
3412          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3413          * @type Object
3414          */
3415         buttonText : {
3416             ok : "OK",
3417             cancel : "Cancel",
3418             yes : "Yes",
3419             no : "No"
3420         }
3421     };
3422 }();
3423
3424 /**
3425  * Shorthand for {@link Roo.MessageBox}
3426  */
3427 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3428 Roo.Msg = Roo.Msg || Roo.MessageBox;
3429 /*
3430  * - LGPL
3431  *
3432  * navbar
3433  * 
3434  */
3435
3436 /**
3437  * @class Roo.bootstrap.Navbar
3438  * @extends Roo.bootstrap.Component
3439  * Bootstrap Navbar class
3440
3441  * @constructor
3442  * Create a new Navbar
3443  * @param {Object} config The config object
3444  */
3445
3446
3447 Roo.bootstrap.Navbar = function(config){
3448     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3449     this.addEvents({
3450         // raw events
3451         /**
3452          * @event beforetoggle
3453          * Fire before toggle the menu
3454          * @param {Roo.EventObject} e
3455          */
3456         "beforetoggle" : true
3457     });
3458 };
3459
3460 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3461     
3462     
3463    
3464     // private
3465     navItems : false,
3466     loadMask : false,
3467     
3468     
3469     getAutoCreate : function(){
3470         
3471         
3472         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3473         
3474     },
3475     
3476     initEvents :function ()
3477     {
3478         //Roo.log(this.el.select('.navbar-toggle',true));
3479         this.el.select('.navbar-toggle',true).on('click', function() {
3480             if(this.fireEvent('beforetoggle', this) !== false){
3481                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3482             }
3483             
3484         }, this);
3485         
3486         var mark = {
3487             tag: "div",
3488             cls:"x-dlg-mask"
3489         };
3490         
3491         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3492         
3493         var size = this.el.getSize();
3494         this.maskEl.setSize(size.width, size.height);
3495         this.maskEl.enableDisplayMode("block");
3496         this.maskEl.hide();
3497         
3498         if(this.loadMask){
3499             this.maskEl.show();
3500         }
3501     },
3502     
3503     
3504     getChildContainer : function()
3505     {
3506         if (this.el.select('.collapse').getCount()) {
3507             return this.el.select('.collapse',true).first();
3508         }
3509         
3510         return this.el;
3511     },
3512     
3513     mask : function()
3514     {
3515         this.maskEl.show();
3516     },
3517     
3518     unmask : function()
3519     {
3520         this.maskEl.hide();
3521     } 
3522     
3523     
3524     
3525     
3526 });
3527
3528
3529
3530  
3531
3532  /*
3533  * - LGPL
3534  *
3535  * navbar
3536  * 
3537  */
3538
3539 /**
3540  * @class Roo.bootstrap.NavSimplebar
3541  * @extends Roo.bootstrap.Navbar
3542  * Bootstrap Sidebar class
3543  *
3544  * @cfg {Boolean} inverse is inverted color
3545  * 
3546  * @cfg {String} type (nav | pills | tabs)
3547  * @cfg {Boolean} arrangement stacked | justified
3548  * @cfg {String} align (left | right) alignment
3549  * 
3550  * @cfg {Boolean} main (true|false) main nav bar? default false
3551  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3552  * 
3553  * @cfg {String} tag (header|footer|nav|div) default is nav 
3554
3555  * 
3556  * 
3557  * 
3558  * @constructor
3559  * Create a new Sidebar
3560  * @param {Object} config The config object
3561  */
3562
3563
3564 Roo.bootstrap.NavSimplebar = function(config){
3565     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3566 };
3567
3568 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3569     
3570     inverse: false,
3571     
3572     type: false,
3573     arrangement: '',
3574     align : false,
3575     
3576     
3577     
3578     main : false,
3579     
3580     
3581     tag : false,
3582     
3583     
3584     getAutoCreate : function(){
3585         
3586         
3587         var cfg = {
3588             tag : this.tag || 'div',
3589             cls : 'navbar'
3590         };
3591           
3592         
3593         cfg.cn = [
3594             {
3595                 cls: 'nav',
3596                 tag : 'ul'
3597             }
3598         ];
3599         
3600          
3601         this.type = this.type || 'nav';
3602         if (['tabs','pills'].indexOf(this.type)!==-1) {
3603             cfg.cn[0].cls += ' nav-' + this.type
3604         
3605         
3606         } else {
3607             if (this.type!=='nav') {
3608                 Roo.log('nav type must be nav/tabs/pills')
3609             }
3610             cfg.cn[0].cls += ' navbar-nav'
3611         }
3612         
3613         
3614         
3615         
3616         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3617             cfg.cn[0].cls += ' nav-' + this.arrangement;
3618         }
3619         
3620         
3621         if (this.align === 'right') {
3622             cfg.cn[0].cls += ' navbar-right';
3623         }
3624         
3625         if (this.inverse) {
3626             cfg.cls += ' navbar-inverse';
3627             
3628         }
3629         
3630         
3631         return cfg;
3632     
3633         
3634     }
3635     
3636     
3637     
3638 });
3639
3640
3641
3642  
3643
3644  
3645        /*
3646  * - LGPL
3647  *
3648  * navbar
3649  * 
3650  */
3651
3652 /**
3653  * @class Roo.bootstrap.NavHeaderbar
3654  * @extends Roo.bootstrap.NavSimplebar
3655  * Bootstrap Sidebar class
3656  *
3657  * @cfg {String} brand what is brand
3658  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3659  * @cfg {String} brand_href href of the brand
3660  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3661  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3662  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3663  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3664  * 
3665  * @constructor
3666  * Create a new Sidebar
3667  * @param {Object} config The config object
3668  */
3669
3670
3671 Roo.bootstrap.NavHeaderbar = function(config){
3672     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3673       
3674 };
3675
3676 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3677     
3678     position: '',
3679     brand: '',
3680     brand_href: false,
3681     srButton : true,
3682     autohide : false,
3683     desktopCenter : false,
3684    
3685     
3686     getAutoCreate : function(){
3687         
3688         var   cfg = {
3689             tag: this.nav || 'nav',
3690             cls: 'navbar',
3691             role: 'navigation',
3692             cn: []
3693         };
3694         
3695         var cn = cfg.cn;
3696         if (this.desktopCenter) {
3697             cn.push({cls : 'container', cn : []});
3698             cn = cn[0].cn;
3699         }
3700         
3701         if(this.srButton){
3702             cn.push({
3703                 tag: 'div',
3704                 cls: 'navbar-header',
3705                 cn: [
3706                     {
3707                         tag: 'button',
3708                         type: 'button',
3709                         cls: 'navbar-toggle',
3710                         'data-toggle': 'collapse',
3711                         cn: [
3712                             {
3713                                 tag: 'span',
3714                                 cls: 'sr-only',
3715                                 html: 'Toggle navigation'
3716                             },
3717                             {
3718                                 tag: 'span',
3719                                 cls: 'icon-bar'
3720                             },
3721                             {
3722                                 tag: 'span',
3723                                 cls: 'icon-bar'
3724                             },
3725                             {
3726                                 tag: 'span',
3727                                 cls: 'icon-bar'
3728                             }
3729                         ]
3730                     }
3731                 ]
3732             });
3733         }
3734         
3735         cn.push({
3736             tag: 'div',
3737             cls: 'collapse navbar-collapse',
3738             cn : []
3739         });
3740         
3741         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3742         
3743         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3744             cfg.cls += ' navbar-' + this.position;
3745             
3746             // tag can override this..
3747             
3748             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3749         }
3750         
3751         if (this.brand !== '') {
3752             cn[0].cn.push({
3753                 tag: 'a',
3754                 href: this.brand_href ? this.brand_href : '#',
3755                 cls: 'navbar-brand',
3756                 cn: [
3757                 this.brand
3758                 ]
3759             });
3760         }
3761         
3762         if(this.main){
3763             cfg.cls += ' main-nav';
3764         }
3765         
3766         
3767         return cfg;
3768
3769         
3770     },
3771     getHeaderChildContainer : function()
3772     {
3773         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3774             return this.el.select('.navbar-header',true).first();
3775         }
3776         
3777         return this.getChildContainer();
3778     },
3779     
3780     
3781     initEvents : function()
3782     {
3783         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3784         
3785         if (this.autohide) {
3786             
3787             var prevScroll = 0;
3788             var ft = this.el;
3789             
3790             Roo.get(document).on('scroll',function(e) {
3791                 var ns = Roo.get(document).getScroll().top;
3792                 var os = prevScroll;
3793                 prevScroll = ns;
3794                 
3795                 if(ns > os){
3796                     ft.removeClass('slideDown');
3797                     ft.addClass('slideUp');
3798                     return;
3799                 }
3800                 ft.removeClass('slideUp');
3801                 ft.addClass('slideDown');
3802                  
3803               
3804           },this);
3805         }
3806     }    
3807     
3808 });
3809
3810
3811
3812  
3813
3814  /*
3815  * - LGPL
3816  *
3817  * navbar
3818  * 
3819  */
3820
3821 /**
3822  * @class Roo.bootstrap.NavSidebar
3823  * @extends Roo.bootstrap.Navbar
3824  * Bootstrap Sidebar class
3825  * 
3826  * @constructor
3827  * Create a new Sidebar
3828  * @param {Object} config The config object
3829  */
3830
3831
3832 Roo.bootstrap.NavSidebar = function(config){
3833     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3834 };
3835
3836 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3837     
3838     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3839     
3840     getAutoCreate : function(){
3841         
3842         
3843         return  {
3844             tag: 'div',
3845             cls: 'sidebar sidebar-nav'
3846         };
3847     
3848         
3849     }
3850     
3851     
3852     
3853 });
3854
3855
3856
3857  
3858
3859  /*
3860  * - LGPL
3861  *
3862  * nav group
3863  * 
3864  */
3865
3866 /**
3867  * @class Roo.bootstrap.NavGroup
3868  * @extends Roo.bootstrap.Component
3869  * Bootstrap NavGroup class
3870  * @cfg {String} align (left|right)
3871  * @cfg {Boolean} inverse
3872  * @cfg {String} type (nav|pills|tab) default nav
3873  * @cfg {String} navId - reference Id for navbar.
3874
3875  * 
3876  * @constructor
3877  * Create a new nav group
3878  * @param {Object} config The config object
3879  */
3880
3881 Roo.bootstrap.NavGroup = function(config){
3882     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3883     this.navItems = [];
3884    
3885     Roo.bootstrap.NavGroup.register(this);
3886      this.addEvents({
3887         /**
3888              * @event changed
3889              * Fires when the active item changes
3890              * @param {Roo.bootstrap.NavGroup} this
3891              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3892              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3893          */
3894         'changed': true
3895      });
3896     
3897 };
3898
3899 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3900     
3901     align: '',
3902     inverse: false,
3903     form: false,
3904     type: 'nav',
3905     navId : '',
3906     // private
3907     
3908     navItems : false, 
3909     
3910     getAutoCreate : function()
3911     {
3912         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3913         
3914         cfg = {
3915             tag : 'ul',
3916             cls: 'nav' 
3917         };
3918         
3919         if (['tabs','pills'].indexOf(this.type)!==-1) {
3920             cfg.cls += ' nav-' + this.type
3921         } else {
3922             if (this.type!=='nav') {
3923                 Roo.log('nav type must be nav/tabs/pills')
3924             }
3925             cfg.cls += ' navbar-nav'
3926         }
3927         
3928         if (this.parent().sidebar) {
3929             cfg = {
3930                 tag: 'ul',
3931                 cls: 'dashboard-menu sidebar-menu'
3932             };
3933             
3934             return cfg;
3935         }
3936         
3937         if (this.form === true) {
3938             cfg = {
3939                 tag: 'form',
3940                 cls: 'navbar-form'
3941             };
3942             
3943             if (this.align === 'right') {
3944                 cfg.cls += ' navbar-right';
3945             } else {
3946                 cfg.cls += ' navbar-left';
3947             }
3948         }
3949         
3950         if (this.align === 'right') {
3951             cfg.cls += ' navbar-right';
3952         }
3953         
3954         if (this.inverse) {
3955             cfg.cls += ' navbar-inverse';
3956             
3957         }
3958         
3959         
3960         return cfg;
3961     },
3962     /**
3963     * sets the active Navigation item
3964     * @param {Roo.bootstrap.NavItem} the new current navitem
3965     */
3966     setActiveItem : function(item)
3967     {
3968         var prev = false;
3969         Roo.each(this.navItems, function(v){
3970             if (v == item) {
3971                 return ;
3972             }
3973             if (v.isActive()) {
3974                 v.setActive(false, true);
3975                 prev = v;
3976                 
3977             }
3978             
3979         });
3980
3981         item.setActive(true, true);
3982         this.fireEvent('changed', this, item, prev);
3983         
3984         
3985     },
3986     /**
3987     * gets the active Navigation item
3988     * @return {Roo.bootstrap.NavItem} the current navitem
3989     */
3990     getActive : function()
3991     {
3992         
3993         var prev = false;
3994         Roo.each(this.navItems, function(v){
3995             
3996             if (v.isActive()) {
3997                 prev = v;
3998                 
3999             }
4000             
4001         });
4002         return prev;
4003     },
4004     
4005     indexOfNav : function()
4006     {
4007         
4008         var prev = false;
4009         Roo.each(this.navItems, function(v,i){
4010             
4011             if (v.isActive()) {
4012                 prev = i;
4013                 
4014             }
4015             
4016         });
4017         return prev;
4018     },
4019     /**
4020     * adds a Navigation item
4021     * @param {Roo.bootstrap.NavItem} the navitem to add
4022     */
4023     addItem : function(cfg)
4024     {
4025         var cn = new Roo.bootstrap.NavItem(cfg);
4026         this.register(cn);
4027         cn.parentId = this.id;
4028         cn.onRender(this.el, null);
4029         return cn;
4030     },
4031     /**
4032     * register a Navigation item
4033     * @param {Roo.bootstrap.NavItem} the navitem to add
4034     */
4035     register : function(item)
4036     {
4037         this.navItems.push( item);
4038         item.navId = this.navId;
4039     
4040     },
4041     
4042     /**
4043     * clear all the Navigation item
4044     */
4045    
4046     clearAll : function()
4047     {
4048         this.navItems = [];
4049         this.el.dom.innerHTML = '';
4050     },
4051     
4052     getNavItem: function(tabId)
4053     {
4054         var ret = false;
4055         Roo.each(this.navItems, function(e) {
4056             if (e.tabId == tabId) {
4057                ret =  e;
4058                return false;
4059             }
4060             return true;
4061             
4062         });
4063         return ret;
4064     },
4065     
4066     setActiveNext : function()
4067     {
4068         var i = this.indexOfNav(this.getActive());
4069         if (i > this.navItems.length) {
4070             return;
4071         }
4072         this.setActiveItem(this.navItems[i+1]);
4073     },
4074     setActivePrev : function()
4075     {
4076         var i = this.indexOfNav(this.getActive());
4077         if (i  < 1) {
4078             return;
4079         }
4080         this.setActiveItem(this.navItems[i-1]);
4081     },
4082     clearWasActive : function(except) {
4083         Roo.each(this.navItems, function(e) {
4084             if (e.tabId != except.tabId && e.was_active) {
4085                e.was_active = false;
4086                return false;
4087             }
4088             return true;
4089             
4090         });
4091     },
4092     getWasActive : function ()
4093     {
4094         var r = false;
4095         Roo.each(this.navItems, function(e) {
4096             if (e.was_active) {
4097                r = e;
4098                return false;
4099             }
4100             return true;
4101             
4102         });
4103         return r;
4104     }
4105     
4106     
4107 });
4108
4109  
4110 Roo.apply(Roo.bootstrap.NavGroup, {
4111     
4112     groups: {},
4113      /**
4114     * register a Navigation Group
4115     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4116     */
4117     register : function(navgrp)
4118     {
4119         this.groups[navgrp.navId] = navgrp;
4120         
4121     },
4122     /**
4123     * fetch a Navigation Group based on the navigation ID
4124     * @param {string} the navgroup to add
4125     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4126     */
4127     get: function(navId) {
4128         if (typeof(this.groups[navId]) == 'undefined') {
4129             return false;
4130             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4131         }
4132         return this.groups[navId] ;
4133     }
4134     
4135     
4136     
4137 });
4138
4139  /*
4140  * - LGPL
4141  *
4142  * row
4143  * 
4144  */
4145
4146 /**
4147  * @class Roo.bootstrap.NavItem
4148  * @extends Roo.bootstrap.Component
4149  * Bootstrap Navbar.NavItem class
4150  * @cfg {String} href  link to
4151  * @cfg {String} html content of button
4152  * @cfg {String} badge text inside badge
4153  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4154  * @cfg {String} glyphicon name of glyphicon
4155  * @cfg {String} icon name of font awesome icon
4156  * @cfg {Boolean} active Is item active
4157  * @cfg {Boolean} disabled Is item disabled
4158  
4159  * @cfg {Boolean} preventDefault (true | false) default false
4160  * @cfg {String} tabId the tab that this item activates.
4161  * @cfg {String} tagtype (a|span) render as a href or span?
4162  * @cfg {Boolean} animateRef (true|false) link to element default false  
4163   
4164  * @constructor
4165  * Create a new Navbar Item
4166  * @param {Object} config The config object
4167  */
4168 Roo.bootstrap.NavItem = function(config){
4169     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4170     this.addEvents({
4171         // raw events
4172         /**
4173          * @event click
4174          * The raw click event for the entire grid.
4175          * @param {Roo.EventObject} e
4176          */
4177         "click" : true,
4178          /**
4179             * @event changed
4180             * Fires when the active item active state changes
4181             * @param {Roo.bootstrap.NavItem} this
4182             * @param {boolean} state the new state
4183              
4184          */
4185         'changed': true,
4186         /**
4187             * @event scrollto
4188             * Fires when scroll to element
4189             * @param {Roo.bootstrap.NavItem} this
4190             * @param {Object} options
4191             * @param {Roo.EventObject} e
4192              
4193          */
4194         'scrollto': true
4195     });
4196    
4197 };
4198
4199 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4200     
4201     href: false,
4202     html: '',
4203     badge: '',
4204     icon: false,
4205     glyphicon: false,
4206     active: false,
4207     preventDefault : false,
4208     tabId : false,
4209     tagtype : 'a',
4210     disabled : false,
4211     animateRef : false,
4212     was_active : false,
4213     
4214     getAutoCreate : function(){
4215          
4216         var cfg = {
4217             tag: 'li',
4218             cls: 'nav-item'
4219             
4220         };
4221         
4222         if (this.active) {
4223             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4224         }
4225         if (this.disabled) {
4226             cfg.cls += ' disabled';
4227         }
4228         
4229         if (this.href || this.html || this.glyphicon || this.icon) {
4230             cfg.cn = [
4231                 {
4232                     tag: this.tagtype,
4233                     href : this.href || "#",
4234                     html: this.html || ''
4235                 }
4236             ];
4237             
4238             if (this.icon) {
4239                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4240             }
4241
4242             if(this.glyphicon) {
4243                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4244             }
4245             
4246             if (this.menu) {
4247                 
4248                 cfg.cn[0].html += " <span class='caret'></span>";
4249              
4250             }
4251             
4252             if (this.badge !== '') {
4253                  
4254                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4255             }
4256         }
4257         
4258         
4259         
4260         return cfg;
4261     },
4262     initEvents: function() 
4263     {
4264         if (typeof (this.menu) != 'undefined') {
4265             this.menu.parentType = this.xtype;
4266             this.menu.triggerEl = this.el;
4267             this.menu = this.addxtype(Roo.apply({}, this.menu));
4268         }
4269         
4270         this.el.select('a',true).on('click', this.onClick, this);
4271         
4272         if(this.tagtype == 'span'){
4273             this.el.select('span',true).on('click', this.onClick, this);
4274         }
4275        
4276         // at this point parent should be available..
4277         this.parent().register(this);
4278     },
4279     
4280     onClick : function(e)
4281     {
4282         if (e.getTarget('.dropdown-menu-item')) {
4283             // did you click on a menu itemm.... - then don't trigger onclick..
4284             return;
4285         }
4286         
4287         if(
4288                 this.preventDefault || 
4289                 this.href == '#' 
4290         ){
4291             Roo.log("NavItem - prevent Default?");
4292             e.preventDefault();
4293         }
4294         
4295         if (this.disabled) {
4296             return;
4297         }
4298         
4299         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4300         if (tg && tg.transition) {
4301             Roo.log("waiting for the transitionend");
4302             return;
4303         }
4304         
4305         
4306         
4307         //Roo.log("fire event clicked");
4308         if(this.fireEvent('click', this, e) === false){
4309             return;
4310         };
4311         
4312         if(this.tagtype == 'span'){
4313             return;
4314         }
4315         
4316         //Roo.log(this.href);
4317         var ael = this.el.select('a',true).first();
4318         //Roo.log(ael);
4319         
4320         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4321             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4322             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4323                 return; // ignore... - it's a 'hash' to another page.
4324             }
4325             Roo.log("NavItem - prevent Default?");
4326             e.preventDefault();
4327             this.scrollToElement(e);
4328         }
4329         
4330         
4331         var p =  this.parent();
4332    
4333         if (['tabs','pills'].indexOf(p.type)!==-1) {
4334             if (typeof(p.setActiveItem) !== 'undefined') {
4335                 p.setActiveItem(this);
4336             }
4337         }
4338         
4339         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4340         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4341             // remove the collapsed menu expand...
4342             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4343         }
4344     },
4345     
4346     isActive: function () {
4347         return this.active
4348     },
4349     setActive : function(state, fire, is_was_active)
4350     {
4351         if (this.active && !state && this.navId) {
4352             this.was_active = true;
4353             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4354             if (nv) {
4355                 nv.clearWasActive(this);
4356             }
4357             
4358         }
4359         this.active = state;
4360         
4361         if (!state ) {
4362             this.el.removeClass('active');
4363         } else if (!this.el.hasClass('active')) {
4364             this.el.addClass('active');
4365         }
4366         if (fire) {
4367             this.fireEvent('changed', this, state);
4368         }
4369         
4370         // show a panel if it's registered and related..
4371         
4372         if (!this.navId || !this.tabId || !state || is_was_active) {
4373             return;
4374         }
4375         
4376         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4377         if (!tg) {
4378             return;
4379         }
4380         var pan = tg.getPanelByName(this.tabId);
4381         if (!pan) {
4382             return;
4383         }
4384         // if we can not flip to new panel - go back to old nav highlight..
4385         if (false == tg.showPanel(pan)) {
4386             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4387             if (nv) {
4388                 var onav = nv.getWasActive();
4389                 if (onav) {
4390                     onav.setActive(true, false, true);
4391                 }
4392             }
4393             
4394         }
4395         
4396         
4397         
4398     },
4399      // this should not be here...
4400     setDisabled : function(state)
4401     {
4402         this.disabled = state;
4403         if (!state ) {
4404             this.el.removeClass('disabled');
4405         } else if (!this.el.hasClass('disabled')) {
4406             this.el.addClass('disabled');
4407         }
4408         
4409     },
4410     
4411     /**
4412      * Fetch the element to display the tooltip on.
4413      * @return {Roo.Element} defaults to this.el
4414      */
4415     tooltipEl : function()
4416     {
4417         return this.el.select('' + this.tagtype + '', true).first();
4418     },
4419     
4420     scrollToElement : function(e)
4421     {
4422         var c = document.body;
4423         
4424         /*
4425          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4426          */
4427         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4428             c = document.documentElement;
4429         }
4430         
4431         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4432         
4433         if(!target){
4434             return;
4435         }
4436
4437         var o = target.calcOffsetsTo(c);
4438         
4439         var options = {
4440             target : target,
4441             value : o[1]
4442         };
4443         
4444         this.fireEvent('scrollto', this, options, e);
4445         
4446         Roo.get(c).scrollTo('top', options.value, true);
4447         
4448         return;
4449     }
4450 });
4451  
4452
4453  /*
4454  * - LGPL
4455  *
4456  * sidebar item
4457  *
4458  *  li
4459  *    <span> icon </span>
4460  *    <span> text </span>
4461  *    <span>badge </span>
4462  */
4463
4464 /**
4465  * @class Roo.bootstrap.NavSidebarItem
4466  * @extends Roo.bootstrap.NavItem
4467  * Bootstrap Navbar.NavSidebarItem class
4468  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4469  * {bool} open is the menu open
4470  * @constructor
4471  * Create a new Navbar Button
4472  * @param {Object} config The config object
4473  */
4474 Roo.bootstrap.NavSidebarItem = function(config){
4475     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4476     this.addEvents({
4477         // raw events
4478         /**
4479          * @event click
4480          * The raw click event for the entire grid.
4481          * @param {Roo.EventObject} e
4482          */
4483         "click" : true,
4484          /**
4485             * @event changed
4486             * Fires when the active item active state changes
4487             * @param {Roo.bootstrap.NavSidebarItem} this
4488             * @param {boolean} state the new state
4489              
4490          */
4491         'changed': true
4492     });
4493    
4494 };
4495
4496 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4497     
4498     badgeWeight : 'default',
4499     
4500     open: false,
4501     
4502     getAutoCreate : function(){
4503         
4504         
4505         var a = {
4506                 tag: 'a',
4507                 href : this.href || '#',
4508                 cls: '',
4509                 html : '',
4510                 cn : []
4511         };
4512         var cfg = {
4513             tag: 'li',
4514             cls: '',
4515             cn: [ a ]
4516         };
4517         var span = {
4518             tag: 'span',
4519             html : this.html || ''
4520         };
4521         
4522         
4523         if (this.active) {
4524             cfg.cls += ' active';
4525         }
4526         
4527         if (this.disabled) {
4528             cfg.cls += ' disabled';
4529         }
4530         if (this.open) {
4531             cfg.cls += ' open x-open';
4532         }
4533         // left icon..
4534         if (this.glyphicon || this.icon) {
4535             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4536             a.cn.push({ tag : 'i', cls : c }) ;
4537         }
4538         // html..
4539         a.cn.push(span);
4540         // then badge..
4541         if (this.badge !== '') {
4542             
4543             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4544         }
4545         // fi
4546         if (this.menu) {
4547             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4548             a.cls += 'dropdown-toggle treeview' ;
4549         }
4550         
4551         return cfg;
4552          
4553            
4554     },
4555     
4556     initEvents : function()
4557     { 
4558         if (typeof (this.menu) != 'undefined') {
4559             this.menu.parentType = this.xtype;
4560             this.menu.triggerEl = this.el;
4561             this.menu = this.addxtype(Roo.apply({}, this.menu));
4562         }
4563         
4564         this.el.on('click', this.onClick, this);
4565        
4566     
4567         if(this.badge !== ''){
4568  
4569             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4570         }
4571         
4572     },
4573     
4574     onClick : function(e)
4575     {
4576         if(this.disabled){
4577             e.preventDefault();
4578             return;
4579         }
4580         
4581         if(this.preventDefault){
4582             e.preventDefault();
4583         }
4584         
4585         this.fireEvent('click', this);
4586     },
4587     
4588     disable : function()
4589     {
4590         this.setDisabled(true);
4591     },
4592     
4593     enable : function()
4594     {
4595         this.setDisabled(false);
4596     },
4597     
4598     setDisabled : function(state)
4599     {
4600         if(this.disabled == state){
4601             return;
4602         }
4603         
4604         this.disabled = state;
4605         
4606         if (state) {
4607             this.el.addClass('disabled');
4608             return;
4609         }
4610         
4611         this.el.removeClass('disabled');
4612         
4613         return;
4614     },
4615     
4616     setActive : function(state)
4617     {
4618         if(this.active == state){
4619             return;
4620         }
4621         
4622         this.active = state;
4623         
4624         if (state) {
4625             this.el.addClass('active');
4626             return;
4627         }
4628         
4629         this.el.removeClass('active');
4630         
4631         return;
4632     },
4633     
4634     isActive: function () 
4635     {
4636         return this.active;
4637     },
4638     
4639     setBadge : function(str)
4640     {
4641         if(!this.badgeEl){
4642             return;
4643         }
4644         
4645         this.badgeEl.dom.innerHTML = str;
4646     }
4647     
4648    
4649      
4650  
4651 });
4652  
4653
4654  /*
4655  * - LGPL
4656  *
4657  * row
4658  * 
4659  */
4660
4661 /**
4662  * @class Roo.bootstrap.Row
4663  * @extends Roo.bootstrap.Component
4664  * Bootstrap Row class (contains columns...)
4665  * 
4666  * @constructor
4667  * Create a new Row
4668  * @param {Object} config The config object
4669  */
4670
4671 Roo.bootstrap.Row = function(config){
4672     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4673 };
4674
4675 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4676     
4677     getAutoCreate : function(){
4678        return {
4679             cls: 'row clearfix'
4680        };
4681     }
4682     
4683     
4684 });
4685
4686  
4687
4688  /*
4689  * - LGPL
4690  *
4691  * element
4692  * 
4693  */
4694
4695 /**
4696  * @class Roo.bootstrap.Element
4697  * @extends Roo.bootstrap.Component
4698  * Bootstrap Element class
4699  * @cfg {String} html contents of the element
4700  * @cfg {String} tag tag of the element
4701  * @cfg {String} cls class of the element
4702  * @cfg {Boolean} preventDefault (true|false) default false
4703  * @cfg {Boolean} clickable (true|false) default false
4704  * 
4705  * @constructor
4706  * Create a new Element
4707  * @param {Object} config The config object
4708  */
4709
4710 Roo.bootstrap.Element = function(config){
4711     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4712     
4713     this.addEvents({
4714         // raw events
4715         /**
4716          * @event click
4717          * When a element is chick
4718          * @param {Roo.bootstrap.Element} this
4719          * @param {Roo.EventObject} e
4720          */
4721         "click" : true
4722     });
4723 };
4724
4725 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4726     
4727     tag: 'div',
4728     cls: '',
4729     html: '',
4730     preventDefault: false, 
4731     clickable: false,
4732     
4733     getAutoCreate : function(){
4734         
4735         var cfg = {
4736             tag: this.tag,
4737             cls: this.cls,
4738             html: this.html
4739         };
4740         
4741         return cfg;
4742     },
4743     
4744     initEvents: function() 
4745     {
4746         Roo.bootstrap.Element.superclass.initEvents.call(this);
4747         
4748         if(this.clickable){
4749             this.el.on('click', this.onClick, this);
4750         }
4751         
4752     },
4753     
4754     onClick : function(e)
4755     {
4756         if(this.preventDefault){
4757             e.preventDefault();
4758         }
4759         
4760         this.fireEvent('click', this, e);
4761     },
4762     
4763     getValue : function()
4764     {
4765         return this.el.dom.innerHTML;
4766     },
4767     
4768     setValue : function(value)
4769     {
4770         this.el.dom.innerHTML = value;
4771     }
4772    
4773 });
4774
4775  
4776
4777  /*
4778  * - LGPL
4779  *
4780  * pagination
4781  * 
4782  */
4783
4784 /**
4785  * @class Roo.bootstrap.Pagination
4786  * @extends Roo.bootstrap.Component
4787  * Bootstrap Pagination class
4788  * @cfg {String} size xs | sm | md | lg
4789  * @cfg {Boolean} inverse false | true
4790  * 
4791  * @constructor
4792  * Create a new Pagination
4793  * @param {Object} config The config object
4794  */
4795
4796 Roo.bootstrap.Pagination = function(config){
4797     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4798 };
4799
4800 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4801     
4802     cls: false,
4803     size: false,
4804     inverse: false,
4805     
4806     getAutoCreate : function(){
4807         var cfg = {
4808             tag: 'ul',
4809                 cls: 'pagination'
4810         };
4811         if (this.inverse) {
4812             cfg.cls += ' inverse';
4813         }
4814         if (this.html) {
4815             cfg.html=this.html;
4816         }
4817         if (this.cls) {
4818             cfg.cls += " " + this.cls;
4819         }
4820         return cfg;
4821     }
4822    
4823 });
4824
4825  
4826
4827  /*
4828  * - LGPL
4829  *
4830  * Pagination item
4831  * 
4832  */
4833
4834
4835 /**
4836  * @class Roo.bootstrap.PaginationItem
4837  * @extends Roo.bootstrap.Component
4838  * Bootstrap PaginationItem class
4839  * @cfg {String} html text
4840  * @cfg {String} href the link
4841  * @cfg {Boolean} preventDefault (true | false) default true
4842  * @cfg {Boolean} active (true | false) default false
4843  * @cfg {Boolean} disabled default false
4844  * 
4845  * 
4846  * @constructor
4847  * Create a new PaginationItem
4848  * @param {Object} config The config object
4849  */
4850
4851
4852 Roo.bootstrap.PaginationItem = function(config){
4853     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4854     this.addEvents({
4855         // raw events
4856         /**
4857          * @event click
4858          * The raw click event for the entire grid.
4859          * @param {Roo.EventObject} e
4860          */
4861         "click" : true
4862     });
4863 };
4864
4865 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4866     
4867     href : false,
4868     html : false,
4869     preventDefault: true,
4870     active : false,
4871     cls : false,
4872     disabled: false,
4873     
4874     getAutoCreate : function(){
4875         var cfg= {
4876             tag: 'li',
4877             cn: [
4878                 {
4879                     tag : 'a',
4880                     href : this.href ? this.href : '#',
4881                     html : this.html ? this.html : ''
4882                 }
4883             ]
4884         };
4885         
4886         if(this.cls){
4887             cfg.cls = this.cls;
4888         }
4889         
4890         if(this.disabled){
4891             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4892         }
4893         
4894         if(this.active){
4895             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4896         }
4897         
4898         return cfg;
4899     },
4900     
4901     initEvents: function() {
4902         
4903         this.el.on('click', this.onClick, this);
4904         
4905     },
4906     onClick : function(e)
4907     {
4908         Roo.log('PaginationItem on click ');
4909         if(this.preventDefault){
4910             e.preventDefault();
4911         }
4912         
4913         if(this.disabled){
4914             return;
4915         }
4916         
4917         this.fireEvent('click', this, e);
4918     }
4919    
4920 });
4921
4922  
4923
4924  /*
4925  * - LGPL
4926  *
4927  * slider
4928  * 
4929  */
4930
4931
4932 /**
4933  * @class Roo.bootstrap.Slider
4934  * @extends Roo.bootstrap.Component
4935  * Bootstrap Slider class
4936  *    
4937  * @constructor
4938  * Create a new Slider
4939  * @param {Object} config The config object
4940  */
4941
4942 Roo.bootstrap.Slider = function(config){
4943     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4944 };
4945
4946 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4947     
4948     getAutoCreate : function(){
4949         
4950         var cfg = {
4951             tag: 'div',
4952             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4953             cn: [
4954                 {
4955                     tag: 'a',
4956                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4957                 }
4958             ]
4959         };
4960         
4961         return cfg;
4962     }
4963    
4964 });
4965
4966  /*
4967  * Based on:
4968  * Ext JS Library 1.1.1
4969  * Copyright(c) 2006-2007, Ext JS, LLC.
4970  *
4971  * Originally Released Under LGPL - original licence link has changed is not relivant.
4972  *
4973  * Fork - LGPL
4974  * <script type="text/javascript">
4975  */
4976  
4977
4978 /**
4979  * @class Roo.grid.ColumnModel
4980  * @extends Roo.util.Observable
4981  * This is the default implementation of a ColumnModel used by the Grid. It defines
4982  * the columns in the grid.
4983  * <br>Usage:<br>
4984  <pre><code>
4985  var colModel = new Roo.grid.ColumnModel([
4986         {header: "Ticker", width: 60, sortable: true, locked: true},
4987         {header: "Company Name", width: 150, sortable: true},
4988         {header: "Market Cap.", width: 100, sortable: true},
4989         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4990         {header: "Employees", width: 100, sortable: true, resizable: false}
4991  ]);
4992  </code></pre>
4993  * <p>
4994  
4995  * The config options listed for this class are options which may appear in each
4996  * individual column definition.
4997  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4998  * @constructor
4999  * @param {Object} config An Array of column config objects. See this class's
5000  * config objects for details.
5001 */
5002 Roo.grid.ColumnModel = function(config){
5003         /**
5004      * The config passed into the constructor
5005      */
5006     this.config = config;
5007     this.lookup = {};
5008
5009     // if no id, create one
5010     // if the column does not have a dataIndex mapping,
5011     // map it to the order it is in the config
5012     for(var i = 0, len = config.length; i < len; i++){
5013         var c = config[i];
5014         if(typeof c.dataIndex == "undefined"){
5015             c.dataIndex = i;
5016         }
5017         if(typeof c.renderer == "string"){
5018             c.renderer = Roo.util.Format[c.renderer];
5019         }
5020         if(typeof c.id == "undefined"){
5021             c.id = Roo.id();
5022         }
5023         if(c.editor && c.editor.xtype){
5024             c.editor  = Roo.factory(c.editor, Roo.grid);
5025         }
5026         if(c.editor && c.editor.isFormField){
5027             c.editor = new Roo.grid.GridEditor(c.editor);
5028         }
5029         this.lookup[c.id] = c;
5030     }
5031
5032     /**
5033      * The width of columns which have no width specified (defaults to 100)
5034      * @type Number
5035      */
5036     this.defaultWidth = 100;
5037
5038     /**
5039      * Default sortable of columns which have no sortable specified (defaults to false)
5040      * @type Boolean
5041      */
5042     this.defaultSortable = false;
5043
5044     this.addEvents({
5045         /**
5046              * @event widthchange
5047              * Fires when the width of a column changes.
5048              * @param {ColumnModel} this
5049              * @param {Number} columnIndex The column index
5050              * @param {Number} newWidth The new width
5051              */
5052             "widthchange": true,
5053         /**
5054              * @event headerchange
5055              * Fires when the text of a header changes.
5056              * @param {ColumnModel} this
5057              * @param {Number} columnIndex The column index
5058              * @param {Number} newText The new header text
5059              */
5060             "headerchange": true,
5061         /**
5062              * @event hiddenchange
5063              * Fires when a column is hidden or "unhidden".
5064              * @param {ColumnModel} this
5065              * @param {Number} columnIndex The column index
5066              * @param {Boolean} hidden true if hidden, false otherwise
5067              */
5068             "hiddenchange": true,
5069             /**
5070          * @event columnmoved
5071          * Fires when a column is moved.
5072          * @param {ColumnModel} this
5073          * @param {Number} oldIndex
5074          * @param {Number} newIndex
5075          */
5076         "columnmoved" : true,
5077         /**
5078          * @event columlockchange
5079          * Fires when a column's locked state is changed
5080          * @param {ColumnModel} this
5081          * @param {Number} colIndex
5082          * @param {Boolean} locked true if locked
5083          */
5084         "columnlockchange" : true
5085     });
5086     Roo.grid.ColumnModel.superclass.constructor.call(this);
5087 };
5088 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5089     /**
5090      * @cfg {String} header The header text to display in the Grid view.
5091      */
5092     /**
5093      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5094      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5095      * specified, the column's index is used as an index into the Record's data Array.
5096      */
5097     /**
5098      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5099      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5100      */
5101     /**
5102      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5103      * Defaults to the value of the {@link #defaultSortable} property.
5104      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5105      */
5106     /**
5107      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5108      */
5109     /**
5110      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5111      */
5112     /**
5113      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5114      */
5115     /**
5116      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5117      */
5118     /**
5119      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5120      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5121      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5122      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5123      */
5124        /**
5125      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5126      */
5127     /**
5128      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5129      */
5130     /**
5131      * @cfg {String} cursor (Optional)
5132      */
5133     /**
5134      * @cfg {String} tooltip (Optional)
5135      */
5136     /**
5137      * @cfg {Number} xs (Optional)
5138      */
5139     /**
5140      * @cfg {Number} sm (Optional)
5141      */
5142     /**
5143      * @cfg {Number} md (Optional)
5144      */
5145     /**
5146      * @cfg {Number} lg (Optional)
5147      */
5148     /**
5149      * Returns the id of the column at the specified index.
5150      * @param {Number} index The column index
5151      * @return {String} the id
5152      */
5153     getColumnId : function(index){
5154         return this.config[index].id;
5155     },
5156
5157     /**
5158      * Returns the column for a specified id.
5159      * @param {String} id The column id
5160      * @return {Object} the column
5161      */
5162     getColumnById : function(id){
5163         return this.lookup[id];
5164     },
5165
5166     
5167     /**
5168      * Returns the column for a specified dataIndex.
5169      * @param {String} dataIndex The column dataIndex
5170      * @return {Object|Boolean} the column or false if not found
5171      */
5172     getColumnByDataIndex: function(dataIndex){
5173         var index = this.findColumnIndex(dataIndex);
5174         return index > -1 ? this.config[index] : false;
5175     },
5176     
5177     /**
5178      * Returns the index for a specified column id.
5179      * @param {String} id The column id
5180      * @return {Number} the index, or -1 if not found
5181      */
5182     getIndexById : function(id){
5183         for(var i = 0, len = this.config.length; i < len; i++){
5184             if(this.config[i].id == id){
5185                 return i;
5186             }
5187         }
5188         return -1;
5189     },
5190     
5191     /**
5192      * Returns the index for a specified column dataIndex.
5193      * @param {String} dataIndex The column dataIndex
5194      * @return {Number} the index, or -1 if not found
5195      */
5196     
5197     findColumnIndex : function(dataIndex){
5198         for(var i = 0, len = this.config.length; i < len; i++){
5199             if(this.config[i].dataIndex == dataIndex){
5200                 return i;
5201             }
5202         }
5203         return -1;
5204     },
5205     
5206     
5207     moveColumn : function(oldIndex, newIndex){
5208         var c = this.config[oldIndex];
5209         this.config.splice(oldIndex, 1);
5210         this.config.splice(newIndex, 0, c);
5211         this.dataMap = null;
5212         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5213     },
5214
5215     isLocked : function(colIndex){
5216         return this.config[colIndex].locked === true;
5217     },
5218
5219     setLocked : function(colIndex, value, suppressEvent){
5220         if(this.isLocked(colIndex) == value){
5221             return;
5222         }
5223         this.config[colIndex].locked = value;
5224         if(!suppressEvent){
5225             this.fireEvent("columnlockchange", this, colIndex, value);
5226         }
5227     },
5228
5229     getTotalLockedWidth : function(){
5230         var totalWidth = 0;
5231         for(var i = 0; i < this.config.length; i++){
5232             if(this.isLocked(i) && !this.isHidden(i)){
5233                 this.totalWidth += this.getColumnWidth(i);
5234             }
5235         }
5236         return totalWidth;
5237     },
5238
5239     getLockedCount : function(){
5240         for(var i = 0, len = this.config.length; i < len; i++){
5241             if(!this.isLocked(i)){
5242                 return i;
5243             }
5244         }
5245         
5246         return this.config.length;
5247     },
5248
5249     /**
5250      * Returns the number of columns.
5251      * @return {Number}
5252      */
5253     getColumnCount : function(visibleOnly){
5254         if(visibleOnly === true){
5255             var c = 0;
5256             for(var i = 0, len = this.config.length; i < len; i++){
5257                 if(!this.isHidden(i)){
5258                     c++;
5259                 }
5260             }
5261             return c;
5262         }
5263         return this.config.length;
5264     },
5265
5266     /**
5267      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5268      * @param {Function} fn
5269      * @param {Object} scope (optional)
5270      * @return {Array} result
5271      */
5272     getColumnsBy : function(fn, scope){
5273         var r = [];
5274         for(var i = 0, len = this.config.length; i < len; i++){
5275             var c = this.config[i];
5276             if(fn.call(scope||this, c, i) === true){
5277                 r[r.length] = c;
5278             }
5279         }
5280         return r;
5281     },
5282
5283     /**
5284      * Returns true if the specified column is sortable.
5285      * @param {Number} col The column index
5286      * @return {Boolean}
5287      */
5288     isSortable : function(col){
5289         if(typeof this.config[col].sortable == "undefined"){
5290             return this.defaultSortable;
5291         }
5292         return this.config[col].sortable;
5293     },
5294
5295     /**
5296      * Returns the rendering (formatting) function defined for the column.
5297      * @param {Number} col The column index.
5298      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5299      */
5300     getRenderer : function(col){
5301         if(!this.config[col].renderer){
5302             return Roo.grid.ColumnModel.defaultRenderer;
5303         }
5304         return this.config[col].renderer;
5305     },
5306
5307     /**
5308      * Sets the rendering (formatting) function for a column.
5309      * @param {Number} col The column index
5310      * @param {Function} fn The function to use to process the cell's raw data
5311      * to return HTML markup for the grid view. The render function is called with
5312      * the following parameters:<ul>
5313      * <li>Data value.</li>
5314      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5315      * <li>css A CSS style string to apply to the table cell.</li>
5316      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5317      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5318      * <li>Row index</li>
5319      * <li>Column index</li>
5320      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5321      */
5322     setRenderer : function(col, fn){
5323         this.config[col].renderer = fn;
5324     },
5325
5326     /**
5327      * Returns the width for the specified column.
5328      * @param {Number} col The column index
5329      * @return {Number}
5330      */
5331     getColumnWidth : function(col){
5332         return this.config[col].width * 1 || this.defaultWidth;
5333     },
5334
5335     /**
5336      * Sets the width for a column.
5337      * @param {Number} col The column index
5338      * @param {Number} width The new width
5339      */
5340     setColumnWidth : function(col, width, suppressEvent){
5341         this.config[col].width = width;
5342         this.totalWidth = null;
5343         if(!suppressEvent){
5344              this.fireEvent("widthchange", this, col, width);
5345         }
5346     },
5347
5348     /**
5349      * Returns the total width of all columns.
5350      * @param {Boolean} includeHidden True to include hidden column widths
5351      * @return {Number}
5352      */
5353     getTotalWidth : function(includeHidden){
5354         if(!this.totalWidth){
5355             this.totalWidth = 0;
5356             for(var i = 0, len = this.config.length; i < len; i++){
5357                 if(includeHidden || !this.isHidden(i)){
5358                     this.totalWidth += this.getColumnWidth(i);
5359                 }
5360             }
5361         }
5362         return this.totalWidth;
5363     },
5364
5365     /**
5366      * Returns the header for the specified column.
5367      * @param {Number} col The column index
5368      * @return {String}
5369      */
5370     getColumnHeader : function(col){
5371         return this.config[col].header;
5372     },
5373
5374     /**
5375      * Sets the header for a column.
5376      * @param {Number} col The column index
5377      * @param {String} header The new header
5378      */
5379     setColumnHeader : function(col, header){
5380         this.config[col].header = header;
5381         this.fireEvent("headerchange", this, col, header);
5382     },
5383
5384     /**
5385      * Returns the tooltip for the specified column.
5386      * @param {Number} col The column index
5387      * @return {String}
5388      */
5389     getColumnTooltip : function(col){
5390             return this.config[col].tooltip;
5391     },
5392     /**
5393      * Sets the tooltip for a column.
5394      * @param {Number} col The column index
5395      * @param {String} tooltip The new tooltip
5396      */
5397     setColumnTooltip : function(col, tooltip){
5398             this.config[col].tooltip = tooltip;
5399     },
5400
5401     /**
5402      * Returns the dataIndex for the specified column.
5403      * @param {Number} col The column index
5404      * @return {Number}
5405      */
5406     getDataIndex : function(col){
5407         return this.config[col].dataIndex;
5408     },
5409
5410     /**
5411      * Sets the dataIndex for a column.
5412      * @param {Number} col The column index
5413      * @param {Number} dataIndex The new dataIndex
5414      */
5415     setDataIndex : function(col, dataIndex){
5416         this.config[col].dataIndex = dataIndex;
5417     },
5418
5419     
5420     
5421     /**
5422      * Returns true if the cell is editable.
5423      * @param {Number} colIndex The column index
5424      * @param {Number} rowIndex The row index - this is nto actually used..?
5425      * @return {Boolean}
5426      */
5427     isCellEditable : function(colIndex, rowIndex){
5428         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5429     },
5430
5431     /**
5432      * Returns the editor defined for the cell/column.
5433      * return false or null to disable editing.
5434      * @param {Number} colIndex The column index
5435      * @param {Number} rowIndex The row index
5436      * @return {Object}
5437      */
5438     getCellEditor : function(colIndex, rowIndex){
5439         return this.config[colIndex].editor;
5440     },
5441
5442     /**
5443      * Sets if a column is editable.
5444      * @param {Number} col The column index
5445      * @param {Boolean} editable True if the column is editable
5446      */
5447     setEditable : function(col, editable){
5448         this.config[col].editable = editable;
5449     },
5450
5451
5452     /**
5453      * Returns true if the column is hidden.
5454      * @param {Number} colIndex The column index
5455      * @return {Boolean}
5456      */
5457     isHidden : function(colIndex){
5458         return this.config[colIndex].hidden;
5459     },
5460
5461
5462     /**
5463      * Returns true if the column width cannot be changed
5464      */
5465     isFixed : function(colIndex){
5466         return this.config[colIndex].fixed;
5467     },
5468
5469     /**
5470      * Returns true if the column can be resized
5471      * @return {Boolean}
5472      */
5473     isResizable : function(colIndex){
5474         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5475     },
5476     /**
5477      * Sets if a column is hidden.
5478      * @param {Number} colIndex The column index
5479      * @param {Boolean} hidden True if the column is hidden
5480      */
5481     setHidden : function(colIndex, hidden){
5482         this.config[colIndex].hidden = hidden;
5483         this.totalWidth = null;
5484         this.fireEvent("hiddenchange", this, colIndex, hidden);
5485     },
5486
5487     /**
5488      * Sets the editor for a column.
5489      * @param {Number} col The column index
5490      * @param {Object} editor The editor object
5491      */
5492     setEditor : function(col, editor){
5493         this.config[col].editor = editor;
5494     }
5495 });
5496
5497 Roo.grid.ColumnModel.defaultRenderer = function(value){
5498         if(typeof value == "string" && value.length < 1){
5499             return "&#160;";
5500         }
5501         return value;
5502 };
5503
5504 // Alias for backwards compatibility
5505 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5506 /*
5507  * Based on:
5508  * Ext JS Library 1.1.1
5509  * Copyright(c) 2006-2007, Ext JS, LLC.
5510  *
5511  * Originally Released Under LGPL - original licence link has changed is not relivant.
5512  *
5513  * Fork - LGPL
5514  * <script type="text/javascript">
5515  */
5516  
5517 /**
5518  * @class Roo.LoadMask
5519  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5520  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5521  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5522  * element's UpdateManager load indicator and will be destroyed after the initial load.
5523  * @constructor
5524  * Create a new LoadMask
5525  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5526  * @param {Object} config The config object
5527  */
5528 Roo.LoadMask = function(el, config){
5529     this.el = Roo.get(el);
5530     Roo.apply(this, config);
5531     if(this.store){
5532         this.store.on('beforeload', this.onBeforeLoad, this);
5533         this.store.on('load', this.onLoad, this);
5534         this.store.on('loadexception', this.onLoadException, this);
5535         this.removeMask = false;
5536     }else{
5537         var um = this.el.getUpdateManager();
5538         um.showLoadIndicator = false; // disable the default indicator
5539         um.on('beforeupdate', this.onBeforeLoad, this);
5540         um.on('update', this.onLoad, this);
5541         um.on('failure', this.onLoad, this);
5542         this.removeMask = true;
5543     }
5544 };
5545
5546 Roo.LoadMask.prototype = {
5547     /**
5548      * @cfg {Boolean} removeMask
5549      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5550      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5551      */
5552     /**
5553      * @cfg {String} msg
5554      * The text to display in a centered loading message box (defaults to 'Loading...')
5555      */
5556     msg : 'Loading...',
5557     /**
5558      * @cfg {String} msgCls
5559      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5560      */
5561     msgCls : 'x-mask-loading',
5562
5563     /**
5564      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5565      * @type Boolean
5566      */
5567     disabled: false,
5568
5569     /**
5570      * Disables the mask to prevent it from being displayed
5571      */
5572     disable : function(){
5573        this.disabled = true;
5574     },
5575
5576     /**
5577      * Enables the mask so that it can be displayed
5578      */
5579     enable : function(){
5580         this.disabled = false;
5581     },
5582     
5583     onLoadException : function()
5584     {
5585         Roo.log(arguments);
5586         
5587         if (typeof(arguments[3]) != 'undefined') {
5588             Roo.MessageBox.alert("Error loading",arguments[3]);
5589         } 
5590         /*
5591         try {
5592             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5593                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5594             }   
5595         } catch(e) {
5596             
5597         }
5598         */
5599     
5600         
5601         
5602         this.el.unmask(this.removeMask);
5603     },
5604     // private
5605     onLoad : function()
5606     {
5607         this.el.unmask(this.removeMask);
5608     },
5609
5610     // private
5611     onBeforeLoad : function(){
5612         if(!this.disabled){
5613             this.el.mask(this.msg, this.msgCls);
5614         }
5615     },
5616
5617     // private
5618     destroy : function(){
5619         if(this.store){
5620             this.store.un('beforeload', this.onBeforeLoad, this);
5621             this.store.un('load', this.onLoad, this);
5622             this.store.un('loadexception', this.onLoadException, this);
5623         }else{
5624             var um = this.el.getUpdateManager();
5625             um.un('beforeupdate', this.onBeforeLoad, this);
5626             um.un('update', this.onLoad, this);
5627             um.un('failure', this.onLoad, this);
5628         }
5629     }
5630 };/*
5631  * - LGPL
5632  *
5633  * table
5634  * 
5635  */
5636
5637 /**
5638  * @class Roo.bootstrap.Table
5639  * @extends Roo.bootstrap.Component
5640  * Bootstrap Table class
5641  * @cfg {String} cls table class
5642  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5643  * @cfg {String} bgcolor Specifies the background color for a table
5644  * @cfg {Number} border Specifies whether the table cells should have borders or not
5645  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5646  * @cfg {Number} cellspacing Specifies the space between cells
5647  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5648  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5649  * @cfg {String} sortable Specifies that the table should be sortable
5650  * @cfg {String} summary Specifies a summary of the content of a table
5651  * @cfg {Number} width Specifies the width of a table
5652  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5653  * 
5654  * @cfg {boolean} striped Should the rows be alternative striped
5655  * @cfg {boolean} bordered Add borders to the table
5656  * @cfg {boolean} hover Add hover highlighting
5657  * @cfg {boolean} condensed Format condensed
5658  * @cfg {boolean} responsive Format condensed
5659  * @cfg {Boolean} loadMask (true|false) default false
5660  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5661  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5662  * @cfg {Boolean} rowSelection (true|false) default false
5663  * @cfg {Boolean} cellSelection (true|false) default false
5664  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5665  
5666  * 
5667  * @constructor
5668  * Create a new Table
5669  * @param {Object} config The config object
5670  */
5671
5672 Roo.bootstrap.Table = function(config){
5673     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5674     
5675     // BC...
5676     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5677     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5678     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5679     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5680     
5681     
5682     if (this.sm) {
5683         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5684         this.sm = this.selModel;
5685         this.sm.xmodule = this.xmodule || false;
5686     }
5687     if (this.cm && typeof(this.cm.config) == 'undefined') {
5688         this.colModel = new Roo.grid.ColumnModel(this.cm);
5689         this.cm = this.colModel;
5690         this.cm.xmodule = this.xmodule || false;
5691     }
5692     if (this.store) {
5693         this.store= Roo.factory(this.store, Roo.data);
5694         this.ds = this.store;
5695         this.ds.xmodule = this.xmodule || false;
5696          
5697     }
5698     if (this.footer && this.store) {
5699         this.footer.dataSource = this.ds;
5700         this.footer = Roo.factory(this.footer);
5701     }
5702     
5703     /** @private */
5704     this.addEvents({
5705         /**
5706          * @event cellclick
5707          * Fires when a cell is clicked
5708          * @param {Roo.bootstrap.Table} this
5709          * @param {Roo.Element} el
5710          * @param {Number} rowIndex
5711          * @param {Number} columnIndex
5712          * @param {Roo.EventObject} e
5713          */
5714         "cellclick" : true,
5715         /**
5716          * @event celldblclick
5717          * Fires when a cell is double clicked
5718          * @param {Roo.bootstrap.Table} this
5719          * @param {Roo.Element} el
5720          * @param {Number} rowIndex
5721          * @param {Number} columnIndex
5722          * @param {Roo.EventObject} e
5723          */
5724         "celldblclick" : true,
5725         /**
5726          * @event rowclick
5727          * Fires when a row is clicked
5728          * @param {Roo.bootstrap.Table} this
5729          * @param {Roo.Element} el
5730          * @param {Number} rowIndex
5731          * @param {Roo.EventObject} e
5732          */
5733         "rowclick" : true,
5734         /**
5735          * @event rowdblclick
5736          * Fires when a row is double clicked
5737          * @param {Roo.bootstrap.Table} this
5738          * @param {Roo.Element} el
5739          * @param {Number} rowIndex
5740          * @param {Roo.EventObject} e
5741          */
5742         "rowdblclick" : true,
5743         /**
5744          * @event mouseover
5745          * Fires when a mouseover occur
5746          * @param {Roo.bootstrap.Table} this
5747          * @param {Roo.Element} el
5748          * @param {Number} rowIndex
5749          * @param {Number} columnIndex
5750          * @param {Roo.EventObject} e
5751          */
5752         "mouseover" : true,
5753         /**
5754          * @event mouseout
5755          * Fires when a mouseout occur
5756          * @param {Roo.bootstrap.Table} this
5757          * @param {Roo.Element} el
5758          * @param {Number} rowIndex
5759          * @param {Number} columnIndex
5760          * @param {Roo.EventObject} e
5761          */
5762         "mouseout" : true,
5763         /**
5764          * @event rowclass
5765          * Fires when a row is rendered, so you can change add a style to it.
5766          * @param {Roo.bootstrap.Table} this
5767          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5768          */
5769         'rowclass' : true,
5770           /**
5771          * @event rowsrendered
5772          * Fires when all the  rows have been rendered
5773          * @param {Roo.bootstrap.Table} this
5774          */
5775         'rowsrendered' : true,
5776         /**
5777          * @event contextmenu
5778          * The raw contextmenu event for the entire grid.
5779          * @param {Roo.EventObject} e
5780          */
5781         "contextmenu" : true,
5782         /**
5783          * @event rowcontextmenu
5784          * Fires when a row is right clicked
5785          * @param {Roo.bootstrap.Table} this
5786          * @param {Number} rowIndex
5787          * @param {Roo.EventObject} e
5788          */
5789         "rowcontextmenu" : true,
5790         /**
5791          * @event cellcontextmenu
5792          * Fires when a cell is right clicked
5793          * @param {Roo.bootstrap.Table} this
5794          * @param {Number} rowIndex
5795          * @param {Number} cellIndex
5796          * @param {Roo.EventObject} e
5797          */
5798          "cellcontextmenu" : true,
5799          /**
5800          * @event headercontextmenu
5801          * Fires when a header is right clicked
5802          * @param {Roo.bootstrap.Table} this
5803          * @param {Number} columnIndex
5804          * @param {Roo.EventObject} e
5805          */
5806         "headercontextmenu" : true
5807     });
5808 };
5809
5810 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5811     
5812     cls: false,
5813     align: false,
5814     bgcolor: false,
5815     border: false,
5816     cellpadding: false,
5817     cellspacing: false,
5818     frame: false,
5819     rules: false,
5820     sortable: false,
5821     summary: false,
5822     width: false,
5823     striped : false,
5824     bordered: false,
5825     hover:  false,
5826     condensed : false,
5827     responsive : false,
5828     sm : false,
5829     cm : false,
5830     store : false,
5831     loadMask : false,
5832     footerShow : true,
5833     headerShow : true,
5834   
5835     rowSelection : false,
5836     cellSelection : false,
5837     layout : false,
5838     
5839     // Roo.Element - the tbody
5840     mainBody: false, 
5841     
5842     getAutoCreate : function(){
5843         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5844         
5845         cfg = {
5846             tag: 'table',
5847             cls : 'table',
5848             cn : []
5849         };
5850             
5851         if (this.striped) {
5852             cfg.cls += ' table-striped';
5853         }
5854         
5855         if (this.hover) {
5856             cfg.cls += ' table-hover';
5857         }
5858         if (this.bordered) {
5859             cfg.cls += ' table-bordered';
5860         }
5861         if (this.condensed) {
5862             cfg.cls += ' table-condensed';
5863         }
5864         if (this.responsive) {
5865             cfg.cls += ' table-responsive';
5866         }
5867         
5868         if (this.cls) {
5869             cfg.cls+=  ' ' +this.cls;
5870         }
5871         
5872         // this lot should be simplifed...
5873         
5874         if (this.align) {
5875             cfg.align=this.align;
5876         }
5877         if (this.bgcolor) {
5878             cfg.bgcolor=this.bgcolor;
5879         }
5880         if (this.border) {
5881             cfg.border=this.border;
5882         }
5883         if (this.cellpadding) {
5884             cfg.cellpadding=this.cellpadding;
5885         }
5886         if (this.cellspacing) {
5887             cfg.cellspacing=this.cellspacing;
5888         }
5889         if (this.frame) {
5890             cfg.frame=this.frame;
5891         }
5892         if (this.rules) {
5893             cfg.rules=this.rules;
5894         }
5895         if (this.sortable) {
5896             cfg.sortable=this.sortable;
5897         }
5898         if (this.summary) {
5899             cfg.summary=this.summary;
5900         }
5901         if (this.width) {
5902             cfg.width=this.width;
5903         }
5904         if (this.layout) {
5905             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5906         }
5907         
5908         if(this.store || this.cm){
5909             if(this.headerShow){
5910                 cfg.cn.push(this.renderHeader());
5911             }
5912             
5913             cfg.cn.push(this.renderBody());
5914             
5915             if(this.footerShow){
5916                 cfg.cn.push(this.renderFooter());
5917             }
5918             
5919             cfg.cls+=  ' TableGrid';
5920         }
5921         
5922         return { cn : [ cfg ] };
5923     },
5924     
5925     initEvents : function()
5926     {   
5927         if(!this.store || !this.cm){
5928             return;
5929         }
5930         
5931         //Roo.log('initEvents with ds!!!!');
5932         
5933         this.mainBody = this.el.select('tbody', true).first();
5934         
5935         
5936         var _this = this;
5937         
5938         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5939             e.on('click', _this.sort, _this);
5940         });
5941         
5942         this.el.on("click", this.onClick, this);
5943         this.el.on("dblclick", this.onDblClick, this);
5944         
5945         // why is this done????? = it breaks dialogs??
5946         //this.parent().el.setStyle('position', 'relative');
5947         
5948         
5949         if (this.footer) {
5950             this.footer.parentId = this.id;
5951             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5952         }
5953         
5954         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5955         
5956         this.store.on('load', this.onLoad, this);
5957         this.store.on('beforeload', this.onBeforeLoad, this);
5958         this.store.on('update', this.onUpdate, this);
5959         this.store.on('add', this.onAdd, this);
5960         
5961         this.el.on("contextmenu", this.onContextMenu, this);
5962         
5963     },
5964     
5965     onContextMenu : function(e, t)
5966     {
5967         this.processEvent("contextmenu", e);
5968     },
5969     
5970     processEvent : function(name, e)
5971     {
5972         if (name != 'touchstart' ) {
5973             this.fireEvent(name, e);    
5974         }
5975         
5976         var t = e.getTarget();
5977         
5978         var cell = Roo.get(t);
5979         
5980         if(!cell){
5981             return;
5982         }
5983         
5984         if(cell.findParent('tfoot', false, true)){
5985             return;
5986         }
5987         
5988         if(cell.findParent('thead', false, true)){
5989             
5990             if(e.getTarget().nodeName.toLowerCase() != 'th'){
5991                 cell = Roo.get(t).findParent('th', false, true);
5992             }
5993             
5994             var cellIndex = cell.dom.cellIndex;
5995             
5996             var ename = name == 'touchstart' ? 'click' : name;
5997             this.fireEvent("header" + ename, this, cellIndex, e);
5998             
5999             return;
6000         }
6001         
6002         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6003             cell = Roo.get(t).findParent('td', false, true);
6004         }
6005         
6006         var row = cell.findParent('tr', false, true);
6007         var cellIndex = cell.dom.cellIndex;
6008         var rowIndex = row.dom.rowIndex - 1;
6009         
6010         if(row !== false){
6011             
6012             this.fireEvent("row" + name, this, rowIndex, e);
6013             
6014             if(cell !== false){
6015             
6016                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6017             }
6018         }
6019         
6020     },
6021     
6022     onMouseover : function(e, el)
6023     {
6024         var cell = Roo.get(el);
6025         
6026         if(!cell){
6027             return;
6028         }
6029         
6030         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6031             cell = cell.findParent('td', false, true);
6032         }
6033         
6034         var row = cell.findParent('tr', false, true);
6035         var cellIndex = cell.dom.cellIndex;
6036         var rowIndex = row.dom.rowIndex - 1; // start from 0
6037         
6038         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6039         
6040     },
6041     
6042     onMouseout : function(e, el)
6043     {
6044         var cell = Roo.get(el);
6045         
6046         if(!cell){
6047             return;
6048         }
6049         
6050         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6051             cell = cell.findParent('td', false, true);
6052         }
6053         
6054         var row = cell.findParent('tr', false, true);
6055         var cellIndex = cell.dom.cellIndex;
6056         var rowIndex = row.dom.rowIndex - 1; // start from 0
6057         
6058         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6059         
6060     },
6061     
6062     onClick : function(e, el)
6063     {
6064         var cell = Roo.get(el);
6065         
6066         if(!cell || (!this.cellSelection && !this.rowSelection)){
6067             return;
6068         }
6069         
6070         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6071             cell = cell.findParent('td', false, true);
6072         }
6073         
6074         if(!cell || typeof(cell) == 'undefined'){
6075             return;
6076         }
6077         
6078         var row = cell.findParent('tr', false, true);
6079         
6080         if(!row || typeof(row) == 'undefined'){
6081             return;
6082         }
6083         
6084         var cellIndex = cell.dom.cellIndex;
6085         var rowIndex = this.getRowIndex(row);
6086         
6087         // why??? - should these not be based on SelectionModel?
6088         if(this.cellSelection){
6089             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6090         }
6091         
6092         if(this.rowSelection){
6093             this.fireEvent('rowclick', this, row, rowIndex, e);
6094         }
6095         
6096         
6097     },
6098     
6099     onDblClick : function(e,el)
6100     {
6101         var cell = Roo.get(el);
6102         
6103         if(!cell || (!this.CellSelection && !this.RowSelection)){
6104             return;
6105         }
6106         
6107         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6108             cell = cell.findParent('td', false, true);
6109         }
6110         
6111         if(!cell || typeof(cell) == 'undefined'){
6112             return;
6113         }
6114         
6115         var row = cell.findParent('tr', false, true);
6116         
6117         if(!row || typeof(row) == 'undefined'){
6118             return;
6119         }
6120         
6121         var cellIndex = cell.dom.cellIndex;
6122         var rowIndex = this.getRowIndex(row);
6123         
6124         if(this.CellSelection){
6125             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6126         }
6127         
6128         if(this.RowSelection){
6129             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6130         }
6131     },
6132     
6133     sort : function(e,el)
6134     {
6135         var col = Roo.get(el);
6136         
6137         if(!col.hasClass('sortable')){
6138             return;
6139         }
6140         
6141         var sort = col.attr('sort');
6142         var dir = 'ASC';
6143         
6144         if(col.hasClass('glyphicon-arrow-up')){
6145             dir = 'DESC';
6146         }
6147         
6148         this.store.sortInfo = {field : sort, direction : dir};
6149         
6150         if (this.footer) {
6151             Roo.log("calling footer first");
6152             this.footer.onClick('first');
6153         } else {
6154         
6155             this.store.load({ params : { start : 0 } });
6156         }
6157     },
6158     
6159     renderHeader : function()
6160     {
6161         var header = {
6162             tag: 'thead',
6163             cn : []
6164         };
6165         
6166         var cm = this.cm;
6167         
6168         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6169             
6170             var config = cm.config[i];
6171             
6172             var c = {
6173                 tag: 'th',
6174                 style : '',
6175                 html: cm.getColumnHeader(i)
6176             };
6177             
6178             var hh = '';
6179             
6180             if(typeof(config.lgHeader) != 'undefined'){
6181                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6182             }
6183             
6184             if(typeof(config.mdHeader) != 'undefined'){
6185                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6186             }
6187             
6188             if(typeof(config.smHeader) != 'undefined'){
6189                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6190             }
6191             
6192             if(typeof(config.xsHeader) != 'undefined'){
6193                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6194             }
6195             
6196             if(hh.length){
6197                 c.html = hh;
6198             }
6199             
6200             if(typeof(config.tooltip) != 'undefined'){
6201                 c.tooltip = config.tooltip;
6202             }
6203             
6204             if(typeof(config.colspan) != 'undefined'){
6205                 c.colspan = config.colspan;
6206             }
6207             
6208             if(typeof(config.hidden) != 'undefined' && config.hidden){
6209                 c.style += ' display:none;';
6210             }
6211             
6212             if(typeof(config.dataIndex) != 'undefined'){
6213                 c.sort = config.dataIndex;
6214             }
6215             
6216             if(typeof(config.sortable) != 'undefined' && config.sortable){
6217                 c.cls = 'sortable';
6218             }
6219             
6220             if(typeof(config.align) != 'undefined' && config.align.length){
6221                 c.style += ' text-align:' + config.align + ';';
6222             }
6223             
6224             if(typeof(config.width) != 'undefined'){
6225                 c.style += ' width:' + config.width + 'px;';
6226             }
6227             
6228             if(typeof(config.cls) != 'undefined'){
6229                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6230             }
6231             
6232             ['xs','sm','md','lg'].map(function(size){
6233                 
6234                 if(typeof(config[size]) == 'undefined'){
6235                     return;
6236                 }
6237                 
6238                 if (!config[size]) { // 0 = hidden
6239                     c.cls += ' hidden-' + size;
6240                     return;
6241                 }
6242                 
6243                 c.cls += ' col-' + size + '-' + config[size];
6244
6245             });
6246             
6247             header.cn.push(c)
6248         }
6249         
6250         return header;
6251     },
6252     
6253     renderBody : function()
6254     {
6255         var body = {
6256             tag: 'tbody',
6257             cn : [
6258                 {
6259                     tag: 'tr',
6260                     cn : [
6261                         {
6262                             tag : 'td',
6263                             colspan :  this.cm.getColumnCount()
6264                         }
6265                     ]
6266                 }
6267             ]
6268         };
6269         
6270         return body;
6271     },
6272     
6273     renderFooter : function()
6274     {
6275         var footer = {
6276             tag: 'tfoot',
6277             cn : [
6278                 {
6279                     tag: 'tr',
6280                     cn : [
6281                         {
6282                             tag : 'td',
6283                             colspan :  this.cm.getColumnCount()
6284                         }
6285                     ]
6286                 }
6287             ]
6288         };
6289         
6290         return footer;
6291     },
6292     
6293     
6294     
6295     onLoad : function()
6296     {
6297 //        Roo.log('ds onload');
6298         this.clear();
6299         
6300         var _this = this;
6301         var cm = this.cm;
6302         var ds = this.store;
6303         
6304         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6305             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6306             
6307             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6308                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6309             }
6310             
6311             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6312                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6313             }
6314         });
6315         
6316         var tbody =  this.mainBody;
6317               
6318         if(ds.getCount() > 0){
6319             ds.data.each(function(d,rowIndex){
6320                 var row =  this.renderRow(cm, ds, rowIndex);
6321                 
6322                 tbody.createChild(row);
6323                 
6324                 var _this = this;
6325                 
6326                 if(row.cellObjects.length){
6327                     Roo.each(row.cellObjects, function(r){
6328                         _this.renderCellObject(r);
6329                     })
6330                 }
6331                 
6332             }, this);
6333         }
6334         
6335         Roo.each(this.el.select('tbody td', true).elements, function(e){
6336             e.on('mouseover', _this.onMouseover, _this);
6337         });
6338         
6339         Roo.each(this.el.select('tbody td', true).elements, function(e){
6340             e.on('mouseout', _this.onMouseout, _this);
6341         });
6342         this.fireEvent('rowsrendered', this);
6343         //if(this.loadMask){
6344         //    this.maskEl.hide();
6345         //}
6346     },
6347     
6348     
6349     onUpdate : function(ds,record)
6350     {
6351         this.refreshRow(record);
6352     },
6353     
6354     onRemove : function(ds, record, index, isUpdate){
6355         if(isUpdate !== true){
6356             this.fireEvent("beforerowremoved", this, index, record);
6357         }
6358         var bt = this.mainBody.dom;
6359         
6360         var rows = this.el.select('tbody > tr', true).elements;
6361         
6362         if(typeof(rows[index]) != 'undefined'){
6363             bt.removeChild(rows[index].dom);
6364         }
6365         
6366 //        if(bt.rows[index]){
6367 //            bt.removeChild(bt.rows[index]);
6368 //        }
6369         
6370         if(isUpdate !== true){
6371             //this.stripeRows(index);
6372             //this.syncRowHeights(index, index);
6373             //this.layout();
6374             this.fireEvent("rowremoved", this, index, record);
6375         }
6376     },
6377     
6378     onAdd : function(ds, records, rowIndex)
6379     {
6380         //Roo.log('on Add called');
6381         // - note this does not handle multiple adding very well..
6382         var bt = this.mainBody.dom;
6383         for (var i =0 ; i < records.length;i++) {
6384             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6385             //Roo.log(records[i]);
6386             //Roo.log(this.store.getAt(rowIndex+i));
6387             this.insertRow(this.store, rowIndex + i, false);
6388             return;
6389         }
6390         
6391     },
6392     
6393     
6394     refreshRow : function(record){
6395         var ds = this.store, index;
6396         if(typeof record == 'number'){
6397             index = record;
6398             record = ds.getAt(index);
6399         }else{
6400             index = ds.indexOf(record);
6401         }
6402         this.insertRow(ds, index, true);
6403         this.onRemove(ds, record, index+1, true);
6404         //this.syncRowHeights(index, index);
6405         //this.layout();
6406         this.fireEvent("rowupdated", this, index, record);
6407     },
6408     
6409     insertRow : function(dm, rowIndex, isUpdate){
6410         
6411         if(!isUpdate){
6412             this.fireEvent("beforerowsinserted", this, rowIndex);
6413         }
6414             //var s = this.getScrollState();
6415         var row = this.renderRow(this.cm, this.store, rowIndex);
6416         // insert before rowIndex..
6417         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6418         
6419         var _this = this;
6420                 
6421         if(row.cellObjects.length){
6422             Roo.each(row.cellObjects, function(r){
6423                 _this.renderCellObject(r);
6424             })
6425         }
6426             
6427         if(!isUpdate){
6428             this.fireEvent("rowsinserted", this, rowIndex);
6429             //this.syncRowHeights(firstRow, lastRow);
6430             //this.stripeRows(firstRow);
6431             //this.layout();
6432         }
6433         
6434     },
6435     
6436     
6437     getRowDom : function(rowIndex)
6438     {
6439         var rows = this.el.select('tbody > tr', true).elements;
6440         
6441         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6442         
6443     },
6444     // returns the object tree for a tr..
6445   
6446     
6447     renderRow : function(cm, ds, rowIndex) 
6448     {
6449         
6450         var d = ds.getAt(rowIndex);
6451         
6452         var row = {
6453             tag : 'tr',
6454             cn : []
6455         };
6456             
6457         var cellObjects = [];
6458         
6459         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6460             var config = cm.config[i];
6461             
6462             var renderer = cm.getRenderer(i);
6463             var value = '';
6464             var id = false;
6465             
6466             if(typeof(renderer) !== 'undefined'){
6467                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6468             }
6469             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6470             // and are rendered into the cells after the row is rendered - using the id for the element.
6471             
6472             if(typeof(value) === 'object'){
6473                 id = Roo.id();
6474                 cellObjects.push({
6475                     container : id,
6476                     cfg : value 
6477                 })
6478             }
6479             
6480             var rowcfg = {
6481                 record: d,
6482                 rowIndex : rowIndex,
6483                 colIndex : i,
6484                 rowClass : ''
6485             };
6486
6487             this.fireEvent('rowclass', this, rowcfg);
6488             
6489             var td = {
6490                 tag: 'td',
6491                 cls : rowcfg.rowClass,
6492                 style: '',
6493                 html: (typeof(value) === 'object') ? '' : value
6494             };
6495             
6496             if (id) {
6497                 td.id = id;
6498             }
6499             
6500             if(typeof(config.colspan) != 'undefined'){
6501                 td.colspan = config.colspan;
6502             }
6503             
6504             if(typeof(config.hidden) != 'undefined' && config.hidden){
6505                 td.style += ' display:none;';
6506             }
6507             
6508             if(typeof(config.align) != 'undefined' && config.align.length){
6509                 td.style += ' text-align:' + config.align + ';';
6510             }
6511             
6512             if(typeof(config.width) != 'undefined'){
6513                 td.style += ' width:' +  config.width + 'px;';
6514             }
6515             
6516             if(typeof(config.cursor) != 'undefined'){
6517                 td.style += ' cursor:' +  config.cursor + ';';
6518             }
6519             
6520             if(typeof(config.cls) != 'undefined'){
6521                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6522             }
6523             
6524             ['xs','sm','md','lg'].map(function(size){
6525                 
6526                 if(typeof(config[size]) == 'undefined'){
6527                     return;
6528                 }
6529                 
6530                 if (!config[size]) { // 0 = hidden
6531                     td.cls += ' hidden-' + size;
6532                     return;
6533                 }
6534                 
6535                 td.cls += ' col-' + size + '-' + config[size];
6536
6537             });
6538              
6539             row.cn.push(td);
6540            
6541         }
6542         
6543         row.cellObjects = cellObjects;
6544         
6545         return row;
6546           
6547     },
6548     
6549     
6550     
6551     onBeforeLoad : function()
6552     {
6553         //Roo.log('ds onBeforeLoad');
6554         
6555         //this.clear();
6556         
6557         //if(this.loadMask){
6558         //    this.maskEl.show();
6559         //}
6560     },
6561      /**
6562      * Remove all rows
6563      */
6564     clear : function()
6565     {
6566         this.el.select('tbody', true).first().dom.innerHTML = '';
6567     },
6568     /**
6569      * Show or hide a row.
6570      * @param {Number} rowIndex to show or hide
6571      * @param {Boolean} state hide
6572      */
6573     setRowVisibility : function(rowIndex, state)
6574     {
6575         var bt = this.mainBody.dom;
6576         
6577         var rows = this.el.select('tbody > tr', true).elements;
6578         
6579         if(typeof(rows[rowIndex]) == 'undefined'){
6580             return;
6581         }
6582         rows[rowIndex].dom.style.display = state ? '' : 'none';
6583     },
6584     
6585     
6586     getSelectionModel : function(){
6587         if(!this.selModel){
6588             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6589         }
6590         return this.selModel;
6591     },
6592     /*
6593      * Render the Roo.bootstrap object from renderder
6594      */
6595     renderCellObject : function(r)
6596     {
6597         var _this = this;
6598         
6599         var t = r.cfg.render(r.container);
6600         
6601         if(r.cfg.cn){
6602             Roo.each(r.cfg.cn, function(c){
6603                 var child = {
6604                     container: t.getChildContainer(),
6605                     cfg: c
6606                 };
6607                 _this.renderCellObject(child);
6608             })
6609         }
6610     },
6611     
6612     getRowIndex : function(row)
6613     {
6614         var rowIndex = -1;
6615         
6616         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6617             if(el != row){
6618                 return;
6619             }
6620             
6621             rowIndex = index;
6622         });
6623         
6624         return rowIndex;
6625     }
6626    
6627 });
6628
6629  
6630
6631  /*
6632  * - LGPL
6633  *
6634  * table cell
6635  * 
6636  */
6637
6638 /**
6639  * @class Roo.bootstrap.TableCell
6640  * @extends Roo.bootstrap.Component
6641  * Bootstrap TableCell class
6642  * @cfg {String} html cell contain text
6643  * @cfg {String} cls cell class
6644  * @cfg {String} tag cell tag (td|th) default td
6645  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6646  * @cfg {String} align Aligns the content in a cell
6647  * @cfg {String} axis Categorizes cells
6648  * @cfg {String} bgcolor Specifies the background color of a cell
6649  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6650  * @cfg {Number} colspan Specifies the number of columns a cell should span
6651  * @cfg {String} headers Specifies one or more header cells a cell is related to
6652  * @cfg {Number} height Sets the height of a cell
6653  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6654  * @cfg {Number} rowspan Sets the number of rows a cell should span
6655  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6656  * @cfg {String} valign Vertical aligns the content in a cell
6657  * @cfg {Number} width Specifies the width of a cell
6658  * 
6659  * @constructor
6660  * Create a new TableCell
6661  * @param {Object} config The config object
6662  */
6663
6664 Roo.bootstrap.TableCell = function(config){
6665     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6666 };
6667
6668 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6669     
6670     html: false,
6671     cls: false,
6672     tag: false,
6673     abbr: false,
6674     align: false,
6675     axis: false,
6676     bgcolor: false,
6677     charoff: false,
6678     colspan: false,
6679     headers: false,
6680     height: false,
6681     nowrap: false,
6682     rowspan: false,
6683     scope: false,
6684     valign: false,
6685     width: false,
6686     
6687     
6688     getAutoCreate : function(){
6689         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6690         
6691         cfg = {
6692             tag: 'td'
6693         };
6694         
6695         if(this.tag){
6696             cfg.tag = this.tag;
6697         }
6698         
6699         if (this.html) {
6700             cfg.html=this.html
6701         }
6702         if (this.cls) {
6703             cfg.cls=this.cls
6704         }
6705         if (this.abbr) {
6706             cfg.abbr=this.abbr
6707         }
6708         if (this.align) {
6709             cfg.align=this.align
6710         }
6711         if (this.axis) {
6712             cfg.axis=this.axis
6713         }
6714         if (this.bgcolor) {
6715             cfg.bgcolor=this.bgcolor
6716         }
6717         if (this.charoff) {
6718             cfg.charoff=this.charoff
6719         }
6720         if (this.colspan) {
6721             cfg.colspan=this.colspan
6722         }
6723         if (this.headers) {
6724             cfg.headers=this.headers
6725         }
6726         if (this.height) {
6727             cfg.height=this.height
6728         }
6729         if (this.nowrap) {
6730             cfg.nowrap=this.nowrap
6731         }
6732         if (this.rowspan) {
6733             cfg.rowspan=this.rowspan
6734         }
6735         if (this.scope) {
6736             cfg.scope=this.scope
6737         }
6738         if (this.valign) {
6739             cfg.valign=this.valign
6740         }
6741         if (this.width) {
6742             cfg.width=this.width
6743         }
6744         
6745         
6746         return cfg;
6747     }
6748    
6749 });
6750
6751  
6752
6753  /*
6754  * - LGPL
6755  *
6756  * table row
6757  * 
6758  */
6759
6760 /**
6761  * @class Roo.bootstrap.TableRow
6762  * @extends Roo.bootstrap.Component
6763  * Bootstrap TableRow class
6764  * @cfg {String} cls row class
6765  * @cfg {String} align Aligns the content in a table row
6766  * @cfg {String} bgcolor Specifies a background color for a table row
6767  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6768  * @cfg {String} valign Vertical aligns the content in a table row
6769  * 
6770  * @constructor
6771  * Create a new TableRow
6772  * @param {Object} config The config object
6773  */
6774
6775 Roo.bootstrap.TableRow = function(config){
6776     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6777 };
6778
6779 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6780     
6781     cls: false,
6782     align: false,
6783     bgcolor: false,
6784     charoff: false,
6785     valign: false,
6786     
6787     getAutoCreate : function(){
6788         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6789         
6790         cfg = {
6791             tag: 'tr'
6792         };
6793             
6794         if(this.cls){
6795             cfg.cls = this.cls;
6796         }
6797         if(this.align){
6798             cfg.align = this.align;
6799         }
6800         if(this.bgcolor){
6801             cfg.bgcolor = this.bgcolor;
6802         }
6803         if(this.charoff){
6804             cfg.charoff = this.charoff;
6805         }
6806         if(this.valign){
6807             cfg.valign = this.valign;
6808         }
6809         
6810         return cfg;
6811     }
6812    
6813 });
6814
6815  
6816
6817  /*
6818  * - LGPL
6819  *
6820  * table body
6821  * 
6822  */
6823
6824 /**
6825  * @class Roo.bootstrap.TableBody
6826  * @extends Roo.bootstrap.Component
6827  * Bootstrap TableBody class
6828  * @cfg {String} cls element class
6829  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6830  * @cfg {String} align Aligns the content inside the element
6831  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6832  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6833  * 
6834  * @constructor
6835  * Create a new TableBody
6836  * @param {Object} config The config object
6837  */
6838
6839 Roo.bootstrap.TableBody = function(config){
6840     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6841 };
6842
6843 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6844     
6845     cls: false,
6846     tag: false,
6847     align: false,
6848     charoff: false,
6849     valign: false,
6850     
6851     getAutoCreate : function(){
6852         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6853         
6854         cfg = {
6855             tag: 'tbody'
6856         };
6857             
6858         if (this.cls) {
6859             cfg.cls=this.cls
6860         }
6861         if(this.tag){
6862             cfg.tag = this.tag;
6863         }
6864         
6865         if(this.align){
6866             cfg.align = this.align;
6867         }
6868         if(this.charoff){
6869             cfg.charoff = this.charoff;
6870         }
6871         if(this.valign){
6872             cfg.valign = this.valign;
6873         }
6874         
6875         return cfg;
6876     }
6877     
6878     
6879 //    initEvents : function()
6880 //    {
6881 //        
6882 //        if(!this.store){
6883 //            return;
6884 //        }
6885 //        
6886 //        this.store = Roo.factory(this.store, Roo.data);
6887 //        this.store.on('load', this.onLoad, this);
6888 //        
6889 //        this.store.load();
6890 //        
6891 //    },
6892 //    
6893 //    onLoad: function () 
6894 //    {   
6895 //        this.fireEvent('load', this);
6896 //    }
6897 //    
6898 //   
6899 });
6900
6901  
6902
6903  /*
6904  * Based on:
6905  * Ext JS Library 1.1.1
6906  * Copyright(c) 2006-2007, Ext JS, LLC.
6907  *
6908  * Originally Released Under LGPL - original licence link has changed is not relivant.
6909  *
6910  * Fork - LGPL
6911  * <script type="text/javascript">
6912  */
6913
6914 // as we use this in bootstrap.
6915 Roo.namespace('Roo.form');
6916  /**
6917  * @class Roo.form.Action
6918  * Internal Class used to handle form actions
6919  * @constructor
6920  * @param {Roo.form.BasicForm} el The form element or its id
6921  * @param {Object} config Configuration options
6922  */
6923
6924  
6925  
6926 // define the action interface
6927 Roo.form.Action = function(form, options){
6928     this.form = form;
6929     this.options = options || {};
6930 };
6931 /**
6932  * Client Validation Failed
6933  * @const 
6934  */
6935 Roo.form.Action.CLIENT_INVALID = 'client';
6936 /**
6937  * Server Validation Failed
6938  * @const 
6939  */
6940 Roo.form.Action.SERVER_INVALID = 'server';
6941  /**
6942  * Connect to Server Failed
6943  * @const 
6944  */
6945 Roo.form.Action.CONNECT_FAILURE = 'connect';
6946 /**
6947  * Reading Data from Server Failed
6948  * @const 
6949  */
6950 Roo.form.Action.LOAD_FAILURE = 'load';
6951
6952 Roo.form.Action.prototype = {
6953     type : 'default',
6954     failureType : undefined,
6955     response : undefined,
6956     result : undefined,
6957
6958     // interface method
6959     run : function(options){
6960
6961     },
6962
6963     // interface method
6964     success : function(response){
6965
6966     },
6967
6968     // interface method
6969     handleResponse : function(response){
6970
6971     },
6972
6973     // default connection failure
6974     failure : function(response){
6975         
6976         this.response = response;
6977         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6978         this.form.afterAction(this, false);
6979     },
6980
6981     processResponse : function(response){
6982         this.response = response;
6983         if(!response.responseText){
6984             return true;
6985         }
6986         this.result = this.handleResponse(response);
6987         return this.result;
6988     },
6989
6990     // utility functions used internally
6991     getUrl : function(appendParams){
6992         var url = this.options.url || this.form.url || this.form.el.dom.action;
6993         if(appendParams){
6994             var p = this.getParams();
6995             if(p){
6996                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6997             }
6998         }
6999         return url;
7000     },
7001
7002     getMethod : function(){
7003         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7004     },
7005
7006     getParams : function(){
7007         var bp = this.form.baseParams;
7008         var p = this.options.params;
7009         if(p){
7010             if(typeof p == "object"){
7011                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7012             }else if(typeof p == 'string' && bp){
7013                 p += '&' + Roo.urlEncode(bp);
7014             }
7015         }else if(bp){
7016             p = Roo.urlEncode(bp);
7017         }
7018         return p;
7019     },
7020
7021     createCallback : function(){
7022         return {
7023             success: this.success,
7024             failure: this.failure,
7025             scope: this,
7026             timeout: (this.form.timeout*1000),
7027             upload: this.form.fileUpload ? this.success : undefined
7028         };
7029     }
7030 };
7031
7032 Roo.form.Action.Submit = function(form, options){
7033     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7034 };
7035
7036 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7037     type : 'submit',
7038
7039     haveProgress : false,
7040     uploadComplete : false,
7041     
7042     // uploadProgress indicator.
7043     uploadProgress : function()
7044     {
7045         if (!this.form.progressUrl) {
7046             return;
7047         }
7048         
7049         if (!this.haveProgress) {
7050             Roo.MessageBox.progress("Uploading", "Uploading");
7051         }
7052         if (this.uploadComplete) {
7053            Roo.MessageBox.hide();
7054            return;
7055         }
7056         
7057         this.haveProgress = true;
7058    
7059         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7060         
7061         var c = new Roo.data.Connection();
7062         c.request({
7063             url : this.form.progressUrl,
7064             params: {
7065                 id : uid
7066             },
7067             method: 'GET',
7068             success : function(req){
7069                //console.log(data);
7070                 var rdata = false;
7071                 var edata;
7072                 try  {
7073                    rdata = Roo.decode(req.responseText)
7074                 } catch (e) {
7075                     Roo.log("Invalid data from server..");
7076                     Roo.log(edata);
7077                     return;
7078                 }
7079                 if (!rdata || !rdata.success) {
7080                     Roo.log(rdata);
7081                     Roo.MessageBox.alert(Roo.encode(rdata));
7082                     return;
7083                 }
7084                 var data = rdata.data;
7085                 
7086                 if (this.uploadComplete) {
7087                    Roo.MessageBox.hide();
7088                    return;
7089                 }
7090                    
7091                 if (data){
7092                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7093                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7094                     );
7095                 }
7096                 this.uploadProgress.defer(2000,this);
7097             },
7098        
7099             failure: function(data) {
7100                 Roo.log('progress url failed ');
7101                 Roo.log(data);
7102             },
7103             scope : this
7104         });
7105            
7106     },
7107     
7108     
7109     run : function()
7110     {
7111         // run get Values on the form, so it syncs any secondary forms.
7112         this.form.getValues();
7113         
7114         var o = this.options;
7115         var method = this.getMethod();
7116         var isPost = method == 'POST';
7117         if(o.clientValidation === false || this.form.isValid()){
7118             
7119             if (this.form.progressUrl) {
7120                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7121                     (new Date() * 1) + '' + Math.random());
7122                     
7123             } 
7124             
7125             
7126             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7127                 form:this.form.el.dom,
7128                 url:this.getUrl(!isPost),
7129                 method: method,
7130                 params:isPost ? this.getParams() : null,
7131                 isUpload: this.form.fileUpload
7132             }));
7133             
7134             this.uploadProgress();
7135
7136         }else if (o.clientValidation !== false){ // client validation failed
7137             this.failureType = Roo.form.Action.CLIENT_INVALID;
7138             this.form.afterAction(this, false);
7139         }
7140     },
7141
7142     success : function(response)
7143     {
7144         this.uploadComplete= true;
7145         if (this.haveProgress) {
7146             Roo.MessageBox.hide();
7147         }
7148         
7149         
7150         var result = this.processResponse(response);
7151         if(result === true || result.success){
7152             this.form.afterAction(this, true);
7153             return;
7154         }
7155         if(result.errors){
7156             this.form.markInvalid(result.errors);
7157             this.failureType = Roo.form.Action.SERVER_INVALID;
7158         }
7159         this.form.afterAction(this, false);
7160     },
7161     failure : function(response)
7162     {
7163         this.uploadComplete= true;
7164         if (this.haveProgress) {
7165             Roo.MessageBox.hide();
7166         }
7167         
7168         this.response = response;
7169         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7170         this.form.afterAction(this, false);
7171     },
7172     
7173     handleResponse : function(response){
7174         if(this.form.errorReader){
7175             var rs = this.form.errorReader.read(response);
7176             var errors = [];
7177             if(rs.records){
7178                 for(var i = 0, len = rs.records.length; i < len; i++) {
7179                     var r = rs.records[i];
7180                     errors[i] = r.data;
7181                 }
7182             }
7183             if(errors.length < 1){
7184                 errors = null;
7185             }
7186             return {
7187                 success : rs.success,
7188                 errors : errors
7189             };
7190         }
7191         var ret = false;
7192         try {
7193             ret = Roo.decode(response.responseText);
7194         } catch (e) {
7195             ret = {
7196                 success: false,
7197                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7198                 errors : []
7199             };
7200         }
7201         return ret;
7202         
7203     }
7204 });
7205
7206
7207 Roo.form.Action.Load = function(form, options){
7208     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7209     this.reader = this.form.reader;
7210 };
7211
7212 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7213     type : 'load',
7214
7215     run : function(){
7216         
7217         Roo.Ajax.request(Roo.apply(
7218                 this.createCallback(), {
7219                     method:this.getMethod(),
7220                     url:this.getUrl(false),
7221                     params:this.getParams()
7222         }));
7223     },
7224
7225     success : function(response){
7226         
7227         var result = this.processResponse(response);
7228         if(result === true || !result.success || !result.data){
7229             this.failureType = Roo.form.Action.LOAD_FAILURE;
7230             this.form.afterAction(this, false);
7231             return;
7232         }
7233         this.form.clearInvalid();
7234         this.form.setValues(result.data);
7235         this.form.afterAction(this, true);
7236     },
7237
7238     handleResponse : function(response){
7239         if(this.form.reader){
7240             var rs = this.form.reader.read(response);
7241             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7242             return {
7243                 success : rs.success,
7244                 data : data
7245             };
7246         }
7247         return Roo.decode(response.responseText);
7248     }
7249 });
7250
7251 Roo.form.Action.ACTION_TYPES = {
7252     'load' : Roo.form.Action.Load,
7253     'submit' : Roo.form.Action.Submit
7254 };/*
7255  * - LGPL
7256  *
7257  * form
7258  * 
7259  */
7260
7261 /**
7262  * @class Roo.bootstrap.Form
7263  * @extends Roo.bootstrap.Component
7264  * Bootstrap Form class
7265  * @cfg {String} method  GET | POST (default POST)
7266  * @cfg {String} labelAlign top | left (default top)
7267  * @cfg {String} align left  | right - for navbars
7268  * @cfg {Boolean} loadMask load mask when submit (default true)
7269
7270  * 
7271  * @constructor
7272  * Create a new Form
7273  * @param {Object} config The config object
7274  */
7275
7276
7277 Roo.bootstrap.Form = function(config){
7278     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7279     this.addEvents({
7280         /**
7281          * @event clientvalidation
7282          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7283          * @param {Form} this
7284          * @param {Boolean} valid true if the form has passed client-side validation
7285          */
7286         clientvalidation: true,
7287         /**
7288          * @event beforeaction
7289          * Fires before any action is performed. Return false to cancel the action.
7290          * @param {Form} this
7291          * @param {Action} action The action to be performed
7292          */
7293         beforeaction: true,
7294         /**
7295          * @event actionfailed
7296          * Fires when an action fails.
7297          * @param {Form} this
7298          * @param {Action} action The action that failed
7299          */
7300         actionfailed : true,
7301         /**
7302          * @event actioncomplete
7303          * Fires when an action is completed.
7304          * @param {Form} this
7305          * @param {Action} action The action that completed
7306          */
7307         actioncomplete : true
7308     });
7309     
7310 };
7311
7312 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7313       
7314      /**
7315      * @cfg {String} method
7316      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7317      */
7318     method : 'POST',
7319     /**
7320      * @cfg {String} url
7321      * The URL to use for form actions if one isn't supplied in the action options.
7322      */
7323     /**
7324      * @cfg {Boolean} fileUpload
7325      * Set to true if this form is a file upload.
7326      */
7327      
7328     /**
7329      * @cfg {Object} baseParams
7330      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7331      */
7332       
7333     /**
7334      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7335      */
7336     timeout: 30,
7337     /**
7338      * @cfg {Sting} align (left|right) for navbar forms
7339      */
7340     align : 'left',
7341
7342     // private
7343     activeAction : null,
7344  
7345     /**
7346      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7347      * element by passing it or its id or mask the form itself by passing in true.
7348      * @type Mixed
7349      */
7350     waitMsgTarget : false,
7351     
7352     loadMask : true,
7353     
7354     getAutoCreate : function(){
7355         
7356         var cfg = {
7357             tag: 'form',
7358             method : this.method || 'POST',
7359             id : this.id || Roo.id(),
7360             cls : ''
7361         };
7362         if (this.parent().xtype.match(/^Nav/)) {
7363             cfg.cls = 'navbar-form navbar-' + this.align;
7364             
7365         }
7366         
7367         if (this.labelAlign == 'left' ) {
7368             cfg.cls += ' form-horizontal';
7369         }
7370         
7371         
7372         return cfg;
7373     },
7374     initEvents : function()
7375     {
7376         this.el.on('submit', this.onSubmit, this);
7377         // this was added as random key presses on the form where triggering form submit.
7378         this.el.on('keypress', function(e) {
7379             if (e.getCharCode() != 13) {
7380                 return true;
7381             }
7382             // we might need to allow it for textareas.. and some other items.
7383             // check e.getTarget().
7384             
7385             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7386                 return true;
7387             }
7388         
7389             Roo.log("keypress blocked");
7390             
7391             e.preventDefault();
7392             return false;
7393         });
7394         
7395     },
7396     // private
7397     onSubmit : function(e){
7398         e.stopEvent();
7399     },
7400     
7401      /**
7402      * Returns true if client-side validation on the form is successful.
7403      * @return Boolean
7404      */
7405     isValid : function(){
7406         var items = this.getItems();
7407         var valid = true;
7408         items.each(function(f){
7409            if(!f.validate()){
7410                valid = false;
7411                
7412            }
7413         });
7414         return valid;
7415     },
7416     /**
7417      * Returns true if any fields in this form have changed since their original load.
7418      * @return Boolean
7419      */
7420     isDirty : function(){
7421         var dirty = false;
7422         var items = this.getItems();
7423         items.each(function(f){
7424            if(f.isDirty()){
7425                dirty = true;
7426                return false;
7427            }
7428            return true;
7429         });
7430         return dirty;
7431     },
7432      /**
7433      * Performs a predefined action (submit or load) or custom actions you define on this form.
7434      * @param {String} actionName The name of the action type
7435      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7436      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7437      * accept other config options):
7438      * <pre>
7439 Property          Type             Description
7440 ----------------  ---------------  ----------------------------------------------------------------------------------
7441 url               String           The url for the action (defaults to the form's url)
7442 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7443 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7444 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7445                                    validate the form on the client (defaults to false)
7446      * </pre>
7447      * @return {BasicForm} this
7448      */
7449     doAction : function(action, options){
7450         if(typeof action == 'string'){
7451             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7452         }
7453         if(this.fireEvent('beforeaction', this, action) !== false){
7454             this.beforeAction(action);
7455             action.run.defer(100, action);
7456         }
7457         return this;
7458     },
7459     
7460     // private
7461     beforeAction : function(action){
7462         var o = action.options;
7463         
7464         if(this.loadMask){
7465             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7466         }
7467         // not really supported yet.. ??
7468         
7469         //if(this.waitMsgTarget === true){
7470         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7471         //}else if(this.waitMsgTarget){
7472         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7473         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7474         //}else {
7475         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7476        // }
7477          
7478     },
7479
7480     // private
7481     afterAction : function(action, success){
7482         this.activeAction = null;
7483         var o = action.options;
7484         
7485         //if(this.waitMsgTarget === true){
7486             this.el.unmask();
7487         //}else if(this.waitMsgTarget){
7488         //    this.waitMsgTarget.unmask();
7489         //}else{
7490         //    Roo.MessageBox.updateProgress(1);
7491         //    Roo.MessageBox.hide();
7492        // }
7493         // 
7494         if(success){
7495             if(o.reset){
7496                 this.reset();
7497             }
7498             Roo.callback(o.success, o.scope, [this, action]);
7499             this.fireEvent('actioncomplete', this, action);
7500             
7501         }else{
7502             
7503             // failure condition..
7504             // we have a scenario where updates need confirming.
7505             // eg. if a locking scenario exists..
7506             // we look for { errors : { needs_confirm : true }} in the response.
7507             if (
7508                 (typeof(action.result) != 'undefined')  &&
7509                 (typeof(action.result.errors) != 'undefined')  &&
7510                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7511            ){
7512                 var _t = this;
7513                 Roo.log("not supported yet");
7514                  /*
7515                 
7516                 Roo.MessageBox.confirm(
7517                     "Change requires confirmation",
7518                     action.result.errorMsg,
7519                     function(r) {
7520                         if (r != 'yes') {
7521                             return;
7522                         }
7523                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7524                     }
7525                     
7526                 );
7527                 */
7528                 
7529                 
7530                 return;
7531             }
7532             
7533             Roo.callback(o.failure, o.scope, [this, action]);
7534             // show an error message if no failed handler is set..
7535             if (!this.hasListener('actionfailed')) {
7536                 Roo.log("need to add dialog support");
7537                 /*
7538                 Roo.MessageBox.alert("Error",
7539                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7540                         action.result.errorMsg :
7541                         "Saving Failed, please check your entries or try again"
7542                 );
7543                 */
7544             }
7545             
7546             this.fireEvent('actionfailed', this, action);
7547         }
7548         
7549     },
7550     /**
7551      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7552      * @param {String} id The value to search for
7553      * @return Field
7554      */
7555     findField : function(id){
7556         var items = this.getItems();
7557         var field = items.get(id);
7558         if(!field){
7559              items.each(function(f){
7560                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7561                     field = f;
7562                     return false;
7563                 }
7564                 return true;
7565             });
7566         }
7567         return field || null;
7568     },
7569      /**
7570      * Mark fields in this form invalid in bulk.
7571      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7572      * @return {BasicForm} this
7573      */
7574     markInvalid : function(errors){
7575         if(errors instanceof Array){
7576             for(var i = 0, len = errors.length; i < len; i++){
7577                 var fieldError = errors[i];
7578                 var f = this.findField(fieldError.id);
7579                 if(f){
7580                     f.markInvalid(fieldError.msg);
7581                 }
7582             }
7583         }else{
7584             var field, id;
7585             for(id in errors){
7586                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7587                     field.markInvalid(errors[id]);
7588                 }
7589             }
7590         }
7591         //Roo.each(this.childForms || [], function (f) {
7592         //    f.markInvalid(errors);
7593         //});
7594         
7595         return this;
7596     },
7597
7598     /**
7599      * Set values for fields in this form in bulk.
7600      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7601      * @return {BasicForm} this
7602      */
7603     setValues : function(values){
7604         if(values instanceof Array){ // array of objects
7605             for(var i = 0, len = values.length; i < len; i++){
7606                 var v = values[i];
7607                 var f = this.findField(v.id);
7608                 if(f){
7609                     f.setValue(v.value);
7610                     if(this.trackResetOnLoad){
7611                         f.originalValue = f.getValue();
7612                     }
7613                 }
7614             }
7615         }else{ // object hash
7616             var field, id;
7617             for(id in values){
7618                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7619                     
7620                     if (field.setFromData && 
7621                         field.valueField && 
7622                         field.displayField &&
7623                         // combos' with local stores can 
7624                         // be queried via setValue()
7625                         // to set their value..
7626                         (field.store && !field.store.isLocal)
7627                         ) {
7628                         // it's a combo
7629                         var sd = { };
7630                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7631                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7632                         field.setFromData(sd);
7633                         
7634                     } else {
7635                         field.setValue(values[id]);
7636                     }
7637                     
7638                     
7639                     if(this.trackResetOnLoad){
7640                         field.originalValue = field.getValue();
7641                     }
7642                 }
7643             }
7644         }
7645          
7646         //Roo.each(this.childForms || [], function (f) {
7647         //    f.setValues(values);
7648         //});
7649                 
7650         return this;
7651     },
7652
7653     /**
7654      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7655      * they are returned as an array.
7656      * @param {Boolean} asString
7657      * @return {Object}
7658      */
7659     getValues : function(asString){
7660         //if (this.childForms) {
7661             // copy values from the child forms
7662         //    Roo.each(this.childForms, function (f) {
7663         //        this.setValues(f.getValues());
7664         //    }, this);
7665         //}
7666         
7667         
7668         
7669         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7670         if(asString === true){
7671             return fs;
7672         }
7673         return Roo.urlDecode(fs);
7674     },
7675     
7676     /**
7677      * Returns the fields in this form as an object with key/value pairs. 
7678      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7679      * @return {Object}
7680      */
7681     getFieldValues : function(with_hidden)
7682     {
7683         var items = this.getItems();
7684         var ret = {};
7685         items.each(function(f){
7686             if (!f.getName()) {
7687                 return;
7688             }
7689             var v = f.getValue();
7690             if (f.inputType =='radio') {
7691                 if (typeof(ret[f.getName()]) == 'undefined') {
7692                     ret[f.getName()] = ''; // empty..
7693                 }
7694                 
7695                 if (!f.el.dom.checked) {
7696                     return;
7697                     
7698                 }
7699                 v = f.el.dom.value;
7700                 
7701             }
7702             
7703             // not sure if this supported any more..
7704             if ((typeof(v) == 'object') && f.getRawValue) {
7705                 v = f.getRawValue() ; // dates..
7706             }
7707             // combo boxes where name != hiddenName...
7708             if (f.name != f.getName()) {
7709                 ret[f.name] = f.getRawValue();
7710             }
7711             ret[f.getName()] = v;
7712         });
7713         
7714         return ret;
7715     },
7716
7717     /**
7718      * Clears all invalid messages in this form.
7719      * @return {BasicForm} this
7720      */
7721     clearInvalid : function(){
7722         var items = this.getItems();
7723         
7724         items.each(function(f){
7725            f.clearInvalid();
7726         });
7727         
7728         
7729         
7730         return this;
7731     },
7732
7733     /**
7734      * Resets this form.
7735      * @return {BasicForm} this
7736      */
7737     reset : function(){
7738         var items = this.getItems();
7739         items.each(function(f){
7740             f.reset();
7741         });
7742         
7743         Roo.each(this.childForms || [], function (f) {
7744             f.reset();
7745         });
7746        
7747         
7748         return this;
7749     },
7750     getItems : function()
7751     {
7752         var r=new Roo.util.MixedCollection(false, function(o){
7753             return o.id || (o.id = Roo.id());
7754         });
7755         var iter = function(el) {
7756             if (el.inputEl) {
7757                 r.add(el);
7758             }
7759             if (!el.items) {
7760                 return;
7761             }
7762             Roo.each(el.items,function(e) {
7763                 iter(e);
7764             });
7765             
7766             
7767         };
7768         
7769         iter(this);
7770         return r;
7771         
7772         
7773         
7774         
7775     }
7776     
7777 });
7778
7779  
7780 /*
7781  * Based on:
7782  * Ext JS Library 1.1.1
7783  * Copyright(c) 2006-2007, Ext JS, LLC.
7784  *
7785  * Originally Released Under LGPL - original licence link has changed is not relivant.
7786  *
7787  * Fork - LGPL
7788  * <script type="text/javascript">
7789  */
7790 /**
7791  * @class Roo.form.VTypes
7792  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7793  * @singleton
7794  */
7795 Roo.form.VTypes = function(){
7796     // closure these in so they are only created once.
7797     var alpha = /^[a-zA-Z_]+$/;
7798     var alphanum = /^[a-zA-Z0-9_]+$/;
7799     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7800     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7801
7802     // All these messages and functions are configurable
7803     return {
7804         /**
7805          * The function used to validate email addresses
7806          * @param {String} value The email address
7807          */
7808         'email' : function(v){
7809             return email.test(v);
7810         },
7811         /**
7812          * The error text to display when the email validation function returns false
7813          * @type String
7814          */
7815         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7816         /**
7817          * The keystroke filter mask to be applied on email input
7818          * @type RegExp
7819          */
7820         'emailMask' : /[a-z0-9_\.\-@]/i,
7821
7822         /**
7823          * The function used to validate URLs
7824          * @param {String} value The URL
7825          */
7826         'url' : function(v){
7827             return url.test(v);
7828         },
7829         /**
7830          * The error text to display when the url validation function returns false
7831          * @type String
7832          */
7833         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7834         
7835         /**
7836          * The function used to validate alpha values
7837          * @param {String} value The value
7838          */
7839         'alpha' : function(v){
7840             return alpha.test(v);
7841         },
7842         /**
7843          * The error text to display when the alpha validation function returns false
7844          * @type String
7845          */
7846         'alphaText' : 'This field should only contain letters and _',
7847         /**
7848          * The keystroke filter mask to be applied on alpha input
7849          * @type RegExp
7850          */
7851         'alphaMask' : /[a-z_]/i,
7852
7853         /**
7854          * The function used to validate alphanumeric values
7855          * @param {String} value The value
7856          */
7857         'alphanum' : function(v){
7858             return alphanum.test(v);
7859         },
7860         /**
7861          * The error text to display when the alphanumeric validation function returns false
7862          * @type String
7863          */
7864         'alphanumText' : 'This field should only contain letters, numbers and _',
7865         /**
7866          * The keystroke filter mask to be applied on alphanumeric input
7867          * @type RegExp
7868          */
7869         'alphanumMask' : /[a-z0-9_]/i
7870     };
7871 }();/*
7872  * - LGPL
7873  *
7874  * Input
7875  * 
7876  */
7877
7878 /**
7879  * @class Roo.bootstrap.Input
7880  * @extends Roo.bootstrap.Component
7881  * Bootstrap Input class
7882  * @cfg {Boolean} disabled is it disabled
7883  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7884  * @cfg {String} name name of the input
7885  * @cfg {string} fieldLabel - the label associated
7886  * @cfg {string} placeholder - placeholder to put in text.
7887  * @cfg {string}  before - input group add on before
7888  * @cfg {string} after - input group add on after
7889  * @cfg {string} size - (lg|sm) or leave empty..
7890  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7891  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7892  * @cfg {Number} md colspan out of 12 for computer-sized screens
7893  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7894  * @cfg {string} value default value of the input
7895  * @cfg {Number} labelWidth set the width of label (0-12)
7896  * @cfg {String} labelAlign (top|left)
7897  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7898  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7899
7900  * @cfg {String} align (left|center|right) Default left
7901  * @cfg {Boolean} forceFeedback (true|false) Default false
7902  * 
7903  * 
7904  * 
7905  * 
7906  * @constructor
7907  * Create a new Input
7908  * @param {Object} config The config object
7909  */
7910
7911 Roo.bootstrap.Input = function(config){
7912     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7913    
7914         this.addEvents({
7915             /**
7916              * @event focus
7917              * Fires when this field receives input focus.
7918              * @param {Roo.form.Field} this
7919              */
7920             focus : true,
7921             /**
7922              * @event blur
7923              * Fires when this field loses input focus.
7924              * @param {Roo.form.Field} this
7925              */
7926             blur : true,
7927             /**
7928              * @event specialkey
7929              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7930              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7931              * @param {Roo.form.Field} this
7932              * @param {Roo.EventObject} e The event object
7933              */
7934             specialkey : true,
7935             /**
7936              * @event change
7937              * Fires just before the field blurs if the field value has changed.
7938              * @param {Roo.form.Field} this
7939              * @param {Mixed} newValue The new value
7940              * @param {Mixed} oldValue The original value
7941              */
7942             change : true,
7943             /**
7944              * @event invalid
7945              * Fires after the field has been marked as invalid.
7946              * @param {Roo.form.Field} this
7947              * @param {String} msg The validation message
7948              */
7949             invalid : true,
7950             /**
7951              * @event valid
7952              * Fires after the field has been validated with no errors.
7953              * @param {Roo.form.Field} this
7954              */
7955             valid : true,
7956              /**
7957              * @event keyup
7958              * Fires after the key up
7959              * @param {Roo.form.Field} this
7960              * @param {Roo.EventObject}  e The event Object
7961              */
7962             keyup : true
7963         });
7964 };
7965
7966 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7967      /**
7968      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7969       automatic validation (defaults to "keyup").
7970      */
7971     validationEvent : "keyup",
7972      /**
7973      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7974      */
7975     validateOnBlur : true,
7976     /**
7977      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7978      */
7979     validationDelay : 250,
7980      /**
7981      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7982      */
7983     focusClass : "x-form-focus",  // not needed???
7984     
7985        
7986     /**
7987      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7988      */
7989     invalidClass : "has-warning",
7990     
7991     /**
7992      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7993      */
7994     validClass : "has-success",
7995     
7996     /**
7997      * @cfg {Boolean} hasFeedback (true|false) default true
7998      */
7999     hasFeedback : true,
8000     
8001     /**
8002      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8003      */
8004     invalidFeedbackClass : "glyphicon-warning-sign",
8005     
8006     /**
8007      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8008      */
8009     validFeedbackClass : "glyphicon-ok",
8010     
8011     /**
8012      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8013      */
8014     selectOnFocus : false,
8015     
8016      /**
8017      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8018      */
8019     maskRe : null,
8020        /**
8021      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8022      */
8023     vtype : null,
8024     
8025       /**
8026      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8027      */
8028     disableKeyFilter : false,
8029     
8030        /**
8031      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8032      */
8033     disabled : false,
8034      /**
8035      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8036      */
8037     allowBlank : true,
8038     /**
8039      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8040      */
8041     blankText : "This field is required",
8042     
8043      /**
8044      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8045      */
8046     minLength : 0,
8047     /**
8048      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8049      */
8050     maxLength : Number.MAX_VALUE,
8051     /**
8052      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8053      */
8054     minLengthText : "The minimum length for this field is {0}",
8055     /**
8056      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8057      */
8058     maxLengthText : "The maximum length for this field is {0}",
8059   
8060     
8061     /**
8062      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8063      * If available, this function will be called only after the basic validators all return true, and will be passed the
8064      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8065      */
8066     validator : null,
8067     /**
8068      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8069      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8070      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8071      */
8072     regex : null,
8073     /**
8074      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8075      */
8076     regexText : "",
8077     
8078     autocomplete: false,
8079     
8080     
8081     fieldLabel : '',
8082     inputType : 'text',
8083     
8084     name : false,
8085     placeholder: false,
8086     before : false,
8087     after : false,
8088     size : false,
8089     hasFocus : false,
8090     preventMark: false,
8091     isFormField : true,
8092     value : '',
8093     labelWidth : 2,
8094     labelAlign : false,
8095     readOnly : false,
8096     align : false,
8097     formatedValue : false,
8098     forceFeedback : false,
8099     
8100     parentLabelAlign : function()
8101     {
8102         var parent = this;
8103         while (parent.parent()) {
8104             parent = parent.parent();
8105             if (typeof(parent.labelAlign) !='undefined') {
8106                 return parent.labelAlign;
8107             }
8108         }
8109         return 'left';
8110         
8111     },
8112     
8113     getAutoCreate : function(){
8114         
8115         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8116         
8117         var id = Roo.id();
8118         
8119         var cfg = {};
8120         
8121         if(this.inputType != 'hidden'){
8122             cfg.cls = 'form-group' //input-group
8123         }
8124         
8125         var input =  {
8126             tag: 'input',
8127             id : id,
8128             type : this.inputType,
8129             value : this.value,
8130             cls : 'form-control',
8131             placeholder : this.placeholder || '',
8132             autocomplete : this.autocomplete || 'new-password'
8133         };
8134         
8135         
8136         if(this.align){
8137             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8138         }
8139         
8140         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8141             input.maxLength = this.maxLength;
8142         }
8143         
8144         if (this.disabled) {
8145             input.disabled=true;
8146         }
8147         
8148         if (this.readOnly) {
8149             input.readonly=true;
8150         }
8151         
8152         if (this.name) {
8153             input.name = this.name;
8154         }
8155         if (this.size) {
8156             input.cls += ' input-' + this.size;
8157         }
8158         var settings=this;
8159         ['xs','sm','md','lg'].map(function(size){
8160             if (settings[size]) {
8161                 cfg.cls += ' col-' + size + '-' + settings[size];
8162             }
8163         });
8164         
8165         var inputblock = input;
8166         
8167         var feedback = {
8168             tag: 'span',
8169             cls: 'glyphicon form-control-feedback'
8170         };
8171             
8172         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8173             
8174             inputblock = {
8175                 cls : 'has-feedback',
8176                 cn :  [
8177                     input,
8178                     feedback
8179                 ] 
8180             };  
8181         }
8182         
8183         if (this.before || this.after) {
8184             
8185             inputblock = {
8186                 cls : 'input-group',
8187                 cn :  [] 
8188             };
8189             
8190             if (this.before && typeof(this.before) == 'string') {
8191                 
8192                 inputblock.cn.push({
8193                     tag :'span',
8194                     cls : 'roo-input-before input-group-addon',
8195                     html : this.before
8196                 });
8197             }
8198             if (this.before && typeof(this.before) == 'object') {
8199                 this.before = Roo.factory(this.before);
8200                 
8201                 inputblock.cn.push({
8202                     tag :'span',
8203                     cls : 'roo-input-before input-group-' +
8204                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8205                 });
8206             }
8207             
8208             inputblock.cn.push(input);
8209             
8210             if (this.after && typeof(this.after) == 'string') {
8211                 inputblock.cn.push({
8212                     tag :'span',
8213                     cls : 'roo-input-after input-group-addon',
8214                     html : this.after
8215                 });
8216             }
8217             if (this.after && typeof(this.after) == 'object') {
8218                 this.after = Roo.factory(this.after);
8219                 
8220                 inputblock.cn.push({
8221                     tag :'span',
8222                     cls : 'roo-input-after input-group-' +
8223                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8224                 });
8225             }
8226             
8227             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8228                 inputblock.cls += ' has-feedback';
8229                 inputblock.cn.push(feedback);
8230             }
8231         };
8232         
8233         if (align ==='left' && this.fieldLabel.length) {
8234                 
8235                 cfg.cn = [
8236                     
8237                     {
8238                         tag: 'label',
8239                         'for' :  id,
8240                         cls : 'control-label col-sm-' + this.labelWidth,
8241                         html : this.fieldLabel
8242                         
8243                     },
8244                     {
8245                         cls : "col-sm-" + (12 - this.labelWidth), 
8246                         cn: [
8247                             inputblock
8248                         ]
8249                     }
8250                     
8251                 ];
8252         } else if ( this.fieldLabel.length) {
8253                 
8254                  cfg.cn = [
8255                    
8256                     {
8257                         tag: 'label',
8258                         //cls : 'input-group-addon',
8259                         html : this.fieldLabel
8260                         
8261                     },
8262                     
8263                     inputblock
8264                     
8265                 ];
8266
8267         } else {
8268             
8269                 cfg.cn = [
8270                     
8271                         inputblock
8272                     
8273                 ];
8274                 
8275                 
8276         };
8277         
8278         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8279            cfg.cls += ' navbar-form';
8280         }
8281         
8282         return cfg;
8283         
8284     },
8285     /**
8286      * return the real input element.
8287      */
8288     inputEl: function ()
8289     {
8290         return this.el.select('input.form-control',true).first();
8291     },
8292     
8293     tooltipEl : function()
8294     {
8295         return this.inputEl();
8296     },
8297     
8298     setDisabled : function(v)
8299     {
8300         var i  = this.inputEl().dom;
8301         if (!v) {
8302             i.removeAttribute('disabled');
8303             return;
8304             
8305         }
8306         i.setAttribute('disabled','true');
8307     },
8308     initEvents : function()
8309     {
8310           
8311         this.inputEl().on("keydown" , this.fireKey,  this);
8312         this.inputEl().on("focus", this.onFocus,  this);
8313         this.inputEl().on("blur", this.onBlur,  this);
8314         
8315         this.inputEl().relayEvent('keyup', this);
8316  
8317         // reference to original value for reset
8318         this.originalValue = this.getValue();
8319         //Roo.form.TextField.superclass.initEvents.call(this);
8320         if(this.validationEvent == 'keyup'){
8321             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8322             this.inputEl().on('keyup', this.filterValidation, this);
8323         }
8324         else if(this.validationEvent !== false){
8325             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8326         }
8327         
8328         if(this.selectOnFocus){
8329             this.on("focus", this.preFocus, this);
8330             
8331         }
8332         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8333             this.inputEl().on("keypress", this.filterKeys, this);
8334         }
8335        /* if(this.grow){
8336             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8337             this.el.on("click", this.autoSize,  this);
8338         }
8339         */
8340         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8341             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8342         }
8343         
8344         if (typeof(this.before) == 'object') {
8345             this.before.render(this.el.select('.roo-input-before',true).first());
8346         }
8347         if (typeof(this.after) == 'object') {
8348             this.after.render(this.el.select('.roo-input-after',true).first());
8349         }
8350         
8351         
8352     },
8353     filterValidation : function(e){
8354         if(!e.isNavKeyPress()){
8355             this.validationTask.delay(this.validationDelay);
8356         }
8357     },
8358      /**
8359      * Validates the field value
8360      * @return {Boolean} True if the value is valid, else false
8361      */
8362     validate : function(){
8363         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8364         if(this.disabled || this.validateValue(this.getRawValue())){
8365             this.markValid();
8366             return true;
8367         }
8368         
8369         this.markInvalid();
8370         return false;
8371     },
8372     
8373     
8374     /**
8375      * Validates a value according to the field's validation rules and marks the field as invalid
8376      * if the validation fails
8377      * @param {Mixed} value The value to validate
8378      * @return {Boolean} True if the value is valid, else false
8379      */
8380     validateValue : function(value){
8381         if(value.length < 1)  { // if it's blank
8382             if(this.allowBlank){
8383                 return true;
8384             }
8385             return false;
8386         }
8387         
8388         if(value.length < this.minLength){
8389             return false;
8390         }
8391         if(value.length > this.maxLength){
8392             return false;
8393         }
8394         if(this.vtype){
8395             var vt = Roo.form.VTypes;
8396             if(!vt[this.vtype](value, this)){
8397                 return false;
8398             }
8399         }
8400         if(typeof this.validator == "function"){
8401             var msg = this.validator(value);
8402             if(msg !== true){
8403                 return false;
8404             }
8405         }
8406         
8407         if(this.regex && !this.regex.test(value)){
8408             return false;
8409         }
8410         
8411         return true;
8412     },
8413
8414     
8415     
8416      // private
8417     fireKey : function(e){
8418         //Roo.log('field ' + e.getKey());
8419         if(e.isNavKeyPress()){
8420             this.fireEvent("specialkey", this, e);
8421         }
8422     },
8423     focus : function (selectText){
8424         if(this.rendered){
8425             this.inputEl().focus();
8426             if(selectText === true){
8427                 this.inputEl().dom.select();
8428             }
8429         }
8430         return this;
8431     } ,
8432     
8433     onFocus : function(){
8434         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8435            // this.el.addClass(this.focusClass);
8436         }
8437         if(!this.hasFocus){
8438             this.hasFocus = true;
8439             this.startValue = this.getValue();
8440             this.fireEvent("focus", this);
8441         }
8442     },
8443     
8444     beforeBlur : Roo.emptyFn,
8445
8446     
8447     // private
8448     onBlur : function(){
8449         this.beforeBlur();
8450         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8451             //this.el.removeClass(this.focusClass);
8452         }
8453         this.hasFocus = false;
8454         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8455             this.validate();
8456         }
8457         var v = this.getValue();
8458         if(String(v) !== String(this.startValue)){
8459             this.fireEvent('change', this, v, this.startValue);
8460         }
8461         this.fireEvent("blur", this);
8462     },
8463     
8464     /**
8465      * Resets the current field value to the originally loaded value and clears any validation messages
8466      */
8467     reset : function(){
8468         this.setValue(this.originalValue);
8469         this.validate();
8470     },
8471      /**
8472      * Returns the name of the field
8473      * @return {Mixed} name The name field
8474      */
8475     getName: function(){
8476         return this.name;
8477     },
8478      /**
8479      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8480      * @return {Mixed} value The field value
8481      */
8482     getValue : function(){
8483         
8484         var v = this.inputEl().getValue();
8485         
8486         return v;
8487     },
8488     /**
8489      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8490      * @return {Mixed} value The field value
8491      */
8492     getRawValue : function(){
8493         var v = this.inputEl().getValue();
8494         
8495         return v;
8496     },
8497     
8498     /**
8499      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8500      * @param {Mixed} value The value to set
8501      */
8502     setRawValue : function(v){
8503         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8504     },
8505     
8506     selectText : function(start, end){
8507         var v = this.getRawValue();
8508         if(v.length > 0){
8509             start = start === undefined ? 0 : start;
8510             end = end === undefined ? v.length : end;
8511             var d = this.inputEl().dom;
8512             if(d.setSelectionRange){
8513                 d.setSelectionRange(start, end);
8514             }else if(d.createTextRange){
8515                 var range = d.createTextRange();
8516                 range.moveStart("character", start);
8517                 range.moveEnd("character", v.length-end);
8518                 range.select();
8519             }
8520         }
8521     },
8522     
8523     /**
8524      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8525      * @param {Mixed} value The value to set
8526      */
8527     setValue : function(v){
8528         this.value = v;
8529         if(this.rendered){
8530             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8531             this.validate();
8532         }
8533     },
8534     
8535     /*
8536     processValue : function(value){
8537         if(this.stripCharsRe){
8538             var newValue = value.replace(this.stripCharsRe, '');
8539             if(newValue !== value){
8540                 this.setRawValue(newValue);
8541                 return newValue;
8542             }
8543         }
8544         return value;
8545     },
8546   */
8547     preFocus : function(){
8548         
8549         if(this.selectOnFocus){
8550             this.inputEl().dom.select();
8551         }
8552     },
8553     filterKeys : function(e){
8554         var k = e.getKey();
8555         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8556             return;
8557         }
8558         var c = e.getCharCode(), cc = String.fromCharCode(c);
8559         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8560             return;
8561         }
8562         if(!this.maskRe.test(cc)){
8563             e.stopEvent();
8564         }
8565     },
8566      /**
8567      * Clear any invalid styles/messages for this field
8568      */
8569     clearInvalid : function(){
8570         
8571         if(!this.el || this.preventMark){ // not rendered
8572             return;
8573         }
8574         
8575         var label = this.el.select('label', true).first();
8576         var icon = this.el.select('i.fa-star', true).first();
8577         
8578         if(label && icon){
8579             icon.remove();
8580         }
8581         
8582         this.el.removeClass(this.invalidClass);
8583         
8584         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8585             
8586             var feedback = this.el.select('.form-control-feedback', true).first();
8587             
8588             if(feedback){
8589                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8590             }
8591             
8592         }
8593         
8594         this.fireEvent('valid', this);
8595     },
8596     
8597      /**
8598      * Mark this field as valid
8599      */
8600     markValid : function()
8601     {
8602         if(!this.el  || this.preventMark){ // not rendered
8603             return;
8604         }
8605         
8606         this.el.removeClass([this.invalidClass, this.validClass]);
8607         
8608         var feedback = this.el.select('.form-control-feedback', true).first();
8609             
8610         if(feedback){
8611             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8612         }
8613
8614         if(this.disabled || this.allowBlank){
8615             return;
8616         }
8617         
8618         var formGroup = this.el.findParent('.form-group', false, true);
8619         
8620         if(formGroup){
8621             
8622             var label = formGroup.select('label', true).first();
8623             var icon = formGroup.select('i.fa-star', true).first();
8624             
8625             if(label && icon){
8626                 icon.remove();
8627             }
8628         }
8629         
8630         this.el.addClass(this.validClass);
8631         
8632         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8633             
8634             var feedback = this.el.select('.form-control-feedback', true).first();
8635             
8636             if(feedback){
8637                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8638                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8639             }
8640             
8641         }
8642         
8643         this.fireEvent('valid', this);
8644     },
8645     
8646      /**
8647      * Mark this field as invalid
8648      * @param {String} msg The validation message
8649      */
8650     markInvalid : function(msg)
8651     {
8652         if(!this.el  || this.preventMark){ // not rendered
8653             return;
8654         }
8655         
8656         this.el.removeClass([this.invalidClass, this.validClass]);
8657         
8658         var feedback = this.el.select('.form-control-feedback', true).first();
8659             
8660         if(feedback){
8661             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8662         }
8663
8664         if(this.disabled || this.allowBlank){
8665             return;
8666         }
8667         
8668         var formGroup = this.el.findParent('.form-group', false, true);
8669         
8670         if(formGroup){
8671             var label = formGroup.select('label', true).first();
8672             var icon = formGroup.select('i.fa-star', true).first();
8673
8674             if(!this.getValue().length && label && !icon){
8675                 this.el.findParent('.form-group', false, true).createChild({
8676                     tag : 'i',
8677                     cls : 'text-danger fa fa-lg fa-star',
8678                     tooltip : 'This field is required',
8679                     style : 'margin-right:5px;'
8680                 }, label, true);
8681             }
8682         }
8683         
8684         
8685         this.el.addClass(this.invalidClass);
8686         
8687         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8688             
8689             var feedback = this.el.select('.form-control-feedback', true).first();
8690             
8691             if(feedback){
8692                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8693                 
8694                 if(this.getValue().length || this.forceFeedback){
8695                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8696                 }
8697                 
8698             }
8699             
8700         }
8701         
8702         this.fireEvent('invalid', this, msg);
8703     },
8704     // private
8705     SafariOnKeyDown : function(event)
8706     {
8707         // this is a workaround for a password hang bug on chrome/ webkit.
8708         
8709         var isSelectAll = false;
8710         
8711         if(this.inputEl().dom.selectionEnd > 0){
8712             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8713         }
8714         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8715             event.preventDefault();
8716             this.setValue('');
8717             return;
8718         }
8719         
8720         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8721             
8722             event.preventDefault();
8723             // this is very hacky as keydown always get's upper case.
8724             //
8725             var cc = String.fromCharCode(event.getCharCode());
8726             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8727             
8728         }
8729     },
8730     adjustWidth : function(tag, w){
8731         tag = tag.toLowerCase();
8732         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8733             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8734                 if(tag == 'input'){
8735                     return w + 2;
8736                 }
8737                 if(tag == 'textarea'){
8738                     return w-2;
8739                 }
8740             }else if(Roo.isOpera){
8741                 if(tag == 'input'){
8742                     return w + 2;
8743                 }
8744                 if(tag == 'textarea'){
8745                     return w-2;
8746                 }
8747             }
8748         }
8749         return w;
8750     }
8751     
8752 });
8753
8754  
8755 /*
8756  * - LGPL
8757  *
8758  * Input
8759  * 
8760  */
8761
8762 /**
8763  * @class Roo.bootstrap.TextArea
8764  * @extends Roo.bootstrap.Input
8765  * Bootstrap TextArea class
8766  * @cfg {Number} cols Specifies the visible width of a text area
8767  * @cfg {Number} rows Specifies the visible number of lines in a text area
8768  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8769  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8770  * @cfg {string} html text
8771  * 
8772  * @constructor
8773  * Create a new TextArea
8774  * @param {Object} config The config object
8775  */
8776
8777 Roo.bootstrap.TextArea = function(config){
8778     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8779    
8780 };
8781
8782 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8783      
8784     cols : false,
8785     rows : 5,
8786     readOnly : false,
8787     warp : 'soft',
8788     resize : false,
8789     value: false,
8790     html: false,
8791     
8792     getAutoCreate : function(){
8793         
8794         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8795         
8796         var id = Roo.id();
8797         
8798         var cfg = {};
8799         
8800         var input =  {
8801             tag: 'textarea',
8802             id : id,
8803             warp : this.warp,
8804             rows : this.rows,
8805             value : this.value || '',
8806             html: this.html || '',
8807             cls : 'form-control',
8808             placeholder : this.placeholder || '' 
8809             
8810         };
8811         
8812         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8813             input.maxLength = this.maxLength;
8814         }
8815         
8816         if(this.resize){
8817             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8818         }
8819         
8820         if(this.cols){
8821             input.cols = this.cols;
8822         }
8823         
8824         if (this.readOnly) {
8825             input.readonly = true;
8826         }
8827         
8828         if (this.name) {
8829             input.name = this.name;
8830         }
8831         
8832         if (this.size) {
8833             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8834         }
8835         
8836         var settings=this;
8837         ['xs','sm','md','lg'].map(function(size){
8838             if (settings[size]) {
8839                 cfg.cls += ' col-' + size + '-' + settings[size];
8840             }
8841         });
8842         
8843         var inputblock = input;
8844         
8845         if(this.hasFeedback && !this.allowBlank){
8846             
8847             var feedback = {
8848                 tag: 'span',
8849                 cls: 'glyphicon form-control-feedback'
8850             };
8851
8852             inputblock = {
8853                 cls : 'has-feedback',
8854                 cn :  [
8855                     input,
8856                     feedback
8857                 ] 
8858             };  
8859         }
8860         
8861         
8862         if (this.before || this.after) {
8863             
8864             inputblock = {
8865                 cls : 'input-group',
8866                 cn :  [] 
8867             };
8868             if (this.before) {
8869                 inputblock.cn.push({
8870                     tag :'span',
8871                     cls : 'input-group-addon',
8872                     html : this.before
8873                 });
8874             }
8875             
8876             inputblock.cn.push(input);
8877             
8878             if(this.hasFeedback && !this.allowBlank){
8879                 inputblock.cls += ' has-feedback';
8880                 inputblock.cn.push(feedback);
8881             }
8882             
8883             if (this.after) {
8884                 inputblock.cn.push({
8885                     tag :'span',
8886                     cls : 'input-group-addon',
8887                     html : this.after
8888                 });
8889             }
8890             
8891         }
8892         
8893         if (align ==='left' && this.fieldLabel.length) {
8894 //                Roo.log("left and has label");
8895                 cfg.cn = [
8896                     
8897                     {
8898                         tag: 'label',
8899                         'for' :  id,
8900                         cls : 'control-label col-sm-' + this.labelWidth,
8901                         html : this.fieldLabel
8902                         
8903                     },
8904                     {
8905                         cls : "col-sm-" + (12 - this.labelWidth), 
8906                         cn: [
8907                             inputblock
8908                         ]
8909                     }
8910                     
8911                 ];
8912         } else if ( this.fieldLabel.length) {
8913 //                Roo.log(" label");
8914                  cfg.cn = [
8915                    
8916                     {
8917                         tag: 'label',
8918                         //cls : 'input-group-addon',
8919                         html : this.fieldLabel
8920                         
8921                     },
8922                     
8923                     inputblock
8924                     
8925                 ];
8926
8927         } else {
8928             
8929 //                   Roo.log(" no label && no align");
8930                 cfg.cn = [
8931                     
8932                         inputblock
8933                     
8934                 ];
8935                 
8936                 
8937         }
8938         
8939         if (this.disabled) {
8940             input.disabled=true;
8941         }
8942         
8943         return cfg;
8944         
8945     },
8946     /**
8947      * return the real textarea element.
8948      */
8949     inputEl: function ()
8950     {
8951         return this.el.select('textarea.form-control',true).first();
8952     },
8953     
8954     /**
8955      * Clear any invalid styles/messages for this field
8956      */
8957     clearInvalid : function()
8958     {
8959         
8960         if(!this.el || this.preventMark){ // not rendered
8961             return;
8962         }
8963         
8964         var label = this.el.select('label', true).first();
8965         var icon = this.el.select('i.fa-star', true).first();
8966         
8967         if(label && icon){
8968             icon.remove();
8969         }
8970         
8971         this.el.removeClass(this.invalidClass);
8972         
8973         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8974             
8975             var feedback = this.el.select('.form-control-feedback', true).first();
8976             
8977             if(feedback){
8978                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8979             }
8980             
8981         }
8982         
8983         this.fireEvent('valid', this);
8984     },
8985     
8986      /**
8987      * Mark this field as valid
8988      */
8989     markValid : function()
8990     {
8991         if(!this.el  || this.preventMark){ // not rendered
8992             return;
8993         }
8994         
8995         this.el.removeClass([this.invalidClass, this.validClass]);
8996         
8997         var feedback = this.el.select('.form-control-feedback', true).first();
8998             
8999         if(feedback){
9000             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9001         }
9002
9003         if(this.disabled || this.allowBlank){
9004             return;
9005         }
9006         
9007         var label = this.el.select('label', true).first();
9008         var icon = this.el.select('i.fa-star', true).first();
9009         
9010         if(label && icon){
9011             icon.remove();
9012         }
9013         
9014         this.el.addClass(this.validClass);
9015         
9016         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9017             
9018             var feedback = this.el.select('.form-control-feedback', true).first();
9019             
9020             if(feedback){
9021                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9022                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9023             }
9024             
9025         }
9026         
9027         this.fireEvent('valid', this);
9028     },
9029     
9030      /**
9031      * Mark this field as invalid
9032      * @param {String} msg The validation message
9033      */
9034     markInvalid : function(msg)
9035     {
9036         if(!this.el  || this.preventMark){ // not rendered
9037             return;
9038         }
9039         
9040         this.el.removeClass([this.invalidClass, this.validClass]);
9041         
9042         var feedback = this.el.select('.form-control-feedback', true).first();
9043             
9044         if(feedback){
9045             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9046         }
9047
9048         if(this.disabled || this.allowBlank){
9049             return;
9050         }
9051         
9052         var label = this.el.select('label', true).first();
9053         var icon = this.el.select('i.fa-star', true).first();
9054         
9055         if(!this.getValue().length && label && !icon){
9056             this.el.createChild({
9057                 tag : 'i',
9058                 cls : 'text-danger fa fa-lg fa-star',
9059                 tooltip : 'This field is required',
9060                 style : 'margin-right:5px;'
9061             }, label, true);
9062         }
9063
9064         this.el.addClass(this.invalidClass);
9065         
9066         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9067             
9068             var feedback = this.el.select('.form-control-feedback', true).first();
9069             
9070             if(feedback){
9071                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9072                 
9073                 if(this.getValue().length || this.forceFeedback){
9074                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9075                 }
9076                 
9077             }
9078             
9079         }
9080         
9081         this.fireEvent('invalid', this, msg);
9082     }
9083 });
9084
9085  
9086 /*
9087  * - LGPL
9088  *
9089  * trigger field - base class for combo..
9090  * 
9091  */
9092  
9093 /**
9094  * @class Roo.bootstrap.TriggerField
9095  * @extends Roo.bootstrap.Input
9096  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9097  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9098  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9099  * for which you can provide a custom implementation.  For example:
9100  * <pre><code>
9101 var trigger = new Roo.bootstrap.TriggerField();
9102 trigger.onTriggerClick = myTriggerFn;
9103 trigger.applyTo('my-field');
9104 </code></pre>
9105  *
9106  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9107  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9108  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9109  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9110  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9111
9112  * @constructor
9113  * Create a new TriggerField.
9114  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9115  * to the base TextField)
9116  */
9117 Roo.bootstrap.TriggerField = function(config){
9118     this.mimicing = false;
9119     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9120 };
9121
9122 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9123     /**
9124      * @cfg {String} triggerClass A CSS class to apply to the trigger
9125      */
9126      /**
9127      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9128      */
9129     hideTrigger:false,
9130
9131     /**
9132      * @cfg {Boolean} removable (true|false) special filter default false
9133      */
9134     removable : false,
9135     
9136     /** @cfg {Boolean} grow @hide */
9137     /** @cfg {Number} growMin @hide */
9138     /** @cfg {Number} growMax @hide */
9139
9140     /**
9141      * @hide 
9142      * @method
9143      */
9144     autoSize: Roo.emptyFn,
9145     // private
9146     monitorTab : true,
9147     // private
9148     deferHeight : true,
9149
9150     
9151     actionMode : 'wrap',
9152     
9153     caret : false,
9154     
9155     
9156     getAutoCreate : function(){
9157        
9158         var align = this.labelAlign || this.parentLabelAlign();
9159         
9160         var id = Roo.id();
9161         
9162         var cfg = {
9163             cls: 'form-group' //input-group
9164         };
9165         
9166         
9167         var input =  {
9168             tag: 'input',
9169             id : id,
9170             type : this.inputType,
9171             cls : 'form-control',
9172             autocomplete: 'new-password',
9173             placeholder : this.placeholder || '' 
9174             
9175         };
9176         if (this.name) {
9177             input.name = this.name;
9178         }
9179         if (this.size) {
9180             input.cls += ' input-' + this.size;
9181         }
9182         
9183         if (this.disabled) {
9184             input.disabled=true;
9185         }
9186         
9187         var inputblock = input;
9188         
9189         if(this.hasFeedback && !this.allowBlank){
9190             
9191             var feedback = {
9192                 tag: 'span',
9193                 cls: 'glyphicon form-control-feedback'
9194             };
9195             
9196             if(this.removable && !this.editable && !this.tickable){
9197                 inputblock = {
9198                     cls : 'has-feedback',
9199                     cn :  [
9200                         inputblock,
9201                         {
9202                             tag: 'button',
9203                             html : 'x',
9204                             cls : 'roo-combo-removable-btn close'
9205                         },
9206                         feedback
9207                     ] 
9208                 };
9209             } else {
9210                 inputblock = {
9211                     cls : 'has-feedback',
9212                     cn :  [
9213                         inputblock,
9214                         feedback
9215                     ] 
9216                 };
9217             }
9218
9219         } else {
9220             if(this.removable && !this.editable && !this.tickable){
9221                 inputblock = {
9222                     cls : 'roo-removable',
9223                     cn :  [
9224                         inputblock,
9225                         {
9226                             tag: 'button',
9227                             html : 'x',
9228                             cls : 'roo-combo-removable-btn close'
9229                         }
9230                     ] 
9231                 };
9232             }
9233         }
9234         
9235         if (this.before || this.after) {
9236             
9237             inputblock = {
9238                 cls : 'input-group',
9239                 cn :  [] 
9240             };
9241             if (this.before) {
9242                 inputblock.cn.push({
9243                     tag :'span',
9244                     cls : 'input-group-addon',
9245                     html : this.before
9246                 });
9247             }
9248             
9249             inputblock.cn.push(input);
9250             
9251             if(this.hasFeedback && !this.allowBlank){
9252                 inputblock.cls += ' has-feedback';
9253                 inputblock.cn.push(feedback);
9254             }
9255             
9256             if (this.after) {
9257                 inputblock.cn.push({
9258                     tag :'span',
9259                     cls : 'input-group-addon',
9260                     html : this.after
9261                 });
9262             }
9263             
9264         };
9265         
9266         var box = {
9267             tag: 'div',
9268             cn: [
9269                 {
9270                     tag: 'input',
9271                     type : 'hidden',
9272                     cls: 'form-hidden-field'
9273                 },
9274                 inputblock
9275             ]
9276             
9277         };
9278         
9279         if(this.multiple){
9280             box = {
9281                 tag: 'div',
9282                 cn: [
9283                     {
9284                         tag: 'input',
9285                         type : 'hidden',
9286                         cls: 'form-hidden-field'
9287                     },
9288                     {
9289                         tag: 'ul',
9290                         cls: 'roo-select2-choices',
9291                         cn:[
9292                             {
9293                                 tag: 'li',
9294                                 cls: 'roo-select2-search-field',
9295                                 cn: [
9296
9297                                     inputblock
9298                                 ]
9299                             }
9300                         ]
9301                     }
9302                 ]
9303             }
9304         };
9305         
9306         var combobox = {
9307             cls: 'roo-select2-container input-group',
9308             cn: [
9309                 box
9310 //                {
9311 //                    tag: 'ul',
9312 //                    cls: 'typeahead typeahead-long dropdown-menu',
9313 //                    style: 'display:none'
9314 //                }
9315             ]
9316         };
9317         
9318         if(!this.multiple && this.showToggleBtn){
9319             
9320             var caret = {
9321                         tag: 'span',
9322                         cls: 'caret'
9323              };
9324             if (this.caret != false) {
9325                 caret = {
9326                      tag: 'i',
9327                      cls: 'fa fa-' + this.caret
9328                 };
9329                 
9330             }
9331             
9332             combobox.cn.push({
9333                 tag :'span',
9334                 cls : 'input-group-addon btn dropdown-toggle',
9335                 cn : [
9336                     caret,
9337                     {
9338                         tag: 'span',
9339                         cls: 'combobox-clear',
9340                         cn  : [
9341                             {
9342                                 tag : 'i',
9343                                 cls: 'icon-remove'
9344                             }
9345                         ]
9346                     }
9347                 ]
9348
9349             })
9350         }
9351         
9352         if(this.multiple){
9353             combobox.cls += ' roo-select2-container-multi';
9354         }
9355         
9356         if (align ==='left' && this.fieldLabel.length) {
9357             
9358 //                Roo.log("left and has label");
9359                 cfg.cn = [
9360                     
9361                     {
9362                         tag: 'label',
9363                         'for' :  id,
9364                         cls : 'control-label col-sm-' + this.labelWidth,
9365                         html : this.fieldLabel
9366                         
9367                     },
9368                     {
9369                         cls : "col-sm-" + (12 - this.labelWidth), 
9370                         cn: [
9371                             combobox
9372                         ]
9373                     }
9374                     
9375                 ];
9376         } else if ( this.fieldLabel.length) {
9377 //                Roo.log(" label");
9378                  cfg.cn = [
9379                    
9380                     {
9381                         tag: 'label',
9382                         //cls : 'input-group-addon',
9383                         html : this.fieldLabel
9384                         
9385                     },
9386                     
9387                     combobox
9388                     
9389                 ];
9390
9391         } else {
9392             
9393 //                Roo.log(" no label && no align");
9394                 cfg = combobox
9395                      
9396                 
9397         }
9398          
9399         var settings=this;
9400         ['xs','sm','md','lg'].map(function(size){
9401             if (settings[size]) {
9402                 cfg.cls += ' col-' + size + '-' + settings[size];
9403             }
9404         });
9405         
9406         return cfg;
9407         
9408     },
9409     
9410     
9411     
9412     // private
9413     onResize : function(w, h){
9414 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9415 //        if(typeof w == 'number'){
9416 //            var x = w - this.trigger.getWidth();
9417 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9418 //            this.trigger.setStyle('left', x+'px');
9419 //        }
9420     },
9421
9422     // private
9423     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9424
9425     // private
9426     getResizeEl : function(){
9427         return this.inputEl();
9428     },
9429
9430     // private
9431     getPositionEl : function(){
9432         return this.inputEl();
9433     },
9434
9435     // private
9436     alignErrorIcon : function(){
9437         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9438     },
9439
9440     // private
9441     initEvents : function(){
9442         
9443         this.createList();
9444         
9445         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9446         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9447         if(!this.multiple && this.showToggleBtn){
9448             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9449             if(this.hideTrigger){
9450                 this.trigger.setDisplayed(false);
9451             }
9452             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9453         }
9454         
9455         if(this.multiple){
9456             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9457         }
9458         
9459         if(this.removable && !this.editable && !this.tickable){
9460             var close = this.closeTriggerEl();
9461             
9462             if(close){
9463                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9464                 close.on('click', this.removeBtnClick, this, close);
9465             }
9466         }
9467         
9468         //this.trigger.addClassOnOver('x-form-trigger-over');
9469         //this.trigger.addClassOnClick('x-form-trigger-click');
9470         
9471         //if(!this.width){
9472         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9473         //}
9474     },
9475     
9476     closeTriggerEl : function()
9477     {
9478         var close = this.el.select('.roo-combo-removable-btn', true).first();
9479         return close ? close : false;
9480     },
9481     
9482     removeBtnClick : function(e, h, el)
9483     {
9484         e.preventDefault();
9485         
9486         if(this.fireEvent("remove", this) !== false){
9487             this.reset();
9488             this.fireEvent("afterremove", this)
9489         }
9490     },
9491     
9492     createList : function()
9493     {
9494         this.list = Roo.get(document.body).createChild({
9495             tag: 'ul',
9496             cls: 'typeahead typeahead-long dropdown-menu',
9497             style: 'display:none'
9498         });
9499         
9500         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9501         
9502     },
9503
9504     // private
9505     initTrigger : function(){
9506        
9507     },
9508
9509     // private
9510     onDestroy : function(){
9511         if(this.trigger){
9512             this.trigger.removeAllListeners();
9513           //  this.trigger.remove();
9514         }
9515         //if(this.wrap){
9516         //    this.wrap.remove();
9517         //}
9518         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9519     },
9520
9521     // private
9522     onFocus : function(){
9523         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9524         /*
9525         if(!this.mimicing){
9526             this.wrap.addClass('x-trigger-wrap-focus');
9527             this.mimicing = true;
9528             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9529             if(this.monitorTab){
9530                 this.el.on("keydown", this.checkTab, this);
9531             }
9532         }
9533         */
9534     },
9535
9536     // private
9537     checkTab : function(e){
9538         if(e.getKey() == e.TAB){
9539             this.triggerBlur();
9540         }
9541     },
9542
9543     // private
9544     onBlur : function(){
9545         // do nothing
9546     },
9547
9548     // private
9549     mimicBlur : function(e, t){
9550         /*
9551         if(!this.wrap.contains(t) && this.validateBlur()){
9552             this.triggerBlur();
9553         }
9554         */
9555     },
9556
9557     // private
9558     triggerBlur : function(){
9559         this.mimicing = false;
9560         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9561         if(this.monitorTab){
9562             this.el.un("keydown", this.checkTab, this);
9563         }
9564         //this.wrap.removeClass('x-trigger-wrap-focus');
9565         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9566     },
9567
9568     // private
9569     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9570     validateBlur : function(e, t){
9571         return true;
9572     },
9573
9574     // private
9575     onDisable : function(){
9576         this.inputEl().dom.disabled = true;
9577         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9578         //if(this.wrap){
9579         //    this.wrap.addClass('x-item-disabled');
9580         //}
9581     },
9582
9583     // private
9584     onEnable : function(){
9585         this.inputEl().dom.disabled = false;
9586         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9587         //if(this.wrap){
9588         //    this.el.removeClass('x-item-disabled');
9589         //}
9590     },
9591
9592     // private
9593     onShow : function(){
9594         var ae = this.getActionEl();
9595         
9596         if(ae){
9597             ae.dom.style.display = '';
9598             ae.dom.style.visibility = 'visible';
9599         }
9600     },
9601
9602     // private
9603     
9604     onHide : function(){
9605         var ae = this.getActionEl();
9606         ae.dom.style.display = 'none';
9607     },
9608
9609     /**
9610      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9611      * by an implementing function.
9612      * @method
9613      * @param {EventObject} e
9614      */
9615     onTriggerClick : Roo.emptyFn
9616 });
9617  /*
9618  * Based on:
9619  * Ext JS Library 1.1.1
9620  * Copyright(c) 2006-2007, Ext JS, LLC.
9621  *
9622  * Originally Released Under LGPL - original licence link has changed is not relivant.
9623  *
9624  * Fork - LGPL
9625  * <script type="text/javascript">
9626  */
9627
9628
9629 /**
9630  * @class Roo.data.SortTypes
9631  * @singleton
9632  * Defines the default sorting (casting?) comparison functions used when sorting data.
9633  */
9634 Roo.data.SortTypes = {
9635     /**
9636      * Default sort that does nothing
9637      * @param {Mixed} s The value being converted
9638      * @return {Mixed} The comparison value
9639      */
9640     none : function(s){
9641         return s;
9642     },
9643     
9644     /**
9645      * The regular expression used to strip tags
9646      * @type {RegExp}
9647      * @property
9648      */
9649     stripTagsRE : /<\/?[^>]+>/gi,
9650     
9651     /**
9652      * Strips all HTML tags to sort on text only
9653      * @param {Mixed} s The value being converted
9654      * @return {String} The comparison value
9655      */
9656     asText : function(s){
9657         return String(s).replace(this.stripTagsRE, "");
9658     },
9659     
9660     /**
9661      * Strips all HTML tags to sort on text only - Case insensitive
9662      * @param {Mixed} s The value being converted
9663      * @return {String} The comparison value
9664      */
9665     asUCText : function(s){
9666         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9667     },
9668     
9669     /**
9670      * Case insensitive string
9671      * @param {Mixed} s The value being converted
9672      * @return {String} The comparison value
9673      */
9674     asUCString : function(s) {
9675         return String(s).toUpperCase();
9676     },
9677     
9678     /**
9679      * Date sorting
9680      * @param {Mixed} s The value being converted
9681      * @return {Number} The comparison value
9682      */
9683     asDate : function(s) {
9684         if(!s){
9685             return 0;
9686         }
9687         if(s instanceof Date){
9688             return s.getTime();
9689         }
9690         return Date.parse(String(s));
9691     },
9692     
9693     /**
9694      * Float sorting
9695      * @param {Mixed} s The value being converted
9696      * @return {Float} The comparison value
9697      */
9698     asFloat : function(s) {
9699         var val = parseFloat(String(s).replace(/,/g, ""));
9700         if(isNaN(val)) {
9701             val = 0;
9702         }
9703         return val;
9704     },
9705     
9706     /**
9707      * Integer sorting
9708      * @param {Mixed} s The value being converted
9709      * @return {Number} The comparison value
9710      */
9711     asInt : function(s) {
9712         var val = parseInt(String(s).replace(/,/g, ""));
9713         if(isNaN(val)) {
9714             val = 0;
9715         }
9716         return val;
9717     }
9718 };/*
9719  * Based on:
9720  * Ext JS Library 1.1.1
9721  * Copyright(c) 2006-2007, Ext JS, LLC.
9722  *
9723  * Originally Released Under LGPL - original licence link has changed is not relivant.
9724  *
9725  * Fork - LGPL
9726  * <script type="text/javascript">
9727  */
9728
9729 /**
9730 * @class Roo.data.Record
9731  * Instances of this class encapsulate both record <em>definition</em> information, and record
9732  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9733  * to access Records cached in an {@link Roo.data.Store} object.<br>
9734  * <p>
9735  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9736  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9737  * objects.<br>
9738  * <p>
9739  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9740  * @constructor
9741  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9742  * {@link #create}. The parameters are the same.
9743  * @param {Array} data An associative Array of data values keyed by the field name.
9744  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9745  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9746  * not specified an integer id is generated.
9747  */
9748 Roo.data.Record = function(data, id){
9749     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9750     this.data = data;
9751 };
9752
9753 /**
9754  * Generate a constructor for a specific record layout.
9755  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9756  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9757  * Each field definition object may contain the following properties: <ul>
9758  * <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,
9759  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9760  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9761  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9762  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9763  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9764  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9765  * this may be omitted.</p></li>
9766  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9767  * <ul><li>auto (Default, implies no conversion)</li>
9768  * <li>string</li>
9769  * <li>int</li>
9770  * <li>float</li>
9771  * <li>boolean</li>
9772  * <li>date</li></ul></p></li>
9773  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9774  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9775  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9776  * by the Reader into an object that will be stored in the Record. It is passed the
9777  * following parameters:<ul>
9778  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9779  * </ul></p></li>
9780  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9781  * </ul>
9782  * <br>usage:<br><pre><code>
9783 var TopicRecord = Roo.data.Record.create(
9784     {name: 'title', mapping: 'topic_title'},
9785     {name: 'author', mapping: 'username'},
9786     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9787     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9788     {name: 'lastPoster', mapping: 'user2'},
9789     {name: 'excerpt', mapping: 'post_text'}
9790 );
9791
9792 var myNewRecord = new TopicRecord({
9793     title: 'Do my job please',
9794     author: 'noobie',
9795     totalPosts: 1,
9796     lastPost: new Date(),
9797     lastPoster: 'Animal',
9798     excerpt: 'No way dude!'
9799 });
9800 myStore.add(myNewRecord);
9801 </code></pre>
9802  * @method create
9803  * @static
9804  */
9805 Roo.data.Record.create = function(o){
9806     var f = function(){
9807         f.superclass.constructor.apply(this, arguments);
9808     };
9809     Roo.extend(f, Roo.data.Record);
9810     var p = f.prototype;
9811     p.fields = new Roo.util.MixedCollection(false, function(field){
9812         return field.name;
9813     });
9814     for(var i = 0, len = o.length; i < len; i++){
9815         p.fields.add(new Roo.data.Field(o[i]));
9816     }
9817     f.getField = function(name){
9818         return p.fields.get(name);  
9819     };
9820     return f;
9821 };
9822
9823 Roo.data.Record.AUTO_ID = 1000;
9824 Roo.data.Record.EDIT = 'edit';
9825 Roo.data.Record.REJECT = 'reject';
9826 Roo.data.Record.COMMIT = 'commit';
9827
9828 Roo.data.Record.prototype = {
9829     /**
9830      * Readonly flag - true if this record has been modified.
9831      * @type Boolean
9832      */
9833     dirty : false,
9834     editing : false,
9835     error: null,
9836     modified: null,
9837
9838     // private
9839     join : function(store){
9840         this.store = store;
9841     },
9842
9843     /**
9844      * Set the named field to the specified value.
9845      * @param {String} name The name of the field to set.
9846      * @param {Object} value The value to set the field to.
9847      */
9848     set : function(name, value){
9849         if(this.data[name] == value){
9850             return;
9851         }
9852         this.dirty = true;
9853         if(!this.modified){
9854             this.modified = {};
9855         }
9856         if(typeof this.modified[name] == 'undefined'){
9857             this.modified[name] = this.data[name];
9858         }
9859         this.data[name] = value;
9860         if(!this.editing && this.store){
9861             this.store.afterEdit(this);
9862         }       
9863     },
9864
9865     /**
9866      * Get the value of the named field.
9867      * @param {String} name The name of the field to get the value of.
9868      * @return {Object} The value of the field.
9869      */
9870     get : function(name){
9871         return this.data[name]; 
9872     },
9873
9874     // private
9875     beginEdit : function(){
9876         this.editing = true;
9877         this.modified = {}; 
9878     },
9879
9880     // private
9881     cancelEdit : function(){
9882         this.editing = false;
9883         delete this.modified;
9884     },
9885
9886     // private
9887     endEdit : function(){
9888         this.editing = false;
9889         if(this.dirty && this.store){
9890             this.store.afterEdit(this);
9891         }
9892     },
9893
9894     /**
9895      * Usually called by the {@link Roo.data.Store} which owns the Record.
9896      * Rejects all changes made to the Record since either creation, or the last commit operation.
9897      * Modified fields are reverted to their original values.
9898      * <p>
9899      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9900      * of reject operations.
9901      */
9902     reject : function(){
9903         var m = this.modified;
9904         for(var n in m){
9905             if(typeof m[n] != "function"){
9906                 this.data[n] = m[n];
9907             }
9908         }
9909         this.dirty = false;
9910         delete this.modified;
9911         this.editing = false;
9912         if(this.store){
9913             this.store.afterReject(this);
9914         }
9915     },
9916
9917     /**
9918      * Usually called by the {@link Roo.data.Store} which owns the Record.
9919      * Commits all changes made to the Record since either creation, or the last commit operation.
9920      * <p>
9921      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9922      * of commit operations.
9923      */
9924     commit : function(){
9925         this.dirty = false;
9926         delete this.modified;
9927         this.editing = false;
9928         if(this.store){
9929             this.store.afterCommit(this);
9930         }
9931     },
9932
9933     // private
9934     hasError : function(){
9935         return this.error != null;
9936     },
9937
9938     // private
9939     clearError : function(){
9940         this.error = null;
9941     },
9942
9943     /**
9944      * Creates a copy of this record.
9945      * @param {String} id (optional) A new record id if you don't want to use this record's id
9946      * @return {Record}
9947      */
9948     copy : function(newId) {
9949         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9950     }
9951 };/*
9952  * Based on:
9953  * Ext JS Library 1.1.1
9954  * Copyright(c) 2006-2007, Ext JS, LLC.
9955  *
9956  * Originally Released Under LGPL - original licence link has changed is not relivant.
9957  *
9958  * Fork - LGPL
9959  * <script type="text/javascript">
9960  */
9961
9962
9963
9964 /**
9965  * @class Roo.data.Store
9966  * @extends Roo.util.Observable
9967  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9968  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9969  * <p>
9970  * 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
9971  * has no knowledge of the format of the data returned by the Proxy.<br>
9972  * <p>
9973  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9974  * instances from the data object. These records are cached and made available through accessor functions.
9975  * @constructor
9976  * Creates a new Store.
9977  * @param {Object} config A config object containing the objects needed for the Store to access data,
9978  * and read the data into Records.
9979  */
9980 Roo.data.Store = function(config){
9981     this.data = new Roo.util.MixedCollection(false);
9982     this.data.getKey = function(o){
9983         return o.id;
9984     };
9985     this.baseParams = {};
9986     // private
9987     this.paramNames = {
9988         "start" : "start",
9989         "limit" : "limit",
9990         "sort" : "sort",
9991         "dir" : "dir",
9992         "multisort" : "_multisort"
9993     };
9994
9995     if(config && config.data){
9996         this.inlineData = config.data;
9997         delete config.data;
9998     }
9999
10000     Roo.apply(this, config);
10001     
10002     if(this.reader){ // reader passed
10003         this.reader = Roo.factory(this.reader, Roo.data);
10004         this.reader.xmodule = this.xmodule || false;
10005         if(!this.recordType){
10006             this.recordType = this.reader.recordType;
10007         }
10008         if(this.reader.onMetaChange){
10009             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10010         }
10011     }
10012
10013     if(this.recordType){
10014         this.fields = this.recordType.prototype.fields;
10015     }
10016     this.modified = [];
10017
10018     this.addEvents({
10019         /**
10020          * @event datachanged
10021          * Fires when the data cache has changed, and a widget which is using this Store
10022          * as a Record cache should refresh its view.
10023          * @param {Store} this
10024          */
10025         datachanged : true,
10026         /**
10027          * @event metachange
10028          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10029          * @param {Store} this
10030          * @param {Object} meta The JSON metadata
10031          */
10032         metachange : true,
10033         /**
10034          * @event add
10035          * Fires when Records have been added to the Store
10036          * @param {Store} this
10037          * @param {Roo.data.Record[]} records The array of Records added
10038          * @param {Number} index The index at which the record(s) were added
10039          */
10040         add : true,
10041         /**
10042          * @event remove
10043          * Fires when a Record has been removed from the Store
10044          * @param {Store} this
10045          * @param {Roo.data.Record} record The Record that was removed
10046          * @param {Number} index The index at which the record was removed
10047          */
10048         remove : true,
10049         /**
10050          * @event update
10051          * Fires when a Record has been updated
10052          * @param {Store} this
10053          * @param {Roo.data.Record} record The Record that was updated
10054          * @param {String} operation The update operation being performed.  Value may be one of:
10055          * <pre><code>
10056  Roo.data.Record.EDIT
10057  Roo.data.Record.REJECT
10058  Roo.data.Record.COMMIT
10059          * </code></pre>
10060          */
10061         update : true,
10062         /**
10063          * @event clear
10064          * Fires when the data cache has been cleared.
10065          * @param {Store} this
10066          */
10067         clear : true,
10068         /**
10069          * @event beforeload
10070          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10071          * the load action will be canceled.
10072          * @param {Store} this
10073          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10074          */
10075         beforeload : true,
10076         /**
10077          * @event beforeloadadd
10078          * Fires after a new set of Records has been loaded.
10079          * @param {Store} this
10080          * @param {Roo.data.Record[]} records The Records that were loaded
10081          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10082          */
10083         beforeloadadd : true,
10084         /**
10085          * @event load
10086          * Fires after a new set of Records has been loaded, before they are added to the store.
10087          * @param {Store} this
10088          * @param {Roo.data.Record[]} records The Records that were loaded
10089          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10090          * @params {Object} return from reader
10091          */
10092         load : true,
10093         /**
10094          * @event loadexception
10095          * Fires if an exception occurs in the Proxy during loading.
10096          * Called with the signature of the Proxy's "loadexception" event.
10097          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10098          * 
10099          * @param {Proxy} 
10100          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10101          * @param {Object} load options 
10102          * @param {Object} jsonData from your request (normally this contains the Exception)
10103          */
10104         loadexception : true
10105     });
10106     
10107     if(this.proxy){
10108         this.proxy = Roo.factory(this.proxy, Roo.data);
10109         this.proxy.xmodule = this.xmodule || false;
10110         this.relayEvents(this.proxy,  ["loadexception"]);
10111     }
10112     this.sortToggle = {};
10113     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10114
10115     Roo.data.Store.superclass.constructor.call(this);
10116
10117     if(this.inlineData){
10118         this.loadData(this.inlineData);
10119         delete this.inlineData;
10120     }
10121 };
10122
10123 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10124      /**
10125     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10126     * without a remote query - used by combo/forms at present.
10127     */
10128     
10129     /**
10130     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10131     */
10132     /**
10133     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10134     */
10135     /**
10136     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10137     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10138     */
10139     /**
10140     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10141     * on any HTTP request
10142     */
10143     /**
10144     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10145     */
10146     /**
10147     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10148     */
10149     multiSort: false,
10150     /**
10151     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10152     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10153     */
10154     remoteSort : false,
10155
10156     /**
10157     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10158      * loaded or when a record is removed. (defaults to false).
10159     */
10160     pruneModifiedRecords : false,
10161
10162     // private
10163     lastOptions : null,
10164
10165     /**
10166      * Add Records to the Store and fires the add event.
10167      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10168      */
10169     add : function(records){
10170         records = [].concat(records);
10171         for(var i = 0, len = records.length; i < len; i++){
10172             records[i].join(this);
10173         }
10174         var index = this.data.length;
10175         this.data.addAll(records);
10176         this.fireEvent("add", this, records, index);
10177     },
10178
10179     /**
10180      * Remove a Record from the Store and fires the remove event.
10181      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10182      */
10183     remove : function(record){
10184         var index = this.data.indexOf(record);
10185         this.data.removeAt(index);
10186         if(this.pruneModifiedRecords){
10187             this.modified.remove(record);
10188         }
10189         this.fireEvent("remove", this, record, index);
10190     },
10191
10192     /**
10193      * Remove all Records from the Store and fires the clear event.
10194      */
10195     removeAll : function(){
10196         this.data.clear();
10197         if(this.pruneModifiedRecords){
10198             this.modified = [];
10199         }
10200         this.fireEvent("clear", this);
10201     },
10202
10203     /**
10204      * Inserts Records to the Store at the given index and fires the add event.
10205      * @param {Number} index The start index at which to insert the passed Records.
10206      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10207      */
10208     insert : function(index, records){
10209         records = [].concat(records);
10210         for(var i = 0, len = records.length; i < len; i++){
10211             this.data.insert(index, records[i]);
10212             records[i].join(this);
10213         }
10214         this.fireEvent("add", this, records, index);
10215     },
10216
10217     /**
10218      * Get the index within the cache of the passed Record.
10219      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10220      * @return {Number} The index of the passed Record. Returns -1 if not found.
10221      */
10222     indexOf : function(record){
10223         return this.data.indexOf(record);
10224     },
10225
10226     /**
10227      * Get the index within the cache of the Record with the passed id.
10228      * @param {String} id The id of the Record to find.
10229      * @return {Number} The index of the Record. Returns -1 if not found.
10230      */
10231     indexOfId : function(id){
10232         return this.data.indexOfKey(id);
10233     },
10234
10235     /**
10236      * Get the Record with the specified id.
10237      * @param {String} id The id of the Record to find.
10238      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10239      */
10240     getById : function(id){
10241         return this.data.key(id);
10242     },
10243
10244     /**
10245      * Get the Record at the specified index.
10246      * @param {Number} index The index of the Record to find.
10247      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10248      */
10249     getAt : function(index){
10250         return this.data.itemAt(index);
10251     },
10252
10253     /**
10254      * Returns a range of Records between specified indices.
10255      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10256      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10257      * @return {Roo.data.Record[]} An array of Records
10258      */
10259     getRange : function(start, end){
10260         return this.data.getRange(start, end);
10261     },
10262
10263     // private
10264     storeOptions : function(o){
10265         o = Roo.apply({}, o);
10266         delete o.callback;
10267         delete o.scope;
10268         this.lastOptions = o;
10269     },
10270
10271     /**
10272      * Loads the Record cache from the configured Proxy using the configured Reader.
10273      * <p>
10274      * If using remote paging, then the first load call must specify the <em>start</em>
10275      * and <em>limit</em> properties in the options.params property to establish the initial
10276      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10277      * <p>
10278      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10279      * and this call will return before the new data has been loaded. Perform any post-processing
10280      * in a callback function, or in a "load" event handler.</strong>
10281      * <p>
10282      * @param {Object} options An object containing properties which control loading options:<ul>
10283      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10284      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10285      * passed the following arguments:<ul>
10286      * <li>r : Roo.data.Record[]</li>
10287      * <li>options: Options object from the load call</li>
10288      * <li>success: Boolean success indicator</li></ul></li>
10289      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10290      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10291      * </ul>
10292      */
10293     load : function(options){
10294         options = options || {};
10295         if(this.fireEvent("beforeload", this, options) !== false){
10296             this.storeOptions(options);
10297             var p = Roo.apply(options.params || {}, this.baseParams);
10298             // if meta was not loaded from remote source.. try requesting it.
10299             if (!this.reader.metaFromRemote) {
10300                 p._requestMeta = 1;
10301             }
10302             if(this.sortInfo && this.remoteSort){
10303                 var pn = this.paramNames;
10304                 p[pn["sort"]] = this.sortInfo.field;
10305                 p[pn["dir"]] = this.sortInfo.direction;
10306             }
10307             if (this.multiSort) {
10308                 var pn = this.paramNames;
10309                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10310             }
10311             
10312             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10313         }
10314     },
10315
10316     /**
10317      * Reloads the Record cache from the configured Proxy using the configured Reader and
10318      * the options from the last load operation performed.
10319      * @param {Object} options (optional) An object containing properties which may override the options
10320      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10321      * the most recently used options are reused).
10322      */
10323     reload : function(options){
10324         this.load(Roo.applyIf(options||{}, this.lastOptions));
10325     },
10326
10327     // private
10328     // Called as a callback by the Reader during a load operation.
10329     loadRecords : function(o, options, success){
10330         if(!o || success === false){
10331             if(success !== false){
10332                 this.fireEvent("load", this, [], options, o);
10333             }
10334             if(options.callback){
10335                 options.callback.call(options.scope || this, [], options, false);
10336             }
10337             return;
10338         }
10339         // if data returned failure - throw an exception.
10340         if (o.success === false) {
10341             // show a message if no listener is registered.
10342             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10343                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10344             }
10345             // loadmask wil be hooked into this..
10346             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10347             return;
10348         }
10349         var r = o.records, t = o.totalRecords || r.length;
10350         
10351         this.fireEvent("beforeloadadd", this, r, options, o);
10352         
10353         if(!options || options.add !== true){
10354             if(this.pruneModifiedRecords){
10355                 this.modified = [];
10356             }
10357             for(var i = 0, len = r.length; i < len; i++){
10358                 r[i].join(this);
10359             }
10360             if(this.snapshot){
10361                 this.data = this.snapshot;
10362                 delete this.snapshot;
10363             }
10364             this.data.clear();
10365             this.data.addAll(r);
10366             this.totalLength = t;
10367             this.applySort();
10368             this.fireEvent("datachanged", this);
10369         }else{
10370             this.totalLength = Math.max(t, this.data.length+r.length);
10371             this.add(r);
10372         }
10373         this.fireEvent("load", this, r, options, o);
10374         if(options.callback){
10375             options.callback.call(options.scope || this, r, options, true);
10376         }
10377     },
10378
10379
10380     /**
10381      * Loads data from a passed data block. A Reader which understands the format of the data
10382      * must have been configured in the constructor.
10383      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10384      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10385      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10386      */
10387     loadData : function(o, append){
10388         var r = this.reader.readRecords(o);
10389         this.loadRecords(r, {add: append}, true);
10390     },
10391
10392     /**
10393      * Gets the number of cached records.
10394      * <p>
10395      * <em>If using paging, this may not be the total size of the dataset. If the data object
10396      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10397      * the data set size</em>
10398      */
10399     getCount : function(){
10400         return this.data.length || 0;
10401     },
10402
10403     /**
10404      * Gets the total number of records in the dataset as returned by the server.
10405      * <p>
10406      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10407      * the dataset size</em>
10408      */
10409     getTotalCount : function(){
10410         return this.totalLength || 0;
10411     },
10412
10413     /**
10414      * Returns the sort state of the Store as an object with two properties:
10415      * <pre><code>
10416  field {String} The name of the field by which the Records are sorted
10417  direction {String} The sort order, "ASC" or "DESC"
10418      * </code></pre>
10419      */
10420     getSortState : function(){
10421         return this.sortInfo;
10422     },
10423
10424     // private
10425     applySort : function(){
10426         if(this.sortInfo && !this.remoteSort){
10427             var s = this.sortInfo, f = s.field;
10428             var st = this.fields.get(f).sortType;
10429             var fn = function(r1, r2){
10430                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10431                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10432             };
10433             this.data.sort(s.direction, fn);
10434             if(this.snapshot && this.snapshot != this.data){
10435                 this.snapshot.sort(s.direction, fn);
10436             }
10437         }
10438     },
10439
10440     /**
10441      * Sets the default sort column and order to be used by the next load operation.
10442      * @param {String} fieldName The name of the field to sort by.
10443      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10444      */
10445     setDefaultSort : function(field, dir){
10446         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10447     },
10448
10449     /**
10450      * Sort the Records.
10451      * If remote sorting is used, the sort is performed on the server, and the cache is
10452      * reloaded. If local sorting is used, the cache is sorted internally.
10453      * @param {String} fieldName The name of the field to sort by.
10454      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10455      */
10456     sort : function(fieldName, dir){
10457         var f = this.fields.get(fieldName);
10458         if(!dir){
10459             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10460             
10461             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10462                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10463             }else{
10464                 dir = f.sortDir;
10465             }
10466         }
10467         this.sortToggle[f.name] = dir;
10468         this.sortInfo = {field: f.name, direction: dir};
10469         if(!this.remoteSort){
10470             this.applySort();
10471             this.fireEvent("datachanged", this);
10472         }else{
10473             this.load(this.lastOptions);
10474         }
10475     },
10476
10477     /**
10478      * Calls the specified function for each of the Records in the cache.
10479      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10480      * Returning <em>false</em> aborts and exits the iteration.
10481      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10482      */
10483     each : function(fn, scope){
10484         this.data.each(fn, scope);
10485     },
10486
10487     /**
10488      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10489      * (e.g., during paging).
10490      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10491      */
10492     getModifiedRecords : function(){
10493         return this.modified;
10494     },
10495
10496     // private
10497     createFilterFn : function(property, value, anyMatch){
10498         if(!value.exec){ // not a regex
10499             value = String(value);
10500             if(value.length == 0){
10501                 return false;
10502             }
10503             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10504         }
10505         return function(r){
10506             return value.test(r.data[property]);
10507         };
10508     },
10509
10510     /**
10511      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10512      * @param {String} property A field on your records
10513      * @param {Number} start The record index to start at (defaults to 0)
10514      * @param {Number} end The last record index to include (defaults to length - 1)
10515      * @return {Number} The sum
10516      */
10517     sum : function(property, start, end){
10518         var rs = this.data.items, v = 0;
10519         start = start || 0;
10520         end = (end || end === 0) ? end : rs.length-1;
10521
10522         for(var i = start; i <= end; i++){
10523             v += (rs[i].data[property] || 0);
10524         }
10525         return v;
10526     },
10527
10528     /**
10529      * Filter the records by a specified property.
10530      * @param {String} field A field on your records
10531      * @param {String/RegExp} value Either a string that the field
10532      * should start with or a RegExp to test against the field
10533      * @param {Boolean} anyMatch True to match any part not just the beginning
10534      */
10535     filter : function(property, value, anyMatch){
10536         var fn = this.createFilterFn(property, value, anyMatch);
10537         return fn ? this.filterBy(fn) : this.clearFilter();
10538     },
10539
10540     /**
10541      * Filter by a function. The specified function will be called with each
10542      * record in this data source. If the function returns true the record is included,
10543      * otherwise it is filtered.
10544      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10545      * @param {Object} scope (optional) The scope of the function (defaults to this)
10546      */
10547     filterBy : function(fn, scope){
10548         this.snapshot = this.snapshot || this.data;
10549         this.data = this.queryBy(fn, scope||this);
10550         this.fireEvent("datachanged", this);
10551     },
10552
10553     /**
10554      * Query the records by a specified property.
10555      * @param {String} field A field on your records
10556      * @param {String/RegExp} value Either a string that the field
10557      * should start with or a RegExp to test against the field
10558      * @param {Boolean} anyMatch True to match any part not just the beginning
10559      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10560      */
10561     query : function(property, value, anyMatch){
10562         var fn = this.createFilterFn(property, value, anyMatch);
10563         return fn ? this.queryBy(fn) : this.data.clone();
10564     },
10565
10566     /**
10567      * Query by a function. The specified function will be called with each
10568      * record in this data source. If the function returns true the record is included
10569      * in the results.
10570      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10571      * @param {Object} scope (optional) The scope of the function (defaults to this)
10572       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10573      **/
10574     queryBy : function(fn, scope){
10575         var data = this.snapshot || this.data;
10576         return data.filterBy(fn, scope||this);
10577     },
10578
10579     /**
10580      * Collects unique values for a particular dataIndex from this store.
10581      * @param {String} dataIndex The property to collect
10582      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10583      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10584      * @return {Array} An array of the unique values
10585      **/
10586     collect : function(dataIndex, allowNull, bypassFilter){
10587         var d = (bypassFilter === true && this.snapshot) ?
10588                 this.snapshot.items : this.data.items;
10589         var v, sv, r = [], l = {};
10590         for(var i = 0, len = d.length; i < len; i++){
10591             v = d[i].data[dataIndex];
10592             sv = String(v);
10593             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10594                 l[sv] = true;
10595                 r[r.length] = v;
10596             }
10597         }
10598         return r;
10599     },
10600
10601     /**
10602      * Revert to a view of the Record cache with no filtering applied.
10603      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10604      */
10605     clearFilter : function(suppressEvent){
10606         if(this.snapshot && this.snapshot != this.data){
10607             this.data = this.snapshot;
10608             delete this.snapshot;
10609             if(suppressEvent !== true){
10610                 this.fireEvent("datachanged", this);
10611             }
10612         }
10613     },
10614
10615     // private
10616     afterEdit : function(record){
10617         if(this.modified.indexOf(record) == -1){
10618             this.modified.push(record);
10619         }
10620         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10621     },
10622     
10623     // private
10624     afterReject : function(record){
10625         this.modified.remove(record);
10626         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10627     },
10628
10629     // private
10630     afterCommit : function(record){
10631         this.modified.remove(record);
10632         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10633     },
10634
10635     /**
10636      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10637      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10638      */
10639     commitChanges : function(){
10640         var m = this.modified.slice(0);
10641         this.modified = [];
10642         for(var i = 0, len = m.length; i < len; i++){
10643             m[i].commit();
10644         }
10645     },
10646
10647     /**
10648      * Cancel outstanding changes on all changed records.
10649      */
10650     rejectChanges : function(){
10651         var m = this.modified.slice(0);
10652         this.modified = [];
10653         for(var i = 0, len = m.length; i < len; i++){
10654             m[i].reject();
10655         }
10656     },
10657
10658     onMetaChange : function(meta, rtype, o){
10659         this.recordType = rtype;
10660         this.fields = rtype.prototype.fields;
10661         delete this.snapshot;
10662         this.sortInfo = meta.sortInfo || this.sortInfo;
10663         this.modified = [];
10664         this.fireEvent('metachange', this, this.reader.meta);
10665     },
10666     
10667     moveIndex : function(data, type)
10668     {
10669         var index = this.indexOf(data);
10670         
10671         var newIndex = index + type;
10672         
10673         this.remove(data);
10674         
10675         this.insert(newIndex, data);
10676         
10677     }
10678 });/*
10679  * Based on:
10680  * Ext JS Library 1.1.1
10681  * Copyright(c) 2006-2007, Ext JS, LLC.
10682  *
10683  * Originally Released Under LGPL - original licence link has changed is not relivant.
10684  *
10685  * Fork - LGPL
10686  * <script type="text/javascript">
10687  */
10688
10689 /**
10690  * @class Roo.data.SimpleStore
10691  * @extends Roo.data.Store
10692  * Small helper class to make creating Stores from Array data easier.
10693  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10694  * @cfg {Array} fields An array of field definition objects, or field name strings.
10695  * @cfg {Array} data The multi-dimensional array of data
10696  * @constructor
10697  * @param {Object} config
10698  */
10699 Roo.data.SimpleStore = function(config){
10700     Roo.data.SimpleStore.superclass.constructor.call(this, {
10701         isLocal : true,
10702         reader: new Roo.data.ArrayReader({
10703                 id: config.id
10704             },
10705             Roo.data.Record.create(config.fields)
10706         ),
10707         proxy : new Roo.data.MemoryProxy(config.data)
10708     });
10709     this.load();
10710 };
10711 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10712  * Based on:
10713  * Ext JS Library 1.1.1
10714  * Copyright(c) 2006-2007, Ext JS, LLC.
10715  *
10716  * Originally Released Under LGPL - original licence link has changed is not relivant.
10717  *
10718  * Fork - LGPL
10719  * <script type="text/javascript">
10720  */
10721
10722 /**
10723 /**
10724  * @extends Roo.data.Store
10725  * @class Roo.data.JsonStore
10726  * Small helper class to make creating Stores for JSON data easier. <br/>
10727 <pre><code>
10728 var store = new Roo.data.JsonStore({
10729     url: 'get-images.php',
10730     root: 'images',
10731     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10732 });
10733 </code></pre>
10734  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10735  * JsonReader and HttpProxy (unless inline data is provided).</b>
10736  * @cfg {Array} fields An array of field definition objects, or field name strings.
10737  * @constructor
10738  * @param {Object} config
10739  */
10740 Roo.data.JsonStore = function(c){
10741     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10742         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10743         reader: new Roo.data.JsonReader(c, c.fields)
10744     }));
10745 };
10746 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10747  * Based on:
10748  * Ext JS Library 1.1.1
10749  * Copyright(c) 2006-2007, Ext JS, LLC.
10750  *
10751  * Originally Released Under LGPL - original licence link has changed is not relivant.
10752  *
10753  * Fork - LGPL
10754  * <script type="text/javascript">
10755  */
10756
10757  
10758 Roo.data.Field = function(config){
10759     if(typeof config == "string"){
10760         config = {name: config};
10761     }
10762     Roo.apply(this, config);
10763     
10764     if(!this.type){
10765         this.type = "auto";
10766     }
10767     
10768     var st = Roo.data.SortTypes;
10769     // named sortTypes are supported, here we look them up
10770     if(typeof this.sortType == "string"){
10771         this.sortType = st[this.sortType];
10772     }
10773     
10774     // set default sortType for strings and dates
10775     if(!this.sortType){
10776         switch(this.type){
10777             case "string":
10778                 this.sortType = st.asUCString;
10779                 break;
10780             case "date":
10781                 this.sortType = st.asDate;
10782                 break;
10783             default:
10784                 this.sortType = st.none;
10785         }
10786     }
10787
10788     // define once
10789     var stripRe = /[\$,%]/g;
10790
10791     // prebuilt conversion function for this field, instead of
10792     // switching every time we're reading a value
10793     if(!this.convert){
10794         var cv, dateFormat = this.dateFormat;
10795         switch(this.type){
10796             case "":
10797             case "auto":
10798             case undefined:
10799                 cv = function(v){ return v; };
10800                 break;
10801             case "string":
10802                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10803                 break;
10804             case "int":
10805                 cv = function(v){
10806                     return v !== undefined && v !== null && v !== '' ?
10807                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10808                     };
10809                 break;
10810             case "float":
10811                 cv = function(v){
10812                     return v !== undefined && v !== null && v !== '' ?
10813                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10814                     };
10815                 break;
10816             case "bool":
10817             case "boolean":
10818                 cv = function(v){ return v === true || v === "true" || v == 1; };
10819                 break;
10820             case "date":
10821                 cv = function(v){
10822                     if(!v){
10823                         return '';
10824                     }
10825                     if(v instanceof Date){
10826                         return v;
10827                     }
10828                     if(dateFormat){
10829                         if(dateFormat == "timestamp"){
10830                             return new Date(v*1000);
10831                         }
10832                         return Date.parseDate(v, dateFormat);
10833                     }
10834                     var parsed = Date.parse(v);
10835                     return parsed ? new Date(parsed) : null;
10836                 };
10837              break;
10838             
10839         }
10840         this.convert = cv;
10841     }
10842 };
10843
10844 Roo.data.Field.prototype = {
10845     dateFormat: null,
10846     defaultValue: "",
10847     mapping: null,
10848     sortType : null,
10849     sortDir : "ASC"
10850 };/*
10851  * Based on:
10852  * Ext JS Library 1.1.1
10853  * Copyright(c) 2006-2007, Ext JS, LLC.
10854  *
10855  * Originally Released Under LGPL - original licence link has changed is not relivant.
10856  *
10857  * Fork - LGPL
10858  * <script type="text/javascript">
10859  */
10860  
10861 // Base class for reading structured data from a data source.  This class is intended to be
10862 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10863
10864 /**
10865  * @class Roo.data.DataReader
10866  * Base class for reading structured data from a data source.  This class is intended to be
10867  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10868  */
10869
10870 Roo.data.DataReader = function(meta, recordType){
10871     
10872     this.meta = meta;
10873     
10874     this.recordType = recordType instanceof Array ? 
10875         Roo.data.Record.create(recordType) : recordType;
10876 };
10877
10878 Roo.data.DataReader.prototype = {
10879      /**
10880      * Create an empty record
10881      * @param {Object} data (optional) - overlay some values
10882      * @return {Roo.data.Record} record created.
10883      */
10884     newRow :  function(d) {
10885         var da =  {};
10886         this.recordType.prototype.fields.each(function(c) {
10887             switch( c.type) {
10888                 case 'int' : da[c.name] = 0; break;
10889                 case 'date' : da[c.name] = new Date(); break;
10890                 case 'float' : da[c.name] = 0.0; break;
10891                 case 'boolean' : da[c.name] = false; break;
10892                 default : da[c.name] = ""; break;
10893             }
10894             
10895         });
10896         return new this.recordType(Roo.apply(da, d));
10897     }
10898     
10899 };/*
10900  * Based on:
10901  * Ext JS Library 1.1.1
10902  * Copyright(c) 2006-2007, Ext JS, LLC.
10903  *
10904  * Originally Released Under LGPL - original licence link has changed is not relivant.
10905  *
10906  * Fork - LGPL
10907  * <script type="text/javascript">
10908  */
10909
10910 /**
10911  * @class Roo.data.DataProxy
10912  * @extends Roo.data.Observable
10913  * This class is an abstract base class for implementations which provide retrieval of
10914  * unformatted data objects.<br>
10915  * <p>
10916  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10917  * (of the appropriate type which knows how to parse the data object) to provide a block of
10918  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10919  * <p>
10920  * Custom implementations must implement the load method as described in
10921  * {@link Roo.data.HttpProxy#load}.
10922  */
10923 Roo.data.DataProxy = function(){
10924     this.addEvents({
10925         /**
10926          * @event beforeload
10927          * Fires before a network request is made to retrieve a data object.
10928          * @param {Object} This DataProxy object.
10929          * @param {Object} params The params parameter to the load function.
10930          */
10931         beforeload : true,
10932         /**
10933          * @event load
10934          * Fires before the load method's callback is called.
10935          * @param {Object} This DataProxy object.
10936          * @param {Object} o The data object.
10937          * @param {Object} arg The callback argument object passed to the load function.
10938          */
10939         load : true,
10940         /**
10941          * @event loadexception
10942          * Fires if an Exception occurs during data retrieval.
10943          * @param {Object} This DataProxy object.
10944          * @param {Object} o The data object.
10945          * @param {Object} arg The callback argument object passed to the load function.
10946          * @param {Object} e The Exception.
10947          */
10948         loadexception : true
10949     });
10950     Roo.data.DataProxy.superclass.constructor.call(this);
10951 };
10952
10953 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10954
10955     /**
10956      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10957      */
10958 /*
10959  * Based on:
10960  * Ext JS Library 1.1.1
10961  * Copyright(c) 2006-2007, Ext JS, LLC.
10962  *
10963  * Originally Released Under LGPL - original licence link has changed is not relivant.
10964  *
10965  * Fork - LGPL
10966  * <script type="text/javascript">
10967  */
10968 /**
10969  * @class Roo.data.MemoryProxy
10970  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10971  * to the Reader when its load method is called.
10972  * @constructor
10973  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10974  */
10975 Roo.data.MemoryProxy = function(data){
10976     if (data.data) {
10977         data = data.data;
10978     }
10979     Roo.data.MemoryProxy.superclass.constructor.call(this);
10980     this.data = data;
10981 };
10982
10983 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10984     /**
10985      * Load data from the requested source (in this case an in-memory
10986      * data object passed to the constructor), read the data object into
10987      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10988      * process that block using the passed callback.
10989      * @param {Object} params This parameter is not used by the MemoryProxy class.
10990      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10991      * object into a block of Roo.data.Records.
10992      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10993      * The function must be passed <ul>
10994      * <li>The Record block object</li>
10995      * <li>The "arg" argument from the load function</li>
10996      * <li>A boolean success indicator</li>
10997      * </ul>
10998      * @param {Object} scope The scope in which to call the callback
10999      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11000      */
11001     load : function(params, reader, callback, scope, arg){
11002         params = params || {};
11003         var result;
11004         try {
11005             result = reader.readRecords(this.data);
11006         }catch(e){
11007             this.fireEvent("loadexception", this, arg, null, e);
11008             callback.call(scope, null, arg, false);
11009             return;
11010         }
11011         callback.call(scope, result, arg, true);
11012     },
11013     
11014     // private
11015     update : function(params, records){
11016         
11017     }
11018 });/*
11019  * Based on:
11020  * Ext JS Library 1.1.1
11021  * Copyright(c) 2006-2007, Ext JS, LLC.
11022  *
11023  * Originally Released Under LGPL - original licence link has changed is not relivant.
11024  *
11025  * Fork - LGPL
11026  * <script type="text/javascript">
11027  */
11028 /**
11029  * @class Roo.data.HttpProxy
11030  * @extends Roo.data.DataProxy
11031  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11032  * configured to reference a certain URL.<br><br>
11033  * <p>
11034  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11035  * from which the running page was served.<br><br>
11036  * <p>
11037  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11038  * <p>
11039  * Be aware that to enable the browser to parse an XML document, the server must set
11040  * the Content-Type header in the HTTP response to "text/xml".
11041  * @constructor
11042  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11043  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11044  * will be used to make the request.
11045  */
11046 Roo.data.HttpProxy = function(conn){
11047     Roo.data.HttpProxy.superclass.constructor.call(this);
11048     // is conn a conn config or a real conn?
11049     this.conn = conn;
11050     this.useAjax = !conn || !conn.events;
11051   
11052 };
11053
11054 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11055     // thse are take from connection...
11056     
11057     /**
11058      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11059      */
11060     /**
11061      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11062      * extra parameters to each request made by this object. (defaults to undefined)
11063      */
11064     /**
11065      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11066      *  to each request made by this object. (defaults to undefined)
11067      */
11068     /**
11069      * @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)
11070      */
11071     /**
11072      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11073      */
11074      /**
11075      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11076      * @type Boolean
11077      */
11078   
11079
11080     /**
11081      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11082      * @type Boolean
11083      */
11084     /**
11085      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11086      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11087      * a finer-grained basis than the DataProxy events.
11088      */
11089     getConnection : function(){
11090         return this.useAjax ? Roo.Ajax : this.conn;
11091     },
11092
11093     /**
11094      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11095      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11096      * process that block using the passed callback.
11097      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11098      * for the request to the remote server.
11099      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11100      * object into a block of Roo.data.Records.
11101      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11102      * The function must be passed <ul>
11103      * <li>The Record block object</li>
11104      * <li>The "arg" argument from the load function</li>
11105      * <li>A boolean success indicator</li>
11106      * </ul>
11107      * @param {Object} scope The scope in which to call the callback
11108      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11109      */
11110     load : function(params, reader, callback, scope, arg){
11111         if(this.fireEvent("beforeload", this, params) !== false){
11112             var  o = {
11113                 params : params || {},
11114                 request: {
11115                     callback : callback,
11116                     scope : scope,
11117                     arg : arg
11118                 },
11119                 reader: reader,
11120                 callback : this.loadResponse,
11121                 scope: this
11122             };
11123             if(this.useAjax){
11124                 Roo.applyIf(o, this.conn);
11125                 if(this.activeRequest){
11126                     Roo.Ajax.abort(this.activeRequest);
11127                 }
11128                 this.activeRequest = Roo.Ajax.request(o);
11129             }else{
11130                 this.conn.request(o);
11131             }
11132         }else{
11133             callback.call(scope||this, null, arg, false);
11134         }
11135     },
11136
11137     // private
11138     loadResponse : function(o, success, response){
11139         delete this.activeRequest;
11140         if(!success){
11141             this.fireEvent("loadexception", this, o, response);
11142             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11143             return;
11144         }
11145         var result;
11146         try {
11147             result = o.reader.read(response);
11148         }catch(e){
11149             this.fireEvent("loadexception", this, o, response, e);
11150             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11151             return;
11152         }
11153         
11154         this.fireEvent("load", this, o, o.request.arg);
11155         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11156     },
11157
11158     // private
11159     update : function(dataSet){
11160
11161     },
11162
11163     // private
11164     updateResponse : function(dataSet){
11165
11166     }
11167 });/*
11168  * Based on:
11169  * Ext JS Library 1.1.1
11170  * Copyright(c) 2006-2007, Ext JS, LLC.
11171  *
11172  * Originally Released Under LGPL - original licence link has changed is not relivant.
11173  *
11174  * Fork - LGPL
11175  * <script type="text/javascript">
11176  */
11177
11178 /**
11179  * @class Roo.data.ScriptTagProxy
11180  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11181  * other than the originating domain of the running page.<br><br>
11182  * <p>
11183  * <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
11184  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11185  * <p>
11186  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11187  * source code that is used as the source inside a &lt;script> tag.<br><br>
11188  * <p>
11189  * In order for the browser to process the returned data, the server must wrap the data object
11190  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11191  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11192  * depending on whether the callback name was passed:
11193  * <p>
11194  * <pre><code>
11195 boolean scriptTag = false;
11196 String cb = request.getParameter("callback");
11197 if (cb != null) {
11198     scriptTag = true;
11199     response.setContentType("text/javascript");
11200 } else {
11201     response.setContentType("application/x-json");
11202 }
11203 Writer out = response.getWriter();
11204 if (scriptTag) {
11205     out.write(cb + "(");
11206 }
11207 out.print(dataBlock.toJsonString());
11208 if (scriptTag) {
11209     out.write(");");
11210 }
11211 </pre></code>
11212  *
11213  * @constructor
11214  * @param {Object} config A configuration object.
11215  */
11216 Roo.data.ScriptTagProxy = function(config){
11217     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11218     Roo.apply(this, config);
11219     this.head = document.getElementsByTagName("head")[0];
11220 };
11221
11222 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11223
11224 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11225     /**
11226      * @cfg {String} url The URL from which to request the data object.
11227      */
11228     /**
11229      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11230      */
11231     timeout : 30000,
11232     /**
11233      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11234      * the server the name of the callback function set up by the load call to process the returned data object.
11235      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11236      * javascript output which calls this named function passing the data object as its only parameter.
11237      */
11238     callbackParam : "callback",
11239     /**
11240      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11241      * name to the request.
11242      */
11243     nocache : true,
11244
11245     /**
11246      * Load data from the configured URL, read the data object into
11247      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11248      * process that block using the passed callback.
11249      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11250      * for the request to the remote server.
11251      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11252      * object into a block of Roo.data.Records.
11253      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11254      * The function must be passed <ul>
11255      * <li>The Record block object</li>
11256      * <li>The "arg" argument from the load function</li>
11257      * <li>A boolean success indicator</li>
11258      * </ul>
11259      * @param {Object} scope The scope in which to call the callback
11260      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11261      */
11262     load : function(params, reader, callback, scope, arg){
11263         if(this.fireEvent("beforeload", this, params) !== false){
11264
11265             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11266
11267             var url = this.url;
11268             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11269             if(this.nocache){
11270                 url += "&_dc=" + (new Date().getTime());
11271             }
11272             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11273             var trans = {
11274                 id : transId,
11275                 cb : "stcCallback"+transId,
11276                 scriptId : "stcScript"+transId,
11277                 params : params,
11278                 arg : arg,
11279                 url : url,
11280                 callback : callback,
11281                 scope : scope,
11282                 reader : reader
11283             };
11284             var conn = this;
11285
11286             window[trans.cb] = function(o){
11287                 conn.handleResponse(o, trans);
11288             };
11289
11290             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11291
11292             if(this.autoAbort !== false){
11293                 this.abort();
11294             }
11295
11296             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11297
11298             var script = document.createElement("script");
11299             script.setAttribute("src", url);
11300             script.setAttribute("type", "text/javascript");
11301             script.setAttribute("id", trans.scriptId);
11302             this.head.appendChild(script);
11303
11304             this.trans = trans;
11305         }else{
11306             callback.call(scope||this, null, arg, false);
11307         }
11308     },
11309
11310     // private
11311     isLoading : function(){
11312         return this.trans ? true : false;
11313     },
11314
11315     /**
11316      * Abort the current server request.
11317      */
11318     abort : function(){
11319         if(this.isLoading()){
11320             this.destroyTrans(this.trans);
11321         }
11322     },
11323
11324     // private
11325     destroyTrans : function(trans, isLoaded){
11326         this.head.removeChild(document.getElementById(trans.scriptId));
11327         clearTimeout(trans.timeoutId);
11328         if(isLoaded){
11329             window[trans.cb] = undefined;
11330             try{
11331                 delete window[trans.cb];
11332             }catch(e){}
11333         }else{
11334             // if hasn't been loaded, wait for load to remove it to prevent script error
11335             window[trans.cb] = function(){
11336                 window[trans.cb] = undefined;
11337                 try{
11338                     delete window[trans.cb];
11339                 }catch(e){}
11340             };
11341         }
11342     },
11343
11344     // private
11345     handleResponse : function(o, trans){
11346         this.trans = false;
11347         this.destroyTrans(trans, true);
11348         var result;
11349         try {
11350             result = trans.reader.readRecords(o);
11351         }catch(e){
11352             this.fireEvent("loadexception", this, o, trans.arg, e);
11353             trans.callback.call(trans.scope||window, null, trans.arg, false);
11354             return;
11355         }
11356         this.fireEvent("load", this, o, trans.arg);
11357         trans.callback.call(trans.scope||window, result, trans.arg, true);
11358     },
11359
11360     // private
11361     handleFailure : function(trans){
11362         this.trans = false;
11363         this.destroyTrans(trans, false);
11364         this.fireEvent("loadexception", this, null, trans.arg);
11365         trans.callback.call(trans.scope||window, null, trans.arg, false);
11366     }
11367 });/*
11368  * Based on:
11369  * Ext JS Library 1.1.1
11370  * Copyright(c) 2006-2007, Ext JS, LLC.
11371  *
11372  * Originally Released Under LGPL - original licence link has changed is not relivant.
11373  *
11374  * Fork - LGPL
11375  * <script type="text/javascript">
11376  */
11377
11378 /**
11379  * @class Roo.data.JsonReader
11380  * @extends Roo.data.DataReader
11381  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11382  * based on mappings in a provided Roo.data.Record constructor.
11383  * 
11384  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11385  * in the reply previously. 
11386  * 
11387  * <p>
11388  * Example code:
11389  * <pre><code>
11390 var RecordDef = Roo.data.Record.create([
11391     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11392     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11393 ]);
11394 var myReader = new Roo.data.JsonReader({
11395     totalProperty: "results",    // The property which contains the total dataset size (optional)
11396     root: "rows",                // The property which contains an Array of row objects
11397     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11398 }, RecordDef);
11399 </code></pre>
11400  * <p>
11401  * This would consume a JSON file like this:
11402  * <pre><code>
11403 { 'results': 2, 'rows': [
11404     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11405     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11406 }
11407 </code></pre>
11408  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11409  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11410  * paged from the remote server.
11411  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11412  * @cfg {String} root name of the property which contains the Array of row objects.
11413  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11414  * @cfg {Array} fields Array of field definition objects
11415  * @constructor
11416  * Create a new JsonReader
11417  * @param {Object} meta Metadata configuration options
11418  * @param {Object} recordType Either an Array of field definition objects,
11419  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11420  */
11421 Roo.data.JsonReader = function(meta, recordType){
11422     
11423     meta = meta || {};
11424     // set some defaults:
11425     Roo.applyIf(meta, {
11426         totalProperty: 'total',
11427         successProperty : 'success',
11428         root : 'data',
11429         id : 'id'
11430     });
11431     
11432     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11433 };
11434 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11435     
11436     /**
11437      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11438      * Used by Store query builder to append _requestMeta to params.
11439      * 
11440      */
11441     metaFromRemote : false,
11442     /**
11443      * This method is only used by a DataProxy which has retrieved data from a remote server.
11444      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11445      * @return {Object} data A data block which is used by an Roo.data.Store object as
11446      * a cache of Roo.data.Records.
11447      */
11448     read : function(response){
11449         var json = response.responseText;
11450        
11451         var o = /* eval:var:o */ eval("("+json+")");
11452         if(!o) {
11453             throw {message: "JsonReader.read: Json object not found"};
11454         }
11455         
11456         if(o.metaData){
11457             
11458             delete this.ef;
11459             this.metaFromRemote = true;
11460             this.meta = o.metaData;
11461             this.recordType = Roo.data.Record.create(o.metaData.fields);
11462             this.onMetaChange(this.meta, this.recordType, o);
11463         }
11464         return this.readRecords(o);
11465     },
11466
11467     // private function a store will implement
11468     onMetaChange : function(meta, recordType, o){
11469
11470     },
11471
11472     /**
11473          * @ignore
11474          */
11475     simpleAccess: function(obj, subsc) {
11476         return obj[subsc];
11477     },
11478
11479         /**
11480          * @ignore
11481          */
11482     getJsonAccessor: function(){
11483         var re = /[\[\.]/;
11484         return function(expr) {
11485             try {
11486                 return(re.test(expr))
11487                     ? new Function("obj", "return obj." + expr)
11488                     : function(obj){
11489                         return obj[expr];
11490                     };
11491             } catch(e){}
11492             return Roo.emptyFn;
11493         };
11494     }(),
11495
11496     /**
11497      * Create a data block containing Roo.data.Records from an XML document.
11498      * @param {Object} o An object which contains an Array of row objects in the property specified
11499      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11500      * which contains the total size of the dataset.
11501      * @return {Object} data A data block which is used by an Roo.data.Store object as
11502      * a cache of Roo.data.Records.
11503      */
11504     readRecords : function(o){
11505         /**
11506          * After any data loads, the raw JSON data is available for further custom processing.
11507          * @type Object
11508          */
11509         this.o = o;
11510         var s = this.meta, Record = this.recordType,
11511             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11512
11513 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11514         if (!this.ef) {
11515             if(s.totalProperty) {
11516                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11517                 }
11518                 if(s.successProperty) {
11519                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11520                 }
11521                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11522                 if (s.id) {
11523                         var g = this.getJsonAccessor(s.id);
11524                         this.getId = function(rec) {
11525                                 var r = g(rec);  
11526                                 return (r === undefined || r === "") ? null : r;
11527                         };
11528                 } else {
11529                         this.getId = function(){return null;};
11530                 }
11531             this.ef = [];
11532             for(var jj = 0; jj < fl; jj++){
11533                 f = fi[jj];
11534                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11535                 this.ef[jj] = this.getJsonAccessor(map);
11536             }
11537         }
11538
11539         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11540         if(s.totalProperty){
11541             var vt = parseInt(this.getTotal(o), 10);
11542             if(!isNaN(vt)){
11543                 totalRecords = vt;
11544             }
11545         }
11546         if(s.successProperty){
11547             var vs = this.getSuccess(o);
11548             if(vs === false || vs === 'false'){
11549                 success = false;
11550             }
11551         }
11552         var records = [];
11553         for(var i = 0; i < c; i++){
11554                 var n = root[i];
11555             var values = {};
11556             var id = this.getId(n);
11557             for(var j = 0; j < fl; j++){
11558                 f = fi[j];
11559             var v = this.ef[j](n);
11560             if (!f.convert) {
11561                 Roo.log('missing convert for ' + f.name);
11562                 Roo.log(f);
11563                 continue;
11564             }
11565             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11566             }
11567             var record = new Record(values, id);
11568             record.json = n;
11569             records[i] = record;
11570         }
11571         return {
11572             raw : o,
11573             success : success,
11574             records : records,
11575             totalRecords : totalRecords
11576         };
11577     }
11578 });/*
11579  * Based on:
11580  * Ext JS Library 1.1.1
11581  * Copyright(c) 2006-2007, Ext JS, LLC.
11582  *
11583  * Originally Released Under LGPL - original licence link has changed is not relivant.
11584  *
11585  * Fork - LGPL
11586  * <script type="text/javascript">
11587  */
11588
11589 /**
11590  * @class Roo.data.ArrayReader
11591  * @extends Roo.data.DataReader
11592  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11593  * Each element of that Array represents a row of data fields. The
11594  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11595  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11596  * <p>
11597  * Example code:.
11598  * <pre><code>
11599 var RecordDef = Roo.data.Record.create([
11600     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11601     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11602 ]);
11603 var myReader = new Roo.data.ArrayReader({
11604     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11605 }, RecordDef);
11606 </code></pre>
11607  * <p>
11608  * This would consume an Array like this:
11609  * <pre><code>
11610 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11611   </code></pre>
11612  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11613  * @constructor
11614  * Create a new JsonReader
11615  * @param {Object} meta Metadata configuration options.
11616  * @param {Object} recordType Either an Array of field definition objects
11617  * as specified to {@link Roo.data.Record#create},
11618  * or an {@link Roo.data.Record} object
11619  * created using {@link Roo.data.Record#create}.
11620  */
11621 Roo.data.ArrayReader = function(meta, recordType){
11622     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11623 };
11624
11625 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11626     /**
11627      * Create a data block containing Roo.data.Records from an XML document.
11628      * @param {Object} o An Array of row objects which represents the dataset.
11629      * @return {Object} data A data block which is used by an Roo.data.Store object as
11630      * a cache of Roo.data.Records.
11631      */
11632     readRecords : function(o){
11633         var sid = this.meta ? this.meta.id : null;
11634         var recordType = this.recordType, fields = recordType.prototype.fields;
11635         var records = [];
11636         var root = o;
11637             for(var i = 0; i < root.length; i++){
11638                     var n = root[i];
11639                 var values = {};
11640                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11641                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11642                 var f = fields.items[j];
11643                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11644                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11645                 v = f.convert(v);
11646                 values[f.name] = v;
11647             }
11648                 var record = new recordType(values, id);
11649                 record.json = n;
11650                 records[records.length] = record;
11651             }
11652             return {
11653                 records : records,
11654                 totalRecords : records.length
11655             };
11656     }
11657 });/*
11658  * - LGPL
11659  * * 
11660  */
11661
11662 /**
11663  * @class Roo.bootstrap.ComboBox
11664  * @extends Roo.bootstrap.TriggerField
11665  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11666  * @cfg {Boolean} append (true|false) default false
11667  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11668  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11669  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11670  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11671  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11672  * @cfg {Boolean} animate default true
11673  * @cfg {Boolean} emptyResultText only for touch device
11674  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11675  * @constructor
11676  * Create a new ComboBox.
11677  * @param {Object} config Configuration options
11678  */
11679 Roo.bootstrap.ComboBox = function(config){
11680     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11681     this.addEvents({
11682         /**
11683          * @event expand
11684          * Fires when the dropdown list is expanded
11685              * @param {Roo.bootstrap.ComboBox} combo This combo box
11686              */
11687         'expand' : true,
11688         /**
11689          * @event collapse
11690          * Fires when the dropdown list is collapsed
11691              * @param {Roo.bootstrap.ComboBox} combo This combo box
11692              */
11693         'collapse' : true,
11694         /**
11695          * @event beforeselect
11696          * Fires before a list item is selected. Return false to cancel the selection.
11697              * @param {Roo.bootstrap.ComboBox} combo This combo box
11698              * @param {Roo.data.Record} record The data record returned from the underlying store
11699              * @param {Number} index The index of the selected item in the dropdown list
11700              */
11701         'beforeselect' : true,
11702         /**
11703          * @event select
11704          * Fires when a list item is selected
11705              * @param {Roo.bootstrap.ComboBox} combo This combo box
11706              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11707              * @param {Number} index The index of the selected item in the dropdown list
11708              */
11709         'select' : true,
11710         /**
11711          * @event beforequery
11712          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11713          * The event object passed has these properties:
11714              * @param {Roo.bootstrap.ComboBox} combo This combo box
11715              * @param {String} query The query
11716              * @param {Boolean} forceAll true to force "all" query
11717              * @param {Boolean} cancel true to cancel the query
11718              * @param {Object} e The query event object
11719              */
11720         'beforequery': true,
11721          /**
11722          * @event add
11723          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11724              * @param {Roo.bootstrap.ComboBox} combo This combo box
11725              */
11726         'add' : true,
11727         /**
11728          * @event edit
11729          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11730              * @param {Roo.bootstrap.ComboBox} combo This combo box
11731              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11732              */
11733         'edit' : true,
11734         /**
11735          * @event remove
11736          * Fires when the remove value from the combobox array
11737              * @param {Roo.bootstrap.ComboBox} combo This combo box
11738              */
11739         'remove' : true,
11740         /**
11741          * @event afterremove
11742          * Fires when the remove value from the combobox array
11743              * @param {Roo.bootstrap.ComboBox} combo This combo box
11744              */
11745         'afterremove' : true,
11746         /**
11747          * @event specialfilter
11748          * Fires when specialfilter
11749             * @param {Roo.bootstrap.ComboBox} combo This combo box
11750             */
11751         'specialfilter' : true,
11752         /**
11753          * @event tick
11754          * Fires when tick the element
11755             * @param {Roo.bootstrap.ComboBox} combo This combo box
11756             */
11757         'tick' : true,
11758         /**
11759          * @event touchviewdisplay
11760          * Fires when touch view require special display (default is using displayField)
11761             * @param {Roo.bootstrap.ComboBox} combo This combo box
11762             * @param {Object} cfg set html .
11763             */
11764         'touchviewdisplay' : true
11765         
11766     });
11767     
11768     this.item = [];
11769     this.tickItems = [];
11770     
11771     this.selectedIndex = -1;
11772     if(this.mode == 'local'){
11773         if(config.queryDelay === undefined){
11774             this.queryDelay = 10;
11775         }
11776         if(config.minChars === undefined){
11777             this.minChars = 0;
11778         }
11779     }
11780 };
11781
11782 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11783      
11784     /**
11785      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11786      * rendering into an Roo.Editor, defaults to false)
11787      */
11788     /**
11789      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11790      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11791      */
11792     /**
11793      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11794      */
11795     /**
11796      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11797      * the dropdown list (defaults to undefined, with no header element)
11798      */
11799
11800      /**
11801      * @cfg {String/Roo.Template} tpl The template to use to render the output
11802      */
11803      
11804      /**
11805      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11806      */
11807     listWidth: undefined,
11808     /**
11809      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11810      * mode = 'remote' or 'text' if mode = 'local')
11811      */
11812     displayField: undefined,
11813     
11814     /**
11815      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11816      * mode = 'remote' or 'value' if mode = 'local'). 
11817      * Note: use of a valueField requires the user make a selection
11818      * in order for a value to be mapped.
11819      */
11820     valueField: undefined,
11821     
11822     
11823     /**
11824      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11825      * field's data value (defaults to the underlying DOM element's name)
11826      */
11827     hiddenName: undefined,
11828     /**
11829      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11830      */
11831     listClass: '',
11832     /**
11833      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11834      */
11835     selectedClass: 'active',
11836     
11837     /**
11838      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11839      */
11840     shadow:'sides',
11841     /**
11842      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11843      * anchor positions (defaults to 'tl-bl')
11844      */
11845     listAlign: 'tl-bl?',
11846     /**
11847      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11848      */
11849     maxHeight: 300,
11850     /**
11851      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11852      * query specified by the allQuery config option (defaults to 'query')
11853      */
11854     triggerAction: 'query',
11855     /**
11856      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11857      * (defaults to 4, does not apply if editable = false)
11858      */
11859     minChars : 4,
11860     /**
11861      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11862      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11863      */
11864     typeAhead: false,
11865     /**
11866      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11867      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11868      */
11869     queryDelay: 500,
11870     /**
11871      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11872      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11873      */
11874     pageSize: 0,
11875     /**
11876      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11877      * when editable = true (defaults to false)
11878      */
11879     selectOnFocus:false,
11880     /**
11881      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11882      */
11883     queryParam: 'query',
11884     /**
11885      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11886      * when mode = 'remote' (defaults to 'Loading...')
11887      */
11888     loadingText: 'Loading...',
11889     /**
11890      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11891      */
11892     resizable: false,
11893     /**
11894      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11895      */
11896     handleHeight : 8,
11897     /**
11898      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11899      * traditional select (defaults to true)
11900      */
11901     editable: true,
11902     /**
11903      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11904      */
11905     allQuery: '',
11906     /**
11907      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11908      */
11909     mode: 'remote',
11910     /**
11911      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11912      * listWidth has a higher value)
11913      */
11914     minListWidth : 70,
11915     /**
11916      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11917      * allow the user to set arbitrary text into the field (defaults to false)
11918      */
11919     forceSelection:false,
11920     /**
11921      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11922      * if typeAhead = true (defaults to 250)
11923      */
11924     typeAheadDelay : 250,
11925     /**
11926      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11927      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11928      */
11929     valueNotFoundText : undefined,
11930     /**
11931      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11932      */
11933     blockFocus : false,
11934     
11935     /**
11936      * @cfg {Boolean} disableClear Disable showing of clear button.
11937      */
11938     disableClear : false,
11939     /**
11940      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11941      */
11942     alwaysQuery : false,
11943     
11944     /**
11945      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11946      */
11947     multiple : false,
11948     
11949     /**
11950      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11951      */
11952     invalidClass : "has-warning",
11953     
11954     /**
11955      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11956      */
11957     validClass : "has-success",
11958     
11959     /**
11960      * @cfg {Boolean} specialFilter (true|false) special filter default false
11961      */
11962     specialFilter : false,
11963     
11964     /**
11965      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11966      */
11967     mobileTouchView : true,
11968     
11969     //private
11970     addicon : false,
11971     editicon: false,
11972     
11973     page: 0,
11974     hasQuery: false,
11975     append: false,
11976     loadNext: false,
11977     autoFocus : true,
11978     tickable : false,
11979     btnPosition : 'right',
11980     triggerList : true,
11981     showToggleBtn : true,
11982     animate : true,
11983     emptyResultText: 'Empty',
11984     triggerText : 'Select',
11985     
11986     // element that contains real text value.. (when hidden is used..)
11987     
11988     getAutoCreate : function()
11989     {
11990         var cfg = false;
11991         
11992         /*
11993          * Touch Devices
11994          */
11995         
11996         if(Roo.isTouch && this.mobileTouchView){
11997             cfg = this.getAutoCreateTouchView();
11998             return cfg;;
11999         }
12000         
12001         /*
12002          *  Normal ComboBox
12003          */
12004         if(!this.tickable){
12005             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12006             return cfg;
12007         }
12008         
12009         /*
12010          *  ComboBox with tickable selections
12011          */
12012              
12013         var align = this.labelAlign || this.parentLabelAlign();
12014         
12015         cfg = {
12016             cls : 'form-group roo-combobox-tickable' //input-group
12017         };
12018         
12019         var buttons = {
12020             tag : 'div',
12021             cls : 'tickable-buttons',
12022             cn : [
12023                 {
12024                     tag : 'button',
12025                     type : 'button',
12026                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12027                     html : this.triggerText
12028                 },
12029                 {
12030                     tag : 'button',
12031                     type : 'button',
12032                     name : 'ok',
12033                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12034                     html : 'Done'
12035                 },
12036                 {
12037                     tag : 'button',
12038                     type : 'button',
12039                     name : 'cancel',
12040                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12041                     html : 'Cancel'
12042                 }
12043             ]
12044         };
12045         
12046         if(this.editable){
12047             buttons.cn.unshift({
12048                 tag: 'input',
12049                 cls: 'roo-select2-search-field-input'
12050             });
12051         }
12052         
12053         var _this = this;
12054         
12055         Roo.each(buttons.cn, function(c){
12056             if (_this.size) {
12057                 c.cls += ' btn-' + _this.size;
12058             }
12059
12060             if (_this.disabled) {
12061                 c.disabled = true;
12062             }
12063         });
12064         
12065         var box = {
12066             tag: 'div',
12067             cn: [
12068                 {
12069                     tag: 'input',
12070                     type : 'hidden',
12071                     cls: 'form-hidden-field'
12072                 },
12073                 {
12074                     tag: 'ul',
12075                     cls: 'roo-select2-choices',
12076                     cn:[
12077                         {
12078                             tag: 'li',
12079                             cls: 'roo-select2-search-field',
12080                             cn: [
12081
12082                                 buttons
12083                             ]
12084                         }
12085                     ]
12086                 }
12087             ]
12088         };
12089         
12090         var combobox = {
12091             cls: 'roo-select2-container input-group roo-select2-container-multi',
12092             cn: [
12093                 box
12094 //                {
12095 //                    tag: 'ul',
12096 //                    cls: 'typeahead typeahead-long dropdown-menu',
12097 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12098 //                }
12099             ]
12100         };
12101         
12102         if(this.hasFeedback && !this.allowBlank){
12103             
12104             var feedback = {
12105                 tag: 'span',
12106                 cls: 'glyphicon form-control-feedback'
12107             };
12108
12109             combobox.cn.push(feedback);
12110         }
12111         
12112         if (align ==='left' && this.fieldLabel.length) {
12113             
12114 //                Roo.log("left and has label");
12115                 cfg.cn = [
12116                     
12117                     {
12118                         tag: 'label',
12119                         'for' :  id,
12120                         cls : 'control-label col-sm-' + this.labelWidth,
12121                         html : this.fieldLabel
12122                         
12123                     },
12124                     {
12125                         cls : "col-sm-" + (12 - this.labelWidth), 
12126                         cn: [
12127                             combobox
12128                         ]
12129                     }
12130                     
12131                 ];
12132         } else if ( this.fieldLabel.length) {
12133 //                Roo.log(" label");
12134                  cfg.cn = [
12135                    
12136                     {
12137                         tag: 'label',
12138                         //cls : 'input-group-addon',
12139                         html : this.fieldLabel
12140                         
12141                     },
12142                     
12143                     combobox
12144                     
12145                 ];
12146
12147         } else {
12148             
12149 //                Roo.log(" no label && no align");
12150                 cfg = combobox
12151                      
12152                 
12153         }
12154          
12155         var settings=this;
12156         ['xs','sm','md','lg'].map(function(size){
12157             if (settings[size]) {
12158                 cfg.cls += ' col-' + size + '-' + settings[size];
12159             }
12160         });
12161         
12162         return cfg;
12163         
12164     },
12165     
12166     _initEventsCalled : false,
12167     
12168     // private
12169     initEvents: function()
12170     {
12171         
12172         if (this._initEventsCalled) { // as we call render... prevent looping...
12173             return;
12174         }
12175         this._initEventsCalled = true;
12176         
12177         if (!this.store) {
12178             throw "can not find store for combo";
12179         }
12180         
12181         this.store = Roo.factory(this.store, Roo.data);
12182         
12183         // if we are building from html. then this element is so complex, that we can not really
12184         // use the rendered HTML.
12185         // so we have to trash and replace the previous code.
12186         if (Roo.XComponent.build_from_html) {
12187             
12188             // remove this element....
12189             var e = this.el.dom, k=0;
12190             while (e ) { e = e.previousSibling;  ++k;}
12191
12192             this.el.remove();
12193             
12194             this.el=false;
12195             this.rendered = false;
12196             
12197             this.render(this.parent().getChildContainer(true), k);
12198             
12199             
12200             
12201         }
12202         
12203         
12204         /*
12205          * Touch Devices
12206          */
12207         
12208         if(Roo.isTouch && this.mobileTouchView){
12209             this.initTouchView();
12210             return;
12211         }
12212         
12213         if(this.tickable){
12214             this.initTickableEvents();
12215             return;
12216         }
12217         
12218         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12219         
12220         if(this.hiddenName){
12221             
12222             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12223             
12224             this.hiddenField.dom.value =
12225                 this.hiddenValue !== undefined ? this.hiddenValue :
12226                 this.value !== undefined ? this.value : '';
12227
12228             // prevent input submission
12229             this.el.dom.removeAttribute('name');
12230             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12231              
12232              
12233         }
12234         //if(Roo.isGecko){
12235         //    this.el.dom.setAttribute('autocomplete', 'off');
12236         //}
12237         
12238         var cls = 'x-combo-list';
12239         
12240         //this.list = new Roo.Layer({
12241         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12242         //});
12243         
12244         var _this = this;
12245         
12246         (function(){
12247             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12248             _this.list.setWidth(lw);
12249         }).defer(100);
12250         
12251         this.list.on('mouseover', this.onViewOver, this);
12252         this.list.on('mousemove', this.onViewMove, this);
12253         
12254         this.list.on('scroll', this.onViewScroll, this);
12255         
12256         /*
12257         this.list.swallowEvent('mousewheel');
12258         this.assetHeight = 0;
12259
12260         if(this.title){
12261             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12262             this.assetHeight += this.header.getHeight();
12263         }
12264
12265         this.innerList = this.list.createChild({cls:cls+'-inner'});
12266         this.innerList.on('mouseover', this.onViewOver, this);
12267         this.innerList.on('mousemove', this.onViewMove, this);
12268         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12269         
12270         if(this.allowBlank && !this.pageSize && !this.disableClear){
12271             this.footer = this.list.createChild({cls:cls+'-ft'});
12272             this.pageTb = new Roo.Toolbar(this.footer);
12273            
12274         }
12275         if(this.pageSize){
12276             this.footer = this.list.createChild({cls:cls+'-ft'});
12277             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12278                     {pageSize: this.pageSize});
12279             
12280         }
12281         
12282         if (this.pageTb && this.allowBlank && !this.disableClear) {
12283             var _this = this;
12284             this.pageTb.add(new Roo.Toolbar.Fill(), {
12285                 cls: 'x-btn-icon x-btn-clear',
12286                 text: '&#160;',
12287                 handler: function()
12288                 {
12289                     _this.collapse();
12290                     _this.clearValue();
12291                     _this.onSelect(false, -1);
12292                 }
12293             });
12294         }
12295         if (this.footer) {
12296             this.assetHeight += this.footer.getHeight();
12297         }
12298         */
12299             
12300         if(!this.tpl){
12301             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12302         }
12303
12304         this.view = new Roo.View(this.list, this.tpl, {
12305             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12306         });
12307         //this.view.wrapEl.setDisplayed(false);
12308         this.view.on('click', this.onViewClick, this);
12309         
12310         
12311         
12312         this.store.on('beforeload', this.onBeforeLoad, this);
12313         this.store.on('load', this.onLoad, this);
12314         this.store.on('loadexception', this.onLoadException, this);
12315         /*
12316         if(this.resizable){
12317             this.resizer = new Roo.Resizable(this.list,  {
12318                pinned:true, handles:'se'
12319             });
12320             this.resizer.on('resize', function(r, w, h){
12321                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12322                 this.listWidth = w;
12323                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12324                 this.restrictHeight();
12325             }, this);
12326             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12327         }
12328         */
12329         if(!this.editable){
12330             this.editable = true;
12331             this.setEditable(false);
12332         }
12333         
12334         /*
12335         
12336         if (typeof(this.events.add.listeners) != 'undefined') {
12337             
12338             this.addicon = this.wrap.createChild(
12339                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12340        
12341             this.addicon.on('click', function(e) {
12342                 this.fireEvent('add', this);
12343             }, this);
12344         }
12345         if (typeof(this.events.edit.listeners) != 'undefined') {
12346             
12347             this.editicon = this.wrap.createChild(
12348                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12349             if (this.addicon) {
12350                 this.editicon.setStyle('margin-left', '40px');
12351             }
12352             this.editicon.on('click', function(e) {
12353                 
12354                 // we fire even  if inothing is selected..
12355                 this.fireEvent('edit', this, this.lastData );
12356                 
12357             }, this);
12358         }
12359         */
12360         
12361         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12362             "up" : function(e){
12363                 this.inKeyMode = true;
12364                 this.selectPrev();
12365             },
12366
12367             "down" : function(e){
12368                 if(!this.isExpanded()){
12369                     this.onTriggerClick();
12370                 }else{
12371                     this.inKeyMode = true;
12372                     this.selectNext();
12373                 }
12374             },
12375
12376             "enter" : function(e){
12377 //                this.onViewClick();
12378                 //return true;
12379                 this.collapse();
12380                 
12381                 if(this.fireEvent("specialkey", this, e)){
12382                     this.onViewClick(false);
12383                 }
12384                 
12385                 return true;
12386             },
12387
12388             "esc" : function(e){
12389                 this.collapse();
12390             },
12391
12392             "tab" : function(e){
12393                 this.collapse();
12394                 
12395                 if(this.fireEvent("specialkey", this, e)){
12396                     this.onViewClick(false);
12397                 }
12398                 
12399                 return true;
12400             },
12401
12402             scope : this,
12403
12404             doRelay : function(foo, bar, hname){
12405                 if(hname == 'down' || this.scope.isExpanded()){
12406                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12407                 }
12408                 return true;
12409             },
12410
12411             forceKeyDown: true
12412         });
12413         
12414         
12415         this.queryDelay = Math.max(this.queryDelay || 10,
12416                 this.mode == 'local' ? 10 : 250);
12417         
12418         
12419         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12420         
12421         if(this.typeAhead){
12422             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12423         }
12424         if(this.editable !== false){
12425             this.inputEl().on("keyup", this.onKeyUp, this);
12426         }
12427         if(this.forceSelection){
12428             this.inputEl().on('blur', this.doForce, this);
12429         }
12430         
12431         if(this.multiple){
12432             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12433             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12434         }
12435     },
12436     
12437     initTickableEvents: function()
12438     {   
12439         this.createList();
12440         
12441         if(this.hiddenName){
12442             
12443             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12444             
12445             this.hiddenField.dom.value =
12446                 this.hiddenValue !== undefined ? this.hiddenValue :
12447                 this.value !== undefined ? this.value : '';
12448
12449             // prevent input submission
12450             this.el.dom.removeAttribute('name');
12451             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12452              
12453              
12454         }
12455         
12456 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12457         
12458         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12459         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12460         if(this.triggerList){
12461             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12462         }
12463          
12464         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12465         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12466         
12467         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12468         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12469         
12470         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12471         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12472         
12473         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12474         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12475         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12476         
12477         this.okBtn.hide();
12478         this.cancelBtn.hide();
12479         
12480         var _this = this;
12481         
12482         (function(){
12483             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12484             _this.list.setWidth(lw);
12485         }).defer(100);
12486         
12487         this.list.on('mouseover', this.onViewOver, this);
12488         this.list.on('mousemove', this.onViewMove, this);
12489         
12490         this.list.on('scroll', this.onViewScroll, this);
12491         
12492         if(!this.tpl){
12493             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>';
12494         }
12495
12496         this.view = new Roo.View(this.list, this.tpl, {
12497             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12498         });
12499         
12500         //this.view.wrapEl.setDisplayed(false);
12501         this.view.on('click', this.onViewClick, this);
12502         
12503         
12504         
12505         this.store.on('beforeload', this.onBeforeLoad, this);
12506         this.store.on('load', this.onLoad, this);
12507         this.store.on('loadexception', this.onLoadException, this);
12508         
12509         if(this.editable){
12510             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12511                 "up" : function(e){
12512                     this.inKeyMode = true;
12513                     this.selectPrev();
12514                 },
12515
12516                 "down" : function(e){
12517                     this.inKeyMode = true;
12518                     this.selectNext();
12519                 },
12520
12521                 "enter" : function(e){
12522                     if(this.fireEvent("specialkey", this, e)){
12523                         this.onViewClick(false);
12524                     }
12525                     
12526                     return true;
12527                 },
12528
12529                 "esc" : function(e){
12530                     this.onTickableFooterButtonClick(e, false, false);
12531                 },
12532
12533                 "tab" : function(e){
12534                     this.fireEvent("specialkey", this, e);
12535                     
12536                     this.onTickableFooterButtonClick(e, false, false);
12537                     
12538                     return true;
12539                 },
12540
12541                 scope : this,
12542
12543                 doRelay : function(e, fn, key){
12544                     if(this.scope.isExpanded()){
12545                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12546                     }
12547                     return true;
12548                 },
12549
12550                 forceKeyDown: true
12551             });
12552         }
12553         
12554         this.queryDelay = Math.max(this.queryDelay || 10,
12555                 this.mode == 'local' ? 10 : 250);
12556         
12557         
12558         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12559         
12560         if(this.typeAhead){
12561             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12562         }
12563         
12564         if(this.editable !== false){
12565             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12566         }
12567         
12568     },
12569
12570     onDestroy : function(){
12571         if(this.view){
12572             this.view.setStore(null);
12573             this.view.el.removeAllListeners();
12574             this.view.el.remove();
12575             this.view.purgeListeners();
12576         }
12577         if(this.list){
12578             this.list.dom.innerHTML  = '';
12579         }
12580         
12581         if(this.store){
12582             this.store.un('beforeload', this.onBeforeLoad, this);
12583             this.store.un('load', this.onLoad, this);
12584             this.store.un('loadexception', this.onLoadException, this);
12585         }
12586         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12587     },
12588
12589     // private
12590     fireKey : function(e){
12591         if(e.isNavKeyPress() && !this.list.isVisible()){
12592             this.fireEvent("specialkey", this, e);
12593         }
12594     },
12595
12596     // private
12597     onResize: function(w, h){
12598 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12599 //        
12600 //        if(typeof w != 'number'){
12601 //            // we do not handle it!?!?
12602 //            return;
12603 //        }
12604 //        var tw = this.trigger.getWidth();
12605 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12606 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12607 //        var x = w - tw;
12608 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12609 //            
12610 //        //this.trigger.setStyle('left', x+'px');
12611 //        
12612 //        if(this.list && this.listWidth === undefined){
12613 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12614 //            this.list.setWidth(lw);
12615 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12616 //        }
12617         
12618     
12619         
12620     },
12621
12622     /**
12623      * Allow or prevent the user from directly editing the field text.  If false is passed,
12624      * the user will only be able to select from the items defined in the dropdown list.  This method
12625      * is the runtime equivalent of setting the 'editable' config option at config time.
12626      * @param {Boolean} value True to allow the user to directly edit the field text
12627      */
12628     setEditable : function(value){
12629         if(value == this.editable){
12630             return;
12631         }
12632         this.editable = value;
12633         if(!value){
12634             this.inputEl().dom.setAttribute('readOnly', true);
12635             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12636             this.inputEl().addClass('x-combo-noedit');
12637         }else{
12638             this.inputEl().dom.setAttribute('readOnly', false);
12639             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12640             this.inputEl().removeClass('x-combo-noedit');
12641         }
12642     },
12643
12644     // private
12645     
12646     onBeforeLoad : function(combo,opts){
12647         if(!this.hasFocus){
12648             return;
12649         }
12650          if (!opts.add) {
12651             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12652          }
12653         this.restrictHeight();
12654         this.selectedIndex = -1;
12655     },
12656
12657     // private
12658     onLoad : function(){
12659         
12660         this.hasQuery = false;
12661         
12662         if(!this.hasFocus){
12663             return;
12664         }
12665         
12666         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12667             this.loading.hide();
12668         }
12669              
12670         if(this.store.getCount() > 0){
12671             this.expand();
12672             this.restrictHeight();
12673             if(this.lastQuery == this.allQuery){
12674                 if(this.editable && !this.tickable){
12675                     this.inputEl().dom.select();
12676                 }
12677                 
12678                 if(
12679                     !this.selectByValue(this.value, true) &&
12680                     this.autoFocus && 
12681                     (
12682                         !this.store.lastOptions ||
12683                         typeof(this.store.lastOptions.add) == 'undefined' || 
12684                         this.store.lastOptions.add != true
12685                     )
12686                 ){
12687                     this.select(0, true);
12688                 }
12689             }else{
12690                 if(this.autoFocus){
12691                     this.selectNext();
12692                 }
12693                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12694                     this.taTask.delay(this.typeAheadDelay);
12695                 }
12696             }
12697         }else{
12698             this.onEmptyResults();
12699         }
12700         
12701         //this.el.focus();
12702     },
12703     // private
12704     onLoadException : function()
12705     {
12706         this.hasQuery = false;
12707         
12708         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12709             this.loading.hide();
12710         }
12711         
12712         if(this.tickable && this.editable){
12713             return;
12714         }
12715         
12716         this.collapse();
12717         // only causes errors at present
12718         //Roo.log(this.store.reader.jsonData);
12719         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12720             // fixme
12721             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12722         //}
12723         
12724         
12725     },
12726     // private
12727     onTypeAhead : function(){
12728         if(this.store.getCount() > 0){
12729             var r = this.store.getAt(0);
12730             var newValue = r.data[this.displayField];
12731             var len = newValue.length;
12732             var selStart = this.getRawValue().length;
12733             
12734             if(selStart != len){
12735                 this.setRawValue(newValue);
12736                 this.selectText(selStart, newValue.length);
12737             }
12738         }
12739     },
12740
12741     // private
12742     onSelect : function(record, index){
12743         
12744         if(this.fireEvent('beforeselect', this, record, index) !== false){
12745         
12746             this.setFromData(index > -1 ? record.data : false);
12747             
12748             this.collapse();
12749             this.fireEvent('select', this, record, index);
12750         }
12751     },
12752
12753     /**
12754      * Returns the currently selected field value or empty string if no value is set.
12755      * @return {String} value The selected value
12756      */
12757     getValue : function(){
12758         
12759         if(this.multiple){
12760             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12761         }
12762         
12763         if(this.valueField){
12764             return typeof this.value != 'undefined' ? this.value : '';
12765         }else{
12766             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12767         }
12768     },
12769
12770     /**
12771      * Clears any text/value currently set in the field
12772      */
12773     clearValue : function(){
12774         if(this.hiddenField){
12775             this.hiddenField.dom.value = '';
12776         }
12777         this.value = '';
12778         this.setRawValue('');
12779         this.lastSelectionText = '';
12780         this.lastData = false;
12781         
12782         var close = this.closeTriggerEl();
12783         
12784         if(close){
12785             close.hide();
12786         }
12787         
12788     },
12789
12790     /**
12791      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12792      * will be displayed in the field.  If the value does not match the data value of an existing item,
12793      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12794      * Otherwise the field will be blank (although the value will still be set).
12795      * @param {String} value The value to match
12796      */
12797     setValue : function(v){
12798         if(this.multiple){
12799             this.syncValue();
12800             return;
12801         }
12802         
12803         var text = v;
12804         if(this.valueField){
12805             var r = this.findRecord(this.valueField, v);
12806             if(r){
12807                 text = r.data[this.displayField];
12808             }else if(this.valueNotFoundText !== undefined){
12809                 text = this.valueNotFoundText;
12810             }
12811         }
12812         this.lastSelectionText = text;
12813         if(this.hiddenField){
12814             this.hiddenField.dom.value = v;
12815         }
12816         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12817         this.value = v;
12818         
12819         var close = this.closeTriggerEl();
12820         
12821         if(close){
12822             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12823         }
12824     },
12825     /**
12826      * @property {Object} the last set data for the element
12827      */
12828     
12829     lastData : false,
12830     /**
12831      * Sets the value of the field based on a object which is related to the record format for the store.
12832      * @param {Object} value the value to set as. or false on reset?
12833      */
12834     setFromData : function(o){
12835         
12836         if(this.multiple){
12837             this.addItem(o);
12838             return;
12839         }
12840             
12841         var dv = ''; // display value
12842         var vv = ''; // value value..
12843         this.lastData = o;
12844         if (this.displayField) {
12845             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12846         } else {
12847             // this is an error condition!!!
12848             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12849         }
12850         
12851         if(this.valueField){
12852             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12853         }
12854         
12855         var close = this.closeTriggerEl();
12856         
12857         if(close){
12858             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12859         }
12860         
12861         if(this.hiddenField){
12862             this.hiddenField.dom.value = vv;
12863             
12864             this.lastSelectionText = dv;
12865             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12866             this.value = vv;
12867             return;
12868         }
12869         // no hidden field.. - we store the value in 'value', but still display
12870         // display field!!!!
12871         this.lastSelectionText = dv;
12872         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12873         this.value = vv;
12874         
12875         
12876         
12877     },
12878     // private
12879     reset : function(){
12880         // overridden so that last data is reset..
12881         
12882         if(this.multiple){
12883             this.clearItem();
12884             return;
12885         }
12886         
12887         this.setValue(this.originalValue);
12888         this.clearInvalid();
12889         this.lastData = false;
12890         if (this.view) {
12891             this.view.clearSelections();
12892         }
12893     },
12894     // private
12895     findRecord : function(prop, value){
12896         var record;
12897         if(this.store.getCount() > 0){
12898             this.store.each(function(r){
12899                 if(r.data[prop] == value){
12900                     record = r;
12901                     return false;
12902                 }
12903                 return true;
12904             });
12905         }
12906         return record;
12907     },
12908     
12909     getName: function()
12910     {
12911         // returns hidden if it's set..
12912         if (!this.rendered) {return ''};
12913         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12914         
12915     },
12916     // private
12917     onViewMove : function(e, t){
12918         this.inKeyMode = false;
12919     },
12920
12921     // private
12922     onViewOver : function(e, t){
12923         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12924             return;
12925         }
12926         var item = this.view.findItemFromChild(t);
12927         
12928         if(item){
12929             var index = this.view.indexOf(item);
12930             this.select(index, false);
12931         }
12932     },
12933
12934     // private
12935     onViewClick : function(view, doFocus, el, e)
12936     {
12937         var index = this.view.getSelectedIndexes()[0];
12938         
12939         var r = this.store.getAt(index);
12940         
12941         if(this.tickable){
12942             
12943             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12944                 return;
12945             }
12946             
12947             var rm = false;
12948             var _this = this;
12949             
12950             Roo.each(this.tickItems, function(v,k){
12951                 
12952                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12953                     Roo.log(v);
12954                     _this.tickItems.splice(k, 1);
12955                     
12956                     if(typeof(e) == 'undefined' && view == false){
12957                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12958                     }
12959                     
12960                     rm = true;
12961                     return;
12962                 }
12963             });
12964             
12965             if(rm){
12966                 return;
12967             }
12968             
12969             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12970                 this.tickItems.push(r.data);
12971             }
12972             
12973             if(typeof(e) == 'undefined' && view == false){
12974                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12975             }
12976                     
12977             return;
12978         }
12979         
12980         if(r){
12981             this.onSelect(r, index);
12982         }
12983         if(doFocus !== false && !this.blockFocus){
12984             this.inputEl().focus();
12985         }
12986     },
12987
12988     // private
12989     restrictHeight : function(){
12990         //this.innerList.dom.style.height = '';
12991         //var inner = this.innerList.dom;
12992         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12993         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12994         //this.list.beginUpdate();
12995         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12996         this.list.alignTo(this.inputEl(), this.listAlign);
12997         this.list.alignTo(this.inputEl(), this.listAlign);
12998         //this.list.endUpdate();
12999     },
13000
13001     // private
13002     onEmptyResults : function(){
13003         
13004         if(this.tickable && this.editable){
13005             this.restrictHeight();
13006             return;
13007         }
13008         
13009         this.collapse();
13010     },
13011
13012     /**
13013      * Returns true if the dropdown list is expanded, else false.
13014      */
13015     isExpanded : function(){
13016         return this.list.isVisible();
13017     },
13018
13019     /**
13020      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13021      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13022      * @param {String} value The data value of the item to select
13023      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13024      * selected item if it is not currently in view (defaults to true)
13025      * @return {Boolean} True if the value matched an item in the list, else false
13026      */
13027     selectByValue : function(v, scrollIntoView){
13028         if(v !== undefined && v !== null){
13029             var r = this.findRecord(this.valueField || this.displayField, v);
13030             if(r){
13031                 this.select(this.store.indexOf(r), scrollIntoView);
13032                 return true;
13033             }
13034         }
13035         return false;
13036     },
13037
13038     /**
13039      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13040      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13041      * @param {Number} index The zero-based index of the list item to select
13042      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13043      * selected item if it is not currently in view (defaults to true)
13044      */
13045     select : function(index, scrollIntoView){
13046         this.selectedIndex = index;
13047         this.view.select(index);
13048         if(scrollIntoView !== false){
13049             var el = this.view.getNode(index);
13050             /*
13051              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13052              */
13053             if(el){
13054                 this.list.scrollChildIntoView(el, false);
13055             }
13056         }
13057     },
13058
13059     // private
13060     selectNext : function(){
13061         var ct = this.store.getCount();
13062         if(ct > 0){
13063             if(this.selectedIndex == -1){
13064                 this.select(0);
13065             }else if(this.selectedIndex < ct-1){
13066                 this.select(this.selectedIndex+1);
13067             }
13068         }
13069     },
13070
13071     // private
13072     selectPrev : function(){
13073         var ct = this.store.getCount();
13074         if(ct > 0){
13075             if(this.selectedIndex == -1){
13076                 this.select(0);
13077             }else if(this.selectedIndex != 0){
13078                 this.select(this.selectedIndex-1);
13079             }
13080         }
13081     },
13082
13083     // private
13084     onKeyUp : function(e){
13085         if(this.editable !== false && !e.isSpecialKey()){
13086             this.lastKey = e.getKey();
13087             this.dqTask.delay(this.queryDelay);
13088         }
13089     },
13090
13091     // private
13092     validateBlur : function(){
13093         return !this.list || !this.list.isVisible();   
13094     },
13095
13096     // private
13097     initQuery : function(){
13098         
13099         var v = this.getRawValue();
13100         
13101         if(this.tickable && this.editable){
13102             v = this.tickableInputEl().getValue();
13103         }
13104         
13105         this.doQuery(v);
13106     },
13107
13108     // private
13109     doForce : function(){
13110         if(this.inputEl().dom.value.length > 0){
13111             this.inputEl().dom.value =
13112                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13113              
13114         }
13115     },
13116
13117     /**
13118      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13119      * query allowing the query action to be canceled if needed.
13120      * @param {String} query The SQL query to execute
13121      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13122      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13123      * saved in the current store (defaults to false)
13124      */
13125     doQuery : function(q, forceAll){
13126         
13127         if(q === undefined || q === null){
13128             q = '';
13129         }
13130         var qe = {
13131             query: q,
13132             forceAll: forceAll,
13133             combo: this,
13134             cancel:false
13135         };
13136         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13137             return false;
13138         }
13139         q = qe.query;
13140         
13141         forceAll = qe.forceAll;
13142         if(forceAll === true || (q.length >= this.minChars)){
13143             
13144             this.hasQuery = true;
13145             
13146             if(this.lastQuery != q || this.alwaysQuery){
13147                 this.lastQuery = q;
13148                 if(this.mode == 'local'){
13149                     this.selectedIndex = -1;
13150                     if(forceAll){
13151                         this.store.clearFilter();
13152                     }else{
13153                         
13154                         if(this.specialFilter){
13155                             this.fireEvent('specialfilter', this);
13156                             this.onLoad();
13157                             return;
13158                         }
13159                         
13160                         this.store.filter(this.displayField, q);
13161                     }
13162                     
13163                     this.store.fireEvent("datachanged", this.store);
13164                     
13165                     this.onLoad();
13166                     
13167                     
13168                 }else{
13169                     
13170                     this.store.baseParams[this.queryParam] = q;
13171                     
13172                     var options = {params : this.getParams(q)};
13173                     
13174                     if(this.loadNext){
13175                         options.add = true;
13176                         options.params.start = this.page * this.pageSize;
13177                     }
13178                     
13179                     this.store.load(options);
13180                     
13181                     /*
13182                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13183                      *  we should expand the list on onLoad
13184                      *  so command out it
13185                      */
13186 //                    this.expand();
13187                 }
13188             }else{
13189                 this.selectedIndex = -1;
13190                 this.onLoad();   
13191             }
13192         }
13193         
13194         this.loadNext = false;
13195     },
13196     
13197     // private
13198     getParams : function(q){
13199         var p = {};
13200         //p[this.queryParam] = q;
13201         
13202         if(this.pageSize){
13203             p.start = 0;
13204             p.limit = this.pageSize;
13205         }
13206         return p;
13207     },
13208
13209     /**
13210      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13211      */
13212     collapse : function(){
13213         if(!this.isExpanded()){
13214             return;
13215         }
13216         
13217         this.list.hide();
13218         
13219         if(this.tickable){
13220             this.hasFocus = false;
13221             this.okBtn.hide();
13222             this.cancelBtn.hide();
13223             this.trigger.show();
13224             
13225             if(this.editable){
13226                 this.tickableInputEl().dom.value = '';
13227                 this.tickableInputEl().blur();
13228             }
13229             
13230         }
13231         
13232         Roo.get(document).un('mousedown', this.collapseIf, this);
13233         Roo.get(document).un('mousewheel', this.collapseIf, this);
13234         if (!this.editable) {
13235             Roo.get(document).un('keydown', this.listKeyPress, this);
13236         }
13237         this.fireEvent('collapse', this);
13238     },
13239
13240     // private
13241     collapseIf : function(e){
13242         var in_combo  = e.within(this.el);
13243         var in_list =  e.within(this.list);
13244         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13245         
13246         if (in_combo || in_list || is_list) {
13247             //e.stopPropagation();
13248             return;
13249         }
13250         
13251         if(this.tickable){
13252             this.onTickableFooterButtonClick(e, false, false);
13253         }
13254
13255         this.collapse();
13256         
13257     },
13258
13259     /**
13260      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13261      */
13262     expand : function(){
13263        
13264         if(this.isExpanded() || !this.hasFocus){
13265             return;
13266         }
13267         
13268         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13269         this.list.setWidth(lw);
13270         
13271         
13272          Roo.log('expand');
13273         
13274         this.list.show();
13275         
13276         this.restrictHeight();
13277         
13278         if(this.tickable){
13279             
13280             this.tickItems = Roo.apply([], this.item);
13281             
13282             this.okBtn.show();
13283             this.cancelBtn.show();
13284             this.trigger.hide();
13285             
13286             if(this.editable){
13287                 this.tickableInputEl().focus();
13288             }
13289             
13290         }
13291         
13292         Roo.get(document).on('mousedown', this.collapseIf, this);
13293         Roo.get(document).on('mousewheel', this.collapseIf, this);
13294         if (!this.editable) {
13295             Roo.get(document).on('keydown', this.listKeyPress, this);
13296         }
13297         
13298         this.fireEvent('expand', this);
13299     },
13300
13301     // private
13302     // Implements the default empty TriggerField.onTriggerClick function
13303     onTriggerClick : function(e)
13304     {
13305         Roo.log('trigger click');
13306         
13307         if(this.disabled || !this.triggerList){
13308             return;
13309         }
13310         
13311         this.page = 0;
13312         this.loadNext = false;
13313         
13314         if(this.isExpanded()){
13315             this.collapse();
13316             if (!this.blockFocus) {
13317                 this.inputEl().focus();
13318             }
13319             
13320         }else {
13321             this.hasFocus = true;
13322             if(this.triggerAction == 'all') {
13323                 this.doQuery(this.allQuery, true);
13324             } else {
13325                 this.doQuery(this.getRawValue());
13326             }
13327             if (!this.blockFocus) {
13328                 this.inputEl().focus();
13329             }
13330         }
13331     },
13332     
13333     onTickableTriggerClick : function(e)
13334     {
13335         if(this.disabled){
13336             return;
13337         }
13338         
13339         this.page = 0;
13340         this.loadNext = false;
13341         this.hasFocus = true;
13342         
13343         if(this.triggerAction == 'all') {
13344             this.doQuery(this.allQuery, true);
13345         } else {
13346             this.doQuery(this.getRawValue());
13347         }
13348     },
13349     
13350     onSearchFieldClick : function(e)
13351     {
13352         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13353             this.onTickableFooterButtonClick(e, false, false);
13354             return;
13355         }
13356         
13357         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13358             return;
13359         }
13360         
13361         this.page = 0;
13362         this.loadNext = false;
13363         this.hasFocus = true;
13364         
13365         if(this.triggerAction == 'all') {
13366             this.doQuery(this.allQuery, true);
13367         } else {
13368             this.doQuery(this.getRawValue());
13369         }
13370     },
13371     
13372     listKeyPress : function(e)
13373     {
13374         //Roo.log('listkeypress');
13375         // scroll to first matching element based on key pres..
13376         if (e.isSpecialKey()) {
13377             return false;
13378         }
13379         var k = String.fromCharCode(e.getKey()).toUpperCase();
13380         //Roo.log(k);
13381         var match  = false;
13382         var csel = this.view.getSelectedNodes();
13383         var cselitem = false;
13384         if (csel.length) {
13385             var ix = this.view.indexOf(csel[0]);
13386             cselitem  = this.store.getAt(ix);
13387             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13388                 cselitem = false;
13389             }
13390             
13391         }
13392         
13393         this.store.each(function(v) { 
13394             if (cselitem) {
13395                 // start at existing selection.
13396                 if (cselitem.id == v.id) {
13397                     cselitem = false;
13398                 }
13399                 return true;
13400             }
13401                 
13402             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13403                 match = this.store.indexOf(v);
13404                 return false;
13405             }
13406             return true;
13407         }, this);
13408         
13409         if (match === false) {
13410             return true; // no more action?
13411         }
13412         // scroll to?
13413         this.view.select(match);
13414         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13415         sn.scrollIntoView(sn.dom.parentNode, false);
13416     },
13417     
13418     onViewScroll : function(e, t){
13419         
13420         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){
13421             return;
13422         }
13423         
13424         this.hasQuery = true;
13425         
13426         this.loading = this.list.select('.loading', true).first();
13427         
13428         if(this.loading === null){
13429             this.list.createChild({
13430                 tag: 'div',
13431                 cls: 'loading roo-select2-more-results roo-select2-active',
13432                 html: 'Loading more results...'
13433             });
13434             
13435             this.loading = this.list.select('.loading', true).first();
13436             
13437             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13438             
13439             this.loading.hide();
13440         }
13441         
13442         this.loading.show();
13443         
13444         var _combo = this;
13445         
13446         this.page++;
13447         this.loadNext = true;
13448         
13449         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13450         
13451         return;
13452     },
13453     
13454     addItem : function(o)
13455     {   
13456         var dv = ''; // display value
13457         
13458         if (this.displayField) {
13459             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13460         } else {
13461             // this is an error condition!!!
13462             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13463         }
13464         
13465         if(!dv.length){
13466             return;
13467         }
13468         
13469         var choice = this.choices.createChild({
13470             tag: 'li',
13471             cls: 'roo-select2-search-choice',
13472             cn: [
13473                 {
13474                     tag: 'div',
13475                     html: dv
13476                 },
13477                 {
13478                     tag: 'a',
13479                     href: '#',
13480                     cls: 'roo-select2-search-choice-close',
13481                     tabindex: '-1'
13482                 }
13483             ]
13484             
13485         }, this.searchField);
13486         
13487         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13488         
13489         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13490         
13491         this.item.push(o);
13492         
13493         this.lastData = o;
13494         
13495         this.syncValue();
13496         
13497         this.inputEl().dom.value = '';
13498         
13499         this.validate();
13500     },
13501     
13502     onRemoveItem : function(e, _self, o)
13503     {
13504         e.preventDefault();
13505         
13506         this.lastItem = Roo.apply([], this.item);
13507         
13508         var index = this.item.indexOf(o.data) * 1;
13509         
13510         if( index < 0){
13511             Roo.log('not this item?!');
13512             return;
13513         }
13514         
13515         this.item.splice(index, 1);
13516         o.item.remove();
13517         
13518         this.syncValue();
13519         
13520         this.fireEvent('remove', this, e);
13521         
13522         this.validate();
13523         
13524     },
13525     
13526     syncValue : function()
13527     {
13528         if(!this.item.length){
13529             this.clearValue();
13530             return;
13531         }
13532             
13533         var value = [];
13534         var _this = this;
13535         Roo.each(this.item, function(i){
13536             if(_this.valueField){
13537                 value.push(i[_this.valueField]);
13538                 return;
13539             }
13540
13541             value.push(i);
13542         });
13543
13544         this.value = value.join(',');
13545
13546         if(this.hiddenField){
13547             this.hiddenField.dom.value = this.value;
13548         }
13549         
13550         this.store.fireEvent("datachanged", this.store);
13551     },
13552     
13553     clearItem : function()
13554     {
13555         if(!this.multiple){
13556             return;
13557         }
13558         
13559         this.item = [];
13560         
13561         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13562            c.remove();
13563         });
13564         
13565         this.syncValue();
13566         
13567         this.validate();
13568         
13569         if(this.tickable && !Roo.isTouch){
13570             this.view.refresh();
13571         }
13572     },
13573     
13574     inputEl: function ()
13575     {
13576         if(Roo.isTouch && this.mobileTouchView){
13577             return this.el.select('input.form-control',true).first();
13578         }
13579         
13580         if(this.tickable){
13581             return this.searchField;
13582         }
13583         
13584         return this.el.select('input.form-control',true).first();
13585     },
13586     
13587     
13588     onTickableFooterButtonClick : function(e, btn, el)
13589     {
13590         e.preventDefault();
13591         
13592         this.lastItem = Roo.apply([], this.item);
13593         
13594         if(btn && btn.name == 'cancel'){
13595             this.tickItems = Roo.apply([], this.item);
13596             this.collapse();
13597             return;
13598         }
13599         
13600         this.clearItem();
13601         
13602         var _this = this;
13603         
13604         Roo.each(this.tickItems, function(o){
13605             _this.addItem(o);
13606         });
13607         
13608         this.collapse();
13609         
13610     },
13611     
13612     validate : function()
13613     {
13614         var v = this.getRawValue();
13615         
13616         if(this.multiple){
13617             v = this.getValue();
13618         }
13619         
13620         if(this.disabled || this.allowBlank || v.length){
13621             this.markValid();
13622             return true;
13623         }
13624         
13625         this.markInvalid();
13626         return false;
13627     },
13628     
13629     tickableInputEl : function()
13630     {
13631         if(!this.tickable || !this.editable){
13632             return this.inputEl();
13633         }
13634         
13635         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13636     },
13637     
13638     
13639     getAutoCreateTouchView : function()
13640     {
13641         var id = Roo.id();
13642         
13643         var cfg = {
13644             cls: 'form-group' //input-group
13645         };
13646         
13647         var input =  {
13648             tag: 'input',
13649             id : id,
13650             type : this.inputType,
13651             cls : 'form-control x-combo-noedit',
13652             autocomplete: 'new-password',
13653             placeholder : this.placeholder || '',
13654             readonly : true
13655         };
13656         
13657         if (this.name) {
13658             input.name = this.name;
13659         }
13660         
13661         if (this.size) {
13662             input.cls += ' input-' + this.size;
13663         }
13664         
13665         if (this.disabled) {
13666             input.disabled = true;
13667         }
13668         
13669         var inputblock = {
13670             cls : '',
13671             cn : [
13672                 input
13673             ]
13674         };
13675         
13676         if(this.before){
13677             inputblock.cls += ' input-group';
13678             
13679             inputblock.cn.unshift({
13680                 tag :'span',
13681                 cls : 'input-group-addon',
13682                 html : this.before
13683             });
13684         }
13685         
13686         if(this.removable && !this.multiple){
13687             inputblock.cls += ' roo-removable';
13688             
13689             inputblock.cn.push({
13690                 tag: 'button',
13691                 html : 'x',
13692                 cls : 'roo-combo-removable-btn close'
13693             });
13694         }
13695
13696         if(this.hasFeedback && !this.allowBlank){
13697             
13698             inputblock.cls += ' has-feedback';
13699             
13700             inputblock.cn.push({
13701                 tag: 'span',
13702                 cls: 'glyphicon form-control-feedback'
13703             });
13704             
13705         }
13706         
13707         if (this.after) {
13708             
13709             inputblock.cls += (this.before) ? '' : ' input-group';
13710             
13711             inputblock.cn.push({
13712                 tag :'span',
13713                 cls : 'input-group-addon',
13714                 html : this.after
13715             });
13716         }
13717
13718         var box = {
13719             tag: 'div',
13720             cn: [
13721                 {
13722                     tag: 'input',
13723                     type : 'hidden',
13724                     cls: 'form-hidden-field'
13725                 },
13726                 inputblock
13727             ]
13728             
13729         };
13730         
13731         if(this.multiple){
13732             box = {
13733                 tag: 'div',
13734                 cn: [
13735                     {
13736                         tag: 'input',
13737                         type : 'hidden',
13738                         cls: 'form-hidden-field'
13739                     },
13740                     {
13741                         tag: 'ul',
13742                         cls: 'roo-select2-choices',
13743                         cn:[
13744                             {
13745                                 tag: 'li',
13746                                 cls: 'roo-select2-search-field',
13747                                 cn: [
13748
13749                                     inputblock
13750                                 ]
13751                             }
13752                         ]
13753                     }
13754                 ]
13755             }
13756         };
13757         
13758         var combobox = {
13759             cls: 'roo-select2-container input-group',
13760             cn: [
13761                 box
13762             ]
13763         };
13764         
13765         if(this.multiple){
13766             combobox.cls += ' roo-select2-container-multi';
13767         }
13768         
13769         var align = this.labelAlign || this.parentLabelAlign();
13770         
13771         cfg.cn = combobox;
13772         
13773         if(this.fieldLabel.length){
13774             
13775             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13776             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13777             
13778             cfg.cn = [
13779                 {
13780                     tag: 'label',
13781                     cls : 'control-label ' + lw,
13782                     html : this.fieldLabel
13783
13784                 },
13785                 {
13786                     cls : cw, 
13787                     cn: [
13788                         combobox
13789                     ]
13790                 }
13791             ];
13792         }
13793         
13794         var settings = this;
13795         
13796         ['xs','sm','md','lg'].map(function(size){
13797             if (settings[size]) {
13798                 cfg.cls += ' col-' + size + '-' + settings[size];
13799             }
13800         });
13801         
13802         return cfg;
13803     },
13804     
13805     initTouchView : function()
13806     {
13807         this.renderTouchView();
13808         
13809         this.touchViewEl.on('scroll', function(){
13810             this.el.dom.scrollTop = 0;
13811         }, this);
13812         
13813         this.originalValue = this.getValue();
13814         
13815         this.inputEl().on("click", this.showTouchView, this);
13816         
13817         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13818         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13819         
13820         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13821         
13822         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13823         this.store.on('load', this.onTouchViewLoad, this);
13824         this.store.on('loadexception', this.onTouchViewLoadException, this);
13825         
13826         if(this.hiddenName){
13827             
13828             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13829             
13830             this.hiddenField.dom.value =
13831                 this.hiddenValue !== undefined ? this.hiddenValue :
13832                 this.value !== undefined ? this.value : '';
13833         
13834             this.el.dom.removeAttribute('name');
13835             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13836         }
13837         
13838         if(this.multiple){
13839             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13840             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13841         }
13842         
13843         if(this.removable && !this.multiple){
13844             var close = this.closeTriggerEl();
13845             if(close){
13846                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13847                 close.on('click', this.removeBtnClick, this, close);
13848             }
13849         }
13850         /*
13851          * fix the bug in Safari iOS8
13852          */
13853         this.inputEl().on("focus", function(e){
13854             document.activeElement.blur();
13855         }, this);
13856         
13857         return;
13858         
13859         
13860     },
13861     
13862     renderTouchView : function()
13863     {
13864         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13865         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13866         
13867         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13868         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13869         
13870         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13871         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13872         this.touchViewBodyEl.setStyle('overflow', 'auto');
13873         
13874         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13875         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13876         
13877         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13878         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13879         
13880     },
13881     
13882     showTouchView : function()
13883     {
13884         if(this.disabled){
13885             return;
13886         }
13887         
13888         this.touchViewHeaderEl.hide();
13889
13890         if(this.fieldLabel.length){
13891             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13892             this.touchViewHeaderEl.show();
13893         }
13894
13895         this.touchViewEl.show();
13896
13897         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13898         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13899
13900         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13901
13902         if(this.fieldLabel.length){
13903             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13904         }
13905         
13906         this.touchViewBodyEl.setHeight(bodyHeight);
13907
13908         if(this.animate){
13909             var _this = this;
13910             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13911         }else{
13912             this.touchViewEl.addClass('in');
13913         }
13914
13915         this.doTouchViewQuery();
13916         
13917     },
13918     
13919     hideTouchView : function()
13920     {
13921         this.touchViewEl.removeClass('in');
13922
13923         if(this.animate){
13924             var _this = this;
13925             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13926         }else{
13927             this.touchViewEl.setStyle('display', 'none');
13928         }
13929         
13930     },
13931     
13932     setTouchViewValue : function()
13933     {
13934         if(this.multiple){
13935             this.clearItem();
13936         
13937             var _this = this;
13938
13939             Roo.each(this.tickItems, function(o){
13940                 this.addItem(o);
13941             }, this);
13942         }
13943         
13944         this.hideTouchView();
13945     },
13946     
13947     doTouchViewQuery : function()
13948     {
13949         var qe = {
13950             query: '',
13951             forceAll: true,
13952             combo: this,
13953             cancel:false
13954         };
13955         
13956         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13957             return false;
13958         }
13959         
13960         if(!this.alwaysQuery || this.mode == 'local'){
13961             this.onTouchViewLoad();
13962             return;
13963         }
13964         
13965         this.store.load();
13966     },
13967     
13968     onTouchViewBeforeLoad : function(combo,opts)
13969     {
13970         return;
13971     },
13972
13973     // private
13974     onTouchViewLoad : function()
13975     {
13976         if(this.store.getCount() < 1){
13977             this.onTouchViewEmptyResults();
13978             return;
13979         }
13980         
13981         this.clearTouchView();
13982         
13983         var rawValue = this.getRawValue();
13984         
13985         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13986         
13987         this.tickItems = [];
13988         
13989         this.store.data.each(function(d, rowIndex){
13990             var row = this.touchViewListGroup.createChild(template);
13991             
13992             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
13993                 row.addClass(d.data.cls);
13994             }
13995             
13996             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13997                 var cfg = {
13998                     data : d.data,
13999                     html : d.data[this.displayField]
14000                 };
14001                 
14002                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14003                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14004                 }
14005             }
14006             
14007             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
14008                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14009             }
14010             
14011             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
14012                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14013                 this.tickItems.push(d.data);
14014             }
14015             
14016             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14017             
14018         }, this);
14019         
14020         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14021         
14022         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14023
14024         if(this.fieldLabel.length){
14025             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14026         }
14027
14028         var listHeight = this.touchViewListGroup.getHeight();
14029         
14030         var _this = this;
14031         
14032         if(firstChecked && listHeight > bodyHeight){
14033             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14034         }
14035         
14036     },
14037     
14038     onTouchViewLoadException : function()
14039     {
14040         this.hideTouchView();
14041     },
14042     
14043     onTouchViewEmptyResults : function()
14044     {
14045         this.clearTouchView();
14046         
14047         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14048         
14049         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14050         
14051     },
14052     
14053     clearTouchView : function()
14054     {
14055         this.touchViewListGroup.dom.innerHTML = '';
14056     },
14057     
14058     onTouchViewClick : function(e, el, o)
14059     {
14060         e.preventDefault();
14061         
14062         var row = o.row;
14063         var rowIndex = o.rowIndex;
14064         
14065         var r = this.store.getAt(rowIndex);
14066         
14067         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14068             
14069             if(!this.multiple){
14070                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14071                     c.dom.removeAttribute('checked');
14072                 }, this);
14073
14074                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14075
14076                 this.setFromData(r.data);
14077
14078                 var close = this.closeTriggerEl();
14079
14080                 if(close){
14081                     close.show();
14082                 }
14083
14084                 this.hideTouchView();
14085
14086                 this.fireEvent('select', this, r, rowIndex);
14087
14088                 return;
14089             }
14090
14091             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14092                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14093                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14094                 return;
14095             }
14096
14097             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14098             this.addItem(r.data);
14099             this.tickItems.push(r.data);
14100         }
14101     }
14102     
14103
14104     /** 
14105     * @cfg {Boolean} grow 
14106     * @hide 
14107     */
14108     /** 
14109     * @cfg {Number} growMin 
14110     * @hide 
14111     */
14112     /** 
14113     * @cfg {Number} growMax 
14114     * @hide 
14115     */
14116     /**
14117      * @hide
14118      * @method autoSize
14119      */
14120 });
14121
14122 Roo.apply(Roo.bootstrap.ComboBox,  {
14123     
14124     header : {
14125         tag: 'div',
14126         cls: 'modal-header',
14127         cn: [
14128             {
14129                 tag: 'h4',
14130                 cls: 'modal-title'
14131             }
14132         ]
14133     },
14134     
14135     body : {
14136         tag: 'div',
14137         cls: 'modal-body',
14138         cn: [
14139             {
14140                 tag: 'ul',
14141                 cls: 'list-group'
14142             }
14143         ]
14144     },
14145     
14146     listItemRadio : {
14147         tag: 'li',
14148         cls: 'list-group-item',
14149         cn: [
14150             {
14151                 tag: 'span',
14152                 cls: 'roo-combobox-list-group-item-value'
14153             },
14154             {
14155                 tag: 'div',
14156                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14157                 cn: [
14158                     {
14159                         tag: 'input',
14160                         type: 'radio'
14161                     },
14162                     {
14163                         tag: 'label'
14164                     }
14165                 ]
14166             }
14167         ]
14168     },
14169     
14170     listItemCheckbox : {
14171         tag: 'li',
14172         cls: 'list-group-item',
14173         cn: [
14174             {
14175                 tag: 'span',
14176                 cls: 'roo-combobox-list-group-item-value'
14177             },
14178             {
14179                 tag: 'div',
14180                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14181                 cn: [
14182                     {
14183                         tag: 'input',
14184                         type: 'checkbox'
14185                     },
14186                     {
14187                         tag: 'label'
14188                     }
14189                 ]
14190             }
14191         ]
14192     },
14193     
14194     emptyResult : {
14195         tag: 'div',
14196         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14197     },
14198     
14199     footer : {
14200         tag: 'div',
14201         cls: 'modal-footer',
14202         cn: [
14203             {
14204                 tag: 'div',
14205                 cls: 'row',
14206                 cn: [
14207                     {
14208                         tag: 'div',
14209                         cls: 'col-xs-6 text-left',
14210                         cn: {
14211                             tag: 'button',
14212                             cls: 'btn btn-danger roo-touch-view-cancel',
14213                             html: 'Cancel'
14214                         }
14215                     },
14216                     {
14217                         tag: 'div',
14218                         cls: 'col-xs-6 text-right',
14219                         cn: {
14220                             tag: 'button',
14221                             cls: 'btn btn-success roo-touch-view-ok',
14222                             html: 'OK'
14223                         }
14224                     }
14225                 ]
14226             }
14227         ]
14228         
14229     }
14230 });
14231
14232 Roo.apply(Roo.bootstrap.ComboBox,  {
14233     
14234     touchViewTemplate : {
14235         tag: 'div',
14236         cls: 'modal fade roo-combobox-touch-view',
14237         cn: [
14238             {
14239                 tag: 'div',
14240                 cls: 'modal-dialog',
14241                 style : 'position:fixed', // we have to fix position....
14242                 cn: [
14243                     {
14244                         tag: 'div',
14245                         cls: 'modal-content',
14246                         cn: [
14247                             Roo.bootstrap.ComboBox.header,
14248                             Roo.bootstrap.ComboBox.body,
14249                             Roo.bootstrap.ComboBox.footer
14250                         ]
14251                     }
14252                 ]
14253             }
14254         ]
14255     }
14256 });/*
14257  * Based on:
14258  * Ext JS Library 1.1.1
14259  * Copyright(c) 2006-2007, Ext JS, LLC.
14260  *
14261  * Originally Released Under LGPL - original licence link has changed is not relivant.
14262  *
14263  * Fork - LGPL
14264  * <script type="text/javascript">
14265  */
14266
14267 /**
14268  * @class Roo.View
14269  * @extends Roo.util.Observable
14270  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14271  * This class also supports single and multi selection modes. <br>
14272  * Create a data model bound view:
14273  <pre><code>
14274  var store = new Roo.data.Store(...);
14275
14276  var view = new Roo.View({
14277     el : "my-element",
14278     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14279  
14280     singleSelect: true,
14281     selectedClass: "ydataview-selected",
14282     store: store
14283  });
14284
14285  // listen for node click?
14286  view.on("click", function(vw, index, node, e){
14287  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14288  });
14289
14290  // load XML data
14291  dataModel.load("foobar.xml");
14292  </code></pre>
14293  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14294  * <br><br>
14295  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14296  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14297  * 
14298  * Note: old style constructor is still suported (container, template, config)
14299  * 
14300  * @constructor
14301  * Create a new View
14302  * @param {Object} config The config object
14303  * 
14304  */
14305 Roo.View = function(config, depreciated_tpl, depreciated_config){
14306     
14307     this.parent = false;
14308     
14309     if (typeof(depreciated_tpl) == 'undefined') {
14310         // new way.. - universal constructor.
14311         Roo.apply(this, config);
14312         this.el  = Roo.get(this.el);
14313     } else {
14314         // old format..
14315         this.el  = Roo.get(config);
14316         this.tpl = depreciated_tpl;
14317         Roo.apply(this, depreciated_config);
14318     }
14319     this.wrapEl  = this.el.wrap().wrap();
14320     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14321     
14322     
14323     if(typeof(this.tpl) == "string"){
14324         this.tpl = new Roo.Template(this.tpl);
14325     } else {
14326         // support xtype ctors..
14327         this.tpl = new Roo.factory(this.tpl, Roo);
14328     }
14329     
14330     
14331     this.tpl.compile();
14332     
14333     /** @private */
14334     this.addEvents({
14335         /**
14336          * @event beforeclick
14337          * Fires before a click is processed. Returns false to cancel the default action.
14338          * @param {Roo.View} this
14339          * @param {Number} index The index of the target node
14340          * @param {HTMLElement} node The target node
14341          * @param {Roo.EventObject} e The raw event object
14342          */
14343             "beforeclick" : true,
14344         /**
14345          * @event click
14346          * Fires when a template node is clicked.
14347          * @param {Roo.View} this
14348          * @param {Number} index The index of the target node
14349          * @param {HTMLElement} node The target node
14350          * @param {Roo.EventObject} e The raw event object
14351          */
14352             "click" : true,
14353         /**
14354          * @event dblclick
14355          * Fires when a template node is double clicked.
14356          * @param {Roo.View} this
14357          * @param {Number} index The index of the target node
14358          * @param {HTMLElement} node The target node
14359          * @param {Roo.EventObject} e The raw event object
14360          */
14361             "dblclick" : true,
14362         /**
14363          * @event contextmenu
14364          * Fires when a template node is right clicked.
14365          * @param {Roo.View} this
14366          * @param {Number} index The index of the target node
14367          * @param {HTMLElement} node The target node
14368          * @param {Roo.EventObject} e The raw event object
14369          */
14370             "contextmenu" : true,
14371         /**
14372          * @event selectionchange
14373          * Fires when the selected nodes change.
14374          * @param {Roo.View} this
14375          * @param {Array} selections Array of the selected nodes
14376          */
14377             "selectionchange" : true,
14378     
14379         /**
14380          * @event beforeselect
14381          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14382          * @param {Roo.View} this
14383          * @param {HTMLElement} node The node to be selected
14384          * @param {Array} selections Array of currently selected nodes
14385          */
14386             "beforeselect" : true,
14387         /**
14388          * @event preparedata
14389          * Fires on every row to render, to allow you to change the data.
14390          * @param {Roo.View} this
14391          * @param {Object} data to be rendered (change this)
14392          */
14393           "preparedata" : true
14394           
14395           
14396         });
14397
14398
14399
14400     this.el.on({
14401         "click": this.onClick,
14402         "dblclick": this.onDblClick,
14403         "contextmenu": this.onContextMenu,
14404         scope:this
14405     });
14406
14407     this.selections = [];
14408     this.nodes = [];
14409     this.cmp = new Roo.CompositeElementLite([]);
14410     if(this.store){
14411         this.store = Roo.factory(this.store, Roo.data);
14412         this.setStore(this.store, true);
14413     }
14414     
14415     if ( this.footer && this.footer.xtype) {
14416            
14417          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14418         
14419         this.footer.dataSource = this.store;
14420         this.footer.container = fctr;
14421         this.footer = Roo.factory(this.footer, Roo);
14422         fctr.insertFirst(this.el);
14423         
14424         // this is a bit insane - as the paging toolbar seems to detach the el..
14425 //        dom.parentNode.parentNode.parentNode
14426          // they get detached?
14427     }
14428     
14429     
14430     Roo.View.superclass.constructor.call(this);
14431     
14432     
14433 };
14434
14435 Roo.extend(Roo.View, Roo.util.Observable, {
14436     
14437      /**
14438      * @cfg {Roo.data.Store} store Data store to load data from.
14439      */
14440     store : false,
14441     
14442     /**
14443      * @cfg {String|Roo.Element} el The container element.
14444      */
14445     el : '',
14446     
14447     /**
14448      * @cfg {String|Roo.Template} tpl The template used by this View 
14449      */
14450     tpl : false,
14451     /**
14452      * @cfg {String} dataName the named area of the template to use as the data area
14453      *                          Works with domtemplates roo-name="name"
14454      */
14455     dataName: false,
14456     /**
14457      * @cfg {String} selectedClass The css class to add to selected nodes
14458      */
14459     selectedClass : "x-view-selected",
14460      /**
14461      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14462      */
14463     emptyText : "",
14464     
14465     /**
14466      * @cfg {String} text to display on mask (default Loading)
14467      */
14468     mask : false,
14469     /**
14470      * @cfg {Boolean} multiSelect Allow multiple selection
14471      */
14472     multiSelect : false,
14473     /**
14474      * @cfg {Boolean} singleSelect Allow single selection
14475      */
14476     singleSelect:  false,
14477     
14478     /**
14479      * @cfg {Boolean} toggleSelect - selecting 
14480      */
14481     toggleSelect : false,
14482     
14483     /**
14484      * @cfg {Boolean} tickable - selecting 
14485      */
14486     tickable : false,
14487     
14488     /**
14489      * Returns the element this view is bound to.
14490      * @return {Roo.Element}
14491      */
14492     getEl : function(){
14493         return this.wrapEl;
14494     },
14495     
14496     
14497
14498     /**
14499      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14500      */
14501     refresh : function(){
14502         //Roo.log('refresh');
14503         var t = this.tpl;
14504         
14505         // if we are using something like 'domtemplate', then
14506         // the what gets used is:
14507         // t.applySubtemplate(NAME, data, wrapping data..)
14508         // the outer template then get' applied with
14509         //     the store 'extra data'
14510         // and the body get's added to the
14511         //      roo-name="data" node?
14512         //      <span class='roo-tpl-{name}'></span> ?????
14513         
14514         
14515         
14516         this.clearSelections();
14517         this.el.update("");
14518         var html = [];
14519         var records = this.store.getRange();
14520         if(records.length < 1) {
14521             
14522             // is this valid??  = should it render a template??
14523             
14524             this.el.update(this.emptyText);
14525             return;
14526         }
14527         var el = this.el;
14528         if (this.dataName) {
14529             this.el.update(t.apply(this.store.meta)); //????
14530             el = this.el.child('.roo-tpl-' + this.dataName);
14531         }
14532         
14533         for(var i = 0, len = records.length; i < len; i++){
14534             var data = this.prepareData(records[i].data, i, records[i]);
14535             this.fireEvent("preparedata", this, data, i, records[i]);
14536             
14537             var d = Roo.apply({}, data);
14538             
14539             if(this.tickable){
14540                 Roo.apply(d, {'roo-id' : Roo.id()});
14541                 
14542                 var _this = this;
14543             
14544                 Roo.each(this.parent.item, function(item){
14545                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14546                         return;
14547                     }
14548                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14549                 });
14550             }
14551             
14552             html[html.length] = Roo.util.Format.trim(
14553                 this.dataName ?
14554                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14555                     t.apply(d)
14556             );
14557         }
14558         
14559         
14560         
14561         el.update(html.join(""));
14562         this.nodes = el.dom.childNodes;
14563         this.updateIndexes(0);
14564     },
14565     
14566
14567     /**
14568      * Function to override to reformat the data that is sent to
14569      * the template for each node.
14570      * DEPRICATED - use the preparedata event handler.
14571      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14572      * a JSON object for an UpdateManager bound view).
14573      */
14574     prepareData : function(data, index, record)
14575     {
14576         this.fireEvent("preparedata", this, data, index, record);
14577         return data;
14578     },
14579
14580     onUpdate : function(ds, record){
14581         // Roo.log('on update');   
14582         this.clearSelections();
14583         var index = this.store.indexOf(record);
14584         var n = this.nodes[index];
14585         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14586         n.parentNode.removeChild(n);
14587         this.updateIndexes(index, index);
14588     },
14589
14590     
14591     
14592 // --------- FIXME     
14593     onAdd : function(ds, records, index)
14594     {
14595         //Roo.log(['on Add', ds, records, index] );        
14596         this.clearSelections();
14597         if(this.nodes.length == 0){
14598             this.refresh();
14599             return;
14600         }
14601         var n = this.nodes[index];
14602         for(var i = 0, len = records.length; i < len; i++){
14603             var d = this.prepareData(records[i].data, i, records[i]);
14604             if(n){
14605                 this.tpl.insertBefore(n, d);
14606             }else{
14607                 
14608                 this.tpl.append(this.el, d);
14609             }
14610         }
14611         this.updateIndexes(index);
14612     },
14613
14614     onRemove : function(ds, record, index){
14615        // Roo.log('onRemove');
14616         this.clearSelections();
14617         var el = this.dataName  ?
14618             this.el.child('.roo-tpl-' + this.dataName) :
14619             this.el; 
14620         
14621         el.dom.removeChild(this.nodes[index]);
14622         this.updateIndexes(index);
14623     },
14624
14625     /**
14626      * Refresh an individual node.
14627      * @param {Number} index
14628      */
14629     refreshNode : function(index){
14630         this.onUpdate(this.store, this.store.getAt(index));
14631     },
14632
14633     updateIndexes : function(startIndex, endIndex){
14634         var ns = this.nodes;
14635         startIndex = startIndex || 0;
14636         endIndex = endIndex || ns.length - 1;
14637         for(var i = startIndex; i <= endIndex; i++){
14638             ns[i].nodeIndex = i;
14639         }
14640     },
14641
14642     /**
14643      * Changes the data store this view uses and refresh the view.
14644      * @param {Store} store
14645      */
14646     setStore : function(store, initial){
14647         if(!initial && this.store){
14648             this.store.un("datachanged", this.refresh);
14649             this.store.un("add", this.onAdd);
14650             this.store.un("remove", this.onRemove);
14651             this.store.un("update", this.onUpdate);
14652             this.store.un("clear", this.refresh);
14653             this.store.un("beforeload", this.onBeforeLoad);
14654             this.store.un("load", this.onLoad);
14655             this.store.un("loadexception", this.onLoad);
14656         }
14657         if(store){
14658           
14659             store.on("datachanged", this.refresh, this);
14660             store.on("add", this.onAdd, this);
14661             store.on("remove", this.onRemove, this);
14662             store.on("update", this.onUpdate, this);
14663             store.on("clear", this.refresh, this);
14664             store.on("beforeload", this.onBeforeLoad, this);
14665             store.on("load", this.onLoad, this);
14666             store.on("loadexception", this.onLoad, this);
14667         }
14668         
14669         if(store){
14670             this.refresh();
14671         }
14672     },
14673     /**
14674      * onbeforeLoad - masks the loading area.
14675      *
14676      */
14677     onBeforeLoad : function(store,opts)
14678     {
14679          //Roo.log('onBeforeLoad');   
14680         if (!opts.add) {
14681             this.el.update("");
14682         }
14683         this.el.mask(this.mask ? this.mask : "Loading" ); 
14684     },
14685     onLoad : function ()
14686     {
14687         this.el.unmask();
14688     },
14689     
14690
14691     /**
14692      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14693      * @param {HTMLElement} node
14694      * @return {HTMLElement} The template node
14695      */
14696     findItemFromChild : function(node){
14697         var el = this.dataName  ?
14698             this.el.child('.roo-tpl-' + this.dataName,true) :
14699             this.el.dom; 
14700         
14701         if(!node || node.parentNode == el){
14702                     return node;
14703             }
14704             var p = node.parentNode;
14705             while(p && p != el){
14706             if(p.parentNode == el){
14707                 return p;
14708             }
14709             p = p.parentNode;
14710         }
14711             return null;
14712     },
14713
14714     /** @ignore */
14715     onClick : function(e){
14716         var item = this.findItemFromChild(e.getTarget());
14717         if(item){
14718             var index = this.indexOf(item);
14719             if(this.onItemClick(item, index, e) !== false){
14720                 this.fireEvent("click", this, index, item, e);
14721             }
14722         }else{
14723             this.clearSelections();
14724         }
14725     },
14726
14727     /** @ignore */
14728     onContextMenu : function(e){
14729         var item = this.findItemFromChild(e.getTarget());
14730         if(item){
14731             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14732         }
14733     },
14734
14735     /** @ignore */
14736     onDblClick : function(e){
14737         var item = this.findItemFromChild(e.getTarget());
14738         if(item){
14739             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14740         }
14741     },
14742
14743     onItemClick : function(item, index, e)
14744     {
14745         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14746             return false;
14747         }
14748         if (this.toggleSelect) {
14749             var m = this.isSelected(item) ? 'unselect' : 'select';
14750             //Roo.log(m);
14751             var _t = this;
14752             _t[m](item, true, false);
14753             return true;
14754         }
14755         if(this.multiSelect || this.singleSelect){
14756             if(this.multiSelect && e.shiftKey && this.lastSelection){
14757                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14758             }else{
14759                 this.select(item, this.multiSelect && e.ctrlKey);
14760                 this.lastSelection = item;
14761             }
14762             
14763             if(!this.tickable){
14764                 e.preventDefault();
14765             }
14766             
14767         }
14768         return true;
14769     },
14770
14771     /**
14772      * Get the number of selected nodes.
14773      * @return {Number}
14774      */
14775     getSelectionCount : function(){
14776         return this.selections.length;
14777     },
14778
14779     /**
14780      * Get the currently selected nodes.
14781      * @return {Array} An array of HTMLElements
14782      */
14783     getSelectedNodes : function(){
14784         return this.selections;
14785     },
14786
14787     /**
14788      * Get the indexes of the selected nodes.
14789      * @return {Array}
14790      */
14791     getSelectedIndexes : function(){
14792         var indexes = [], s = this.selections;
14793         for(var i = 0, len = s.length; i < len; i++){
14794             indexes.push(s[i].nodeIndex);
14795         }
14796         return indexes;
14797     },
14798
14799     /**
14800      * Clear all selections
14801      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14802      */
14803     clearSelections : function(suppressEvent){
14804         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14805             this.cmp.elements = this.selections;
14806             this.cmp.removeClass(this.selectedClass);
14807             this.selections = [];
14808             if(!suppressEvent){
14809                 this.fireEvent("selectionchange", this, this.selections);
14810             }
14811         }
14812     },
14813
14814     /**
14815      * Returns true if the passed node is selected
14816      * @param {HTMLElement/Number} node The node or node index
14817      * @return {Boolean}
14818      */
14819     isSelected : function(node){
14820         var s = this.selections;
14821         if(s.length < 1){
14822             return false;
14823         }
14824         node = this.getNode(node);
14825         return s.indexOf(node) !== -1;
14826     },
14827
14828     /**
14829      * Selects nodes.
14830      * @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
14831      * @param {Boolean} keepExisting (optional) true to keep existing selections
14832      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14833      */
14834     select : function(nodeInfo, keepExisting, suppressEvent){
14835         if(nodeInfo instanceof Array){
14836             if(!keepExisting){
14837                 this.clearSelections(true);
14838             }
14839             for(var i = 0, len = nodeInfo.length; i < len; i++){
14840                 this.select(nodeInfo[i], true, true);
14841             }
14842             return;
14843         } 
14844         var node = this.getNode(nodeInfo);
14845         if(!node || this.isSelected(node)){
14846             return; // already selected.
14847         }
14848         if(!keepExisting){
14849             this.clearSelections(true);
14850         }
14851         
14852         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14853             Roo.fly(node).addClass(this.selectedClass);
14854             this.selections.push(node);
14855             if(!suppressEvent){
14856                 this.fireEvent("selectionchange", this, this.selections);
14857             }
14858         }
14859         
14860         
14861     },
14862       /**
14863      * Unselects nodes.
14864      * @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
14865      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14866      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14867      */
14868     unselect : function(nodeInfo, keepExisting, suppressEvent)
14869     {
14870         if(nodeInfo instanceof Array){
14871             Roo.each(this.selections, function(s) {
14872                 this.unselect(s, nodeInfo);
14873             }, this);
14874             return;
14875         }
14876         var node = this.getNode(nodeInfo);
14877         if(!node || !this.isSelected(node)){
14878             //Roo.log("not selected");
14879             return; // not selected.
14880         }
14881         // fireevent???
14882         var ns = [];
14883         Roo.each(this.selections, function(s) {
14884             if (s == node ) {
14885                 Roo.fly(node).removeClass(this.selectedClass);
14886
14887                 return;
14888             }
14889             ns.push(s);
14890         },this);
14891         
14892         this.selections= ns;
14893         this.fireEvent("selectionchange", this, this.selections);
14894     },
14895
14896     /**
14897      * Gets a template node.
14898      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14899      * @return {HTMLElement} The node or null if it wasn't found
14900      */
14901     getNode : function(nodeInfo){
14902         if(typeof nodeInfo == "string"){
14903             return document.getElementById(nodeInfo);
14904         }else if(typeof nodeInfo == "number"){
14905             return this.nodes[nodeInfo];
14906         }
14907         return nodeInfo;
14908     },
14909
14910     /**
14911      * Gets a range template nodes.
14912      * @param {Number} startIndex
14913      * @param {Number} endIndex
14914      * @return {Array} An array of nodes
14915      */
14916     getNodes : function(start, end){
14917         var ns = this.nodes;
14918         start = start || 0;
14919         end = typeof end == "undefined" ? ns.length - 1 : end;
14920         var nodes = [];
14921         if(start <= end){
14922             for(var i = start; i <= end; i++){
14923                 nodes.push(ns[i]);
14924             }
14925         } else{
14926             for(var i = start; i >= end; i--){
14927                 nodes.push(ns[i]);
14928             }
14929         }
14930         return nodes;
14931     },
14932
14933     /**
14934      * Finds the index of the passed node
14935      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14936      * @return {Number} The index of the node or -1
14937      */
14938     indexOf : function(node){
14939         node = this.getNode(node);
14940         if(typeof node.nodeIndex == "number"){
14941             return node.nodeIndex;
14942         }
14943         var ns = this.nodes;
14944         for(var i = 0, len = ns.length; i < len; i++){
14945             if(ns[i] == node){
14946                 return i;
14947             }
14948         }
14949         return -1;
14950     }
14951 });
14952 /*
14953  * - LGPL
14954  *
14955  * based on jquery fullcalendar
14956  * 
14957  */
14958
14959 Roo.bootstrap = Roo.bootstrap || {};
14960 /**
14961  * @class Roo.bootstrap.Calendar
14962  * @extends Roo.bootstrap.Component
14963  * Bootstrap Calendar class
14964  * @cfg {Boolean} loadMask (true|false) default false
14965  * @cfg {Object} header generate the user specific header of the calendar, default false
14966
14967  * @constructor
14968  * Create a new Container
14969  * @param {Object} config The config object
14970  */
14971
14972
14973
14974 Roo.bootstrap.Calendar = function(config){
14975     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14976      this.addEvents({
14977         /**
14978              * @event select
14979              * Fires when a date is selected
14980              * @param {DatePicker} this
14981              * @param {Date} date The selected date
14982              */
14983         'select': true,
14984         /**
14985              * @event monthchange
14986              * Fires when the displayed month changes 
14987              * @param {DatePicker} this
14988              * @param {Date} date The selected month
14989              */
14990         'monthchange': true,
14991         /**
14992              * @event evententer
14993              * Fires when mouse over an event
14994              * @param {Calendar} this
14995              * @param {event} Event
14996              */
14997         'evententer': true,
14998         /**
14999              * @event eventleave
15000              * Fires when the mouse leaves an
15001              * @param {Calendar} this
15002              * @param {event}
15003              */
15004         'eventleave': true,
15005         /**
15006              * @event eventclick
15007              * Fires when the mouse click an
15008              * @param {Calendar} this
15009              * @param {event}
15010              */
15011         'eventclick': true
15012         
15013     });
15014
15015 };
15016
15017 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15018     
15019      /**
15020      * @cfg {Number} startDay
15021      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15022      */
15023     startDay : 0,
15024     
15025     loadMask : false,
15026     
15027     header : false,
15028       
15029     getAutoCreate : function(){
15030         
15031         
15032         var fc_button = function(name, corner, style, content ) {
15033             return Roo.apply({},{
15034                 tag : 'span',
15035                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15036                          (corner.length ?
15037                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15038                             ''
15039                         ),
15040                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15041                 unselectable: 'on'
15042             });
15043         };
15044         
15045         var header = {};
15046         
15047         if(!this.header){
15048             header = {
15049                 tag : 'table',
15050                 cls : 'fc-header',
15051                 style : 'width:100%',
15052                 cn : [
15053                     {
15054                         tag: 'tr',
15055                         cn : [
15056                             {
15057                                 tag : 'td',
15058                                 cls : 'fc-header-left',
15059                                 cn : [
15060                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15061                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15062                                     { tag: 'span', cls: 'fc-header-space' },
15063                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15064
15065
15066                                 ]
15067                             },
15068
15069                             {
15070                                 tag : 'td',
15071                                 cls : 'fc-header-center',
15072                                 cn : [
15073                                     {
15074                                         tag: 'span',
15075                                         cls: 'fc-header-title',
15076                                         cn : {
15077                                             tag: 'H2',
15078                                             html : 'month / year'
15079                                         }
15080                                     }
15081
15082                                 ]
15083                             },
15084                             {
15085                                 tag : 'td',
15086                                 cls : 'fc-header-right',
15087                                 cn : [
15088                               /*      fc_button('month', 'left', '', 'month' ),
15089                                     fc_button('week', '', '', 'week' ),
15090                                     fc_button('day', 'right', '', 'day' )
15091                                 */    
15092
15093                                 ]
15094                             }
15095
15096                         ]
15097                     }
15098                 ]
15099             };
15100         }
15101         
15102         header = this.header;
15103         
15104        
15105         var cal_heads = function() {
15106             var ret = [];
15107             // fixme - handle this.
15108             
15109             for (var i =0; i < Date.dayNames.length; i++) {
15110                 var d = Date.dayNames[i];
15111                 ret.push({
15112                     tag: 'th',
15113                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15114                     html : d.substring(0,3)
15115                 });
15116                 
15117             }
15118             ret[0].cls += ' fc-first';
15119             ret[6].cls += ' fc-last';
15120             return ret;
15121         };
15122         var cal_cell = function(n) {
15123             return  {
15124                 tag: 'td',
15125                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15126                 cn : [
15127                     {
15128                         cn : [
15129                             {
15130                                 cls: 'fc-day-number',
15131                                 html: 'D'
15132                             },
15133                             {
15134                                 cls: 'fc-day-content',
15135                              
15136                                 cn : [
15137                                      {
15138                                         style: 'position: relative;' // height: 17px;
15139                                     }
15140                                 ]
15141                             }
15142                             
15143                             
15144                         ]
15145                     }
15146                 ]
15147                 
15148             }
15149         };
15150         var cal_rows = function() {
15151             
15152             var ret = [];
15153             for (var r = 0; r < 6; r++) {
15154                 var row= {
15155                     tag : 'tr',
15156                     cls : 'fc-week',
15157                     cn : []
15158                 };
15159                 
15160                 for (var i =0; i < Date.dayNames.length; i++) {
15161                     var d = Date.dayNames[i];
15162                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15163
15164                 }
15165                 row.cn[0].cls+=' fc-first';
15166                 row.cn[0].cn[0].style = 'min-height:90px';
15167                 row.cn[6].cls+=' fc-last';
15168                 ret.push(row);
15169                 
15170             }
15171             ret[0].cls += ' fc-first';
15172             ret[4].cls += ' fc-prev-last';
15173             ret[5].cls += ' fc-last';
15174             return ret;
15175             
15176         };
15177         
15178         var cal_table = {
15179             tag: 'table',
15180             cls: 'fc-border-separate',
15181             style : 'width:100%',
15182             cellspacing  : 0,
15183             cn : [
15184                 { 
15185                     tag: 'thead',
15186                     cn : [
15187                         { 
15188                             tag: 'tr',
15189                             cls : 'fc-first fc-last',
15190                             cn : cal_heads()
15191                         }
15192                     ]
15193                 },
15194                 { 
15195                     tag: 'tbody',
15196                     cn : cal_rows()
15197                 }
15198                   
15199             ]
15200         };
15201          
15202          var cfg = {
15203             cls : 'fc fc-ltr',
15204             cn : [
15205                 header,
15206                 {
15207                     cls : 'fc-content',
15208                     style : "position: relative;",
15209                     cn : [
15210                         {
15211                             cls : 'fc-view fc-view-month fc-grid',
15212                             style : 'position: relative',
15213                             unselectable : 'on',
15214                             cn : [
15215                                 {
15216                                     cls : 'fc-event-container',
15217                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15218                                 },
15219                                 cal_table
15220                             ]
15221                         }
15222                     ]
15223     
15224                 }
15225            ] 
15226             
15227         };
15228         
15229          
15230         
15231         return cfg;
15232     },
15233     
15234     
15235     initEvents : function()
15236     {
15237         if(!this.store){
15238             throw "can not find store for calendar";
15239         }
15240         
15241         var mark = {
15242             tag: "div",
15243             cls:"x-dlg-mask",
15244             style: "text-align:center",
15245             cn: [
15246                 {
15247                     tag: "div",
15248                     style: "background-color:white;width:50%;margin:250 auto",
15249                     cn: [
15250                         {
15251                             tag: "img",
15252                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15253                         },
15254                         {
15255                             tag: "span",
15256                             html: "Loading"
15257                         }
15258                         
15259                     ]
15260                 }
15261             ]
15262         };
15263         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15264         
15265         var size = this.el.select('.fc-content', true).first().getSize();
15266         this.maskEl.setSize(size.width, size.height);
15267         this.maskEl.enableDisplayMode("block");
15268         if(!this.loadMask){
15269             this.maskEl.hide();
15270         }
15271         
15272         this.store = Roo.factory(this.store, Roo.data);
15273         this.store.on('load', this.onLoad, this);
15274         this.store.on('beforeload', this.onBeforeLoad, this);
15275         
15276         this.resize();
15277         
15278         this.cells = this.el.select('.fc-day',true);
15279         //Roo.log(this.cells);
15280         this.textNodes = this.el.query('.fc-day-number');
15281         this.cells.addClassOnOver('fc-state-hover');
15282         
15283         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15284         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15285         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15286         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15287         
15288         this.on('monthchange', this.onMonthChange, this);
15289         
15290         this.update(new Date().clearTime());
15291     },
15292     
15293     resize : function() {
15294         var sz  = this.el.getSize();
15295         
15296         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15297         this.el.select('.fc-day-content div',true).setHeight(34);
15298     },
15299     
15300     
15301     // private
15302     showPrevMonth : function(e){
15303         this.update(this.activeDate.add("mo", -1));
15304     },
15305     showToday : function(e){
15306         this.update(new Date().clearTime());
15307     },
15308     // private
15309     showNextMonth : function(e){
15310         this.update(this.activeDate.add("mo", 1));
15311     },
15312
15313     // private
15314     showPrevYear : function(){
15315         this.update(this.activeDate.add("y", -1));
15316     },
15317
15318     // private
15319     showNextYear : function(){
15320         this.update(this.activeDate.add("y", 1));
15321     },
15322
15323     
15324    // private
15325     update : function(date)
15326     {
15327         var vd = this.activeDate;
15328         this.activeDate = date;
15329 //        if(vd && this.el){
15330 //            var t = date.getTime();
15331 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15332 //                Roo.log('using add remove');
15333 //                
15334 //                this.fireEvent('monthchange', this, date);
15335 //                
15336 //                this.cells.removeClass("fc-state-highlight");
15337 //                this.cells.each(function(c){
15338 //                   if(c.dateValue == t){
15339 //                       c.addClass("fc-state-highlight");
15340 //                       setTimeout(function(){
15341 //                            try{c.dom.firstChild.focus();}catch(e){}
15342 //                       }, 50);
15343 //                       return false;
15344 //                   }
15345 //                   return true;
15346 //                });
15347 //                return;
15348 //            }
15349 //        }
15350         
15351         var days = date.getDaysInMonth();
15352         
15353         var firstOfMonth = date.getFirstDateOfMonth();
15354         var startingPos = firstOfMonth.getDay()-this.startDay;
15355         
15356         if(startingPos < this.startDay){
15357             startingPos += 7;
15358         }
15359         
15360         var pm = date.add(Date.MONTH, -1);
15361         var prevStart = pm.getDaysInMonth()-startingPos;
15362 //        
15363         this.cells = this.el.select('.fc-day',true);
15364         this.textNodes = this.el.query('.fc-day-number');
15365         this.cells.addClassOnOver('fc-state-hover');
15366         
15367         var cells = this.cells.elements;
15368         var textEls = this.textNodes;
15369         
15370         Roo.each(cells, function(cell){
15371             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15372         });
15373         
15374         days += startingPos;
15375
15376         // convert everything to numbers so it's fast
15377         var day = 86400000;
15378         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15379         //Roo.log(d);
15380         //Roo.log(pm);
15381         //Roo.log(prevStart);
15382         
15383         var today = new Date().clearTime().getTime();
15384         var sel = date.clearTime().getTime();
15385         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15386         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15387         var ddMatch = this.disabledDatesRE;
15388         var ddText = this.disabledDatesText;
15389         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15390         var ddaysText = this.disabledDaysText;
15391         var format = this.format;
15392         
15393         var setCellClass = function(cal, cell){
15394             cell.row = 0;
15395             cell.events = [];
15396             cell.more = [];
15397             //Roo.log('set Cell Class');
15398             cell.title = "";
15399             var t = d.getTime();
15400             
15401             //Roo.log(d);
15402             
15403             cell.dateValue = t;
15404             if(t == today){
15405                 cell.className += " fc-today";
15406                 cell.className += " fc-state-highlight";
15407                 cell.title = cal.todayText;
15408             }
15409             if(t == sel){
15410                 // disable highlight in other month..
15411                 //cell.className += " fc-state-highlight";
15412                 
15413             }
15414             // disabling
15415             if(t < min) {
15416                 cell.className = " fc-state-disabled";
15417                 cell.title = cal.minText;
15418                 return;
15419             }
15420             if(t > max) {
15421                 cell.className = " fc-state-disabled";
15422                 cell.title = cal.maxText;
15423                 return;
15424             }
15425             if(ddays){
15426                 if(ddays.indexOf(d.getDay()) != -1){
15427                     cell.title = ddaysText;
15428                     cell.className = " fc-state-disabled";
15429                 }
15430             }
15431             if(ddMatch && format){
15432                 var fvalue = d.dateFormat(format);
15433                 if(ddMatch.test(fvalue)){
15434                     cell.title = ddText.replace("%0", fvalue);
15435                     cell.className = " fc-state-disabled";
15436                 }
15437             }
15438             
15439             if (!cell.initialClassName) {
15440                 cell.initialClassName = cell.dom.className;
15441             }
15442             
15443             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15444         };
15445
15446         var i = 0;
15447         
15448         for(; i < startingPos; i++) {
15449             textEls[i].innerHTML = (++prevStart);
15450             d.setDate(d.getDate()+1);
15451             
15452             cells[i].className = "fc-past fc-other-month";
15453             setCellClass(this, cells[i]);
15454         }
15455         
15456         var intDay = 0;
15457         
15458         for(; i < days; i++){
15459             intDay = i - startingPos + 1;
15460             textEls[i].innerHTML = (intDay);
15461             d.setDate(d.getDate()+1);
15462             
15463             cells[i].className = ''; // "x-date-active";
15464             setCellClass(this, cells[i]);
15465         }
15466         var extraDays = 0;
15467         
15468         for(; i < 42; i++) {
15469             textEls[i].innerHTML = (++extraDays);
15470             d.setDate(d.getDate()+1);
15471             
15472             cells[i].className = "fc-future fc-other-month";
15473             setCellClass(this, cells[i]);
15474         }
15475         
15476         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15477         
15478         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15479         
15480         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15481         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15482         
15483         if(totalRows != 6){
15484             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15485             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15486         }
15487         
15488         this.fireEvent('monthchange', this, date);
15489         
15490         
15491         /*
15492         if(!this.internalRender){
15493             var main = this.el.dom.firstChild;
15494             var w = main.offsetWidth;
15495             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15496             Roo.fly(main).setWidth(w);
15497             this.internalRender = true;
15498             // opera does not respect the auto grow header center column
15499             // then, after it gets a width opera refuses to recalculate
15500             // without a second pass
15501             if(Roo.isOpera && !this.secondPass){
15502                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15503                 this.secondPass = true;
15504                 this.update.defer(10, this, [date]);
15505             }
15506         }
15507         */
15508         
15509     },
15510     
15511     findCell : function(dt) {
15512         dt = dt.clearTime().getTime();
15513         var ret = false;
15514         this.cells.each(function(c){
15515             //Roo.log("check " +c.dateValue + '?=' + dt);
15516             if(c.dateValue == dt){
15517                 ret = c;
15518                 return false;
15519             }
15520             return true;
15521         });
15522         
15523         return ret;
15524     },
15525     
15526     findCells : function(ev) {
15527         var s = ev.start.clone().clearTime().getTime();
15528        // Roo.log(s);
15529         var e= ev.end.clone().clearTime().getTime();
15530        // Roo.log(e);
15531         var ret = [];
15532         this.cells.each(function(c){
15533              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15534             
15535             if(c.dateValue > e){
15536                 return ;
15537             }
15538             if(c.dateValue < s){
15539                 return ;
15540             }
15541             ret.push(c);
15542         });
15543         
15544         return ret;    
15545     },
15546     
15547 //    findBestRow: function(cells)
15548 //    {
15549 //        var ret = 0;
15550 //        
15551 //        for (var i =0 ; i < cells.length;i++) {
15552 //            ret  = Math.max(cells[i].rows || 0,ret);
15553 //        }
15554 //        return ret;
15555 //        
15556 //    },
15557     
15558     
15559     addItem : function(ev)
15560     {
15561         // look for vertical location slot in
15562         var cells = this.findCells(ev);
15563         
15564 //        ev.row = this.findBestRow(cells);
15565         
15566         // work out the location.
15567         
15568         var crow = false;
15569         var rows = [];
15570         for(var i =0; i < cells.length; i++) {
15571             
15572             cells[i].row = cells[0].row;
15573             
15574             if(i == 0){
15575                 cells[i].row = cells[i].row + 1;
15576             }
15577             
15578             if (!crow) {
15579                 crow = {
15580                     start : cells[i],
15581                     end :  cells[i]
15582                 };
15583                 continue;
15584             }
15585             if (crow.start.getY() == cells[i].getY()) {
15586                 // on same row.
15587                 crow.end = cells[i];
15588                 continue;
15589             }
15590             // different row.
15591             rows.push(crow);
15592             crow = {
15593                 start: cells[i],
15594                 end : cells[i]
15595             };
15596             
15597         }
15598         
15599         rows.push(crow);
15600         ev.els = [];
15601         ev.rows = rows;
15602         ev.cells = cells;
15603         
15604         cells[0].events.push(ev);
15605         
15606         this.calevents.push(ev);
15607     },
15608     
15609     clearEvents: function() {
15610         
15611         if(!this.calevents){
15612             return;
15613         }
15614         
15615         Roo.each(this.cells.elements, function(c){
15616             c.row = 0;
15617             c.events = [];
15618             c.more = [];
15619         });
15620         
15621         Roo.each(this.calevents, function(e) {
15622             Roo.each(e.els, function(el) {
15623                 el.un('mouseenter' ,this.onEventEnter, this);
15624                 el.un('mouseleave' ,this.onEventLeave, this);
15625                 el.remove();
15626             },this);
15627         },this);
15628         
15629         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15630             e.remove();
15631         });
15632         
15633     },
15634     
15635     renderEvents: function()
15636     {   
15637         var _this = this;
15638         
15639         this.cells.each(function(c) {
15640             
15641             if(c.row < 5){
15642                 return;
15643             }
15644             
15645             var ev = c.events;
15646             
15647             var r = 4;
15648             if(c.row != c.events.length){
15649                 r = 4 - (4 - (c.row - c.events.length));
15650             }
15651             
15652             c.events = ev.slice(0, r);
15653             c.more = ev.slice(r);
15654             
15655             if(c.more.length && c.more.length == 1){
15656                 c.events.push(c.more.pop());
15657             }
15658             
15659             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15660             
15661         });
15662             
15663         this.cells.each(function(c) {
15664             
15665             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15666             
15667             
15668             for (var e = 0; e < c.events.length; e++){
15669                 var ev = c.events[e];
15670                 var rows = ev.rows;
15671                 
15672                 for(var i = 0; i < rows.length; i++) {
15673                 
15674                     // how many rows should it span..
15675
15676                     var  cfg = {
15677                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15678                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15679
15680                         unselectable : "on",
15681                         cn : [
15682                             {
15683                                 cls: 'fc-event-inner',
15684                                 cn : [
15685     //                                {
15686     //                                  tag:'span',
15687     //                                  cls: 'fc-event-time',
15688     //                                  html : cells.length > 1 ? '' : ev.time
15689     //                                },
15690                                     {
15691                                       tag:'span',
15692                                       cls: 'fc-event-title',
15693                                       html : String.format('{0}', ev.title)
15694                                     }
15695
15696
15697                                 ]
15698                             },
15699                             {
15700                                 cls: 'ui-resizable-handle ui-resizable-e',
15701                                 html : '&nbsp;&nbsp;&nbsp'
15702                             }
15703
15704                         ]
15705                     };
15706
15707                     if (i == 0) {
15708                         cfg.cls += ' fc-event-start';
15709                     }
15710                     if ((i+1) == rows.length) {
15711                         cfg.cls += ' fc-event-end';
15712                     }
15713
15714                     var ctr = _this.el.select('.fc-event-container',true).first();
15715                     var cg = ctr.createChild(cfg);
15716
15717                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15718                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15719
15720                     var r = (c.more.length) ? 1 : 0;
15721                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15722                     cg.setWidth(ebox.right - sbox.x -2);
15723
15724                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15725                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15726                     cg.on('click', _this.onEventClick, _this, ev);
15727
15728                     ev.els.push(cg);
15729                     
15730                 }
15731                 
15732             }
15733             
15734             
15735             if(c.more.length){
15736                 var  cfg = {
15737                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15738                     style : 'position: absolute',
15739                     unselectable : "on",
15740                     cn : [
15741                         {
15742                             cls: 'fc-event-inner',
15743                             cn : [
15744                                 {
15745                                   tag:'span',
15746                                   cls: 'fc-event-title',
15747                                   html : 'More'
15748                                 }
15749
15750
15751                             ]
15752                         },
15753                         {
15754                             cls: 'ui-resizable-handle ui-resizable-e',
15755                             html : '&nbsp;&nbsp;&nbsp'
15756                         }
15757
15758                     ]
15759                 };
15760
15761                 var ctr = _this.el.select('.fc-event-container',true).first();
15762                 var cg = ctr.createChild(cfg);
15763
15764                 var sbox = c.select('.fc-day-content',true).first().getBox();
15765                 var ebox = c.select('.fc-day-content',true).first().getBox();
15766                 //Roo.log(cg);
15767                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15768                 cg.setWidth(ebox.right - sbox.x -2);
15769
15770                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15771                 
15772             }
15773             
15774         });
15775         
15776         
15777         
15778     },
15779     
15780     onEventEnter: function (e, el,event,d) {
15781         this.fireEvent('evententer', this, el, event);
15782     },
15783     
15784     onEventLeave: function (e, el,event,d) {
15785         this.fireEvent('eventleave', this, el, event);
15786     },
15787     
15788     onEventClick: function (e, el,event,d) {
15789         this.fireEvent('eventclick', this, el, event);
15790     },
15791     
15792     onMonthChange: function () {
15793         this.store.load();
15794     },
15795     
15796     onMoreEventClick: function(e, el, more)
15797     {
15798         var _this = this;
15799         
15800         this.calpopover.placement = 'right';
15801         this.calpopover.setTitle('More');
15802         
15803         this.calpopover.setContent('');
15804         
15805         var ctr = this.calpopover.el.select('.popover-content', true).first();
15806         
15807         Roo.each(more, function(m){
15808             var cfg = {
15809                 cls : 'fc-event-hori fc-event-draggable',
15810                 html : m.title
15811             };
15812             var cg = ctr.createChild(cfg);
15813             
15814             cg.on('click', _this.onEventClick, _this, m);
15815         });
15816         
15817         this.calpopover.show(el);
15818         
15819         
15820     },
15821     
15822     onLoad: function () 
15823     {   
15824         this.calevents = [];
15825         var cal = this;
15826         
15827         if(this.store.getCount() > 0){
15828             this.store.data.each(function(d){
15829                cal.addItem({
15830                     id : d.data.id,
15831                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15832                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15833                     time : d.data.start_time,
15834                     title : d.data.title,
15835                     description : d.data.description,
15836                     venue : d.data.venue
15837                 });
15838             });
15839         }
15840         
15841         this.renderEvents();
15842         
15843         if(this.calevents.length && this.loadMask){
15844             this.maskEl.hide();
15845         }
15846     },
15847     
15848     onBeforeLoad: function()
15849     {
15850         this.clearEvents();
15851         if(this.loadMask){
15852             this.maskEl.show();
15853         }
15854     }
15855 });
15856
15857  
15858  /*
15859  * - LGPL
15860  *
15861  * element
15862  * 
15863  */
15864
15865 /**
15866  * @class Roo.bootstrap.Popover
15867  * @extends Roo.bootstrap.Component
15868  * Bootstrap Popover class
15869  * @cfg {String} html contents of the popover   (or false to use children..)
15870  * @cfg {String} title of popover (or false to hide)
15871  * @cfg {String} placement how it is placed
15872  * @cfg {String} trigger click || hover (or false to trigger manually)
15873  * @cfg {String} over what (parent or false to trigger manually.)
15874  * @cfg {Number} delay - delay before showing
15875  
15876  * @constructor
15877  * Create a new Popover
15878  * @param {Object} config The config object
15879  */
15880
15881 Roo.bootstrap.Popover = function(config){
15882     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15883     
15884     this.addEvents({
15885         // raw events
15886          /**
15887          * @event show
15888          * After the popover show
15889          * 
15890          * @param {Roo.bootstrap.Popover} this
15891          */
15892         "show" : true,
15893         /**
15894          * @event hide
15895          * After the popover hide
15896          * 
15897          * @param {Roo.bootstrap.Popover} this
15898          */
15899         "hide" : true
15900     });
15901 };
15902
15903 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15904     
15905     title: 'Fill in a title',
15906     html: false,
15907     
15908     placement : 'right',
15909     trigger : 'hover', // hover
15910     
15911     delay : 0,
15912     
15913     over: 'parent',
15914     
15915     can_build_overlaid : false,
15916     
15917     getChildContainer : function()
15918     {
15919         return this.el.select('.popover-content',true).first();
15920     },
15921     
15922     getAutoCreate : function(){
15923          
15924         var cfg = {
15925            cls : 'popover roo-dynamic',
15926            style: 'display:block',
15927            cn : [
15928                 {
15929                     cls : 'arrow'
15930                 },
15931                 {
15932                     cls : 'popover-inner',
15933                     cn : [
15934                         {
15935                             tag: 'h3',
15936                             cls: 'popover-title',
15937                             html : this.title
15938                         },
15939                         {
15940                             cls : 'popover-content',
15941                             html : this.html
15942                         }
15943                     ]
15944                     
15945                 }
15946            ]
15947         };
15948         
15949         return cfg;
15950     },
15951     setTitle: function(str)
15952     {
15953         this.title = str;
15954         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15955     },
15956     setContent: function(str)
15957     {
15958         this.html = str;
15959         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15960     },
15961     // as it get's added to the bottom of the page.
15962     onRender : function(ct, position)
15963     {
15964         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15965         if(!this.el){
15966             var cfg = Roo.apply({},  this.getAutoCreate());
15967             cfg.id = Roo.id();
15968             
15969             if (this.cls) {
15970                 cfg.cls += ' ' + this.cls;
15971             }
15972             if (this.style) {
15973                 cfg.style = this.style;
15974             }
15975             //Roo.log("adding to ");
15976             this.el = Roo.get(document.body).createChild(cfg, position);
15977 //            Roo.log(this.el);
15978         }
15979         this.initEvents();
15980     },
15981     
15982     initEvents : function()
15983     {
15984         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15985         this.el.enableDisplayMode('block');
15986         this.el.hide();
15987         if (this.over === false) {
15988             return; 
15989         }
15990         if (this.triggers === false) {
15991             return;
15992         }
15993         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15994         var triggers = this.trigger ? this.trigger.split(' ') : [];
15995         Roo.each(triggers, function(trigger) {
15996         
15997             if (trigger == 'click') {
15998                 on_el.on('click', this.toggle, this);
15999             } else if (trigger != 'manual') {
16000                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16001                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16002       
16003                 on_el.on(eventIn  ,this.enter, this);
16004                 on_el.on(eventOut, this.leave, this);
16005             }
16006         }, this);
16007         
16008     },
16009     
16010     
16011     // private
16012     timeout : null,
16013     hoverState : null,
16014     
16015     toggle : function () {
16016         this.hoverState == 'in' ? this.leave() : this.enter();
16017     },
16018     
16019     enter : function () {
16020         
16021         clearTimeout(this.timeout);
16022     
16023         this.hoverState = 'in';
16024     
16025         if (!this.delay || !this.delay.show) {
16026             this.show();
16027             return;
16028         }
16029         var _t = this;
16030         this.timeout = setTimeout(function () {
16031             if (_t.hoverState == 'in') {
16032                 _t.show();
16033             }
16034         }, this.delay.show)
16035     },
16036     
16037     leave : function() {
16038         clearTimeout(this.timeout);
16039     
16040         this.hoverState = 'out';
16041     
16042         if (!this.delay || !this.delay.hide) {
16043             this.hide();
16044             return;
16045         }
16046         var _t = this;
16047         this.timeout = setTimeout(function () {
16048             if (_t.hoverState == 'out') {
16049                 _t.hide();
16050             }
16051         }, this.delay.hide)
16052     },
16053     
16054     show : function (on_el)
16055     {
16056         if (!on_el) {
16057             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16058         }
16059         
16060         // set content.
16061         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16062         if (this.html !== false) {
16063             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16064         }
16065         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16066         if (!this.title.length) {
16067             this.el.select('.popover-title',true).hide();
16068         }
16069         
16070         var placement = typeof this.placement == 'function' ?
16071             this.placement.call(this, this.el, on_el) :
16072             this.placement;
16073             
16074         var autoToken = /\s?auto?\s?/i;
16075         var autoPlace = autoToken.test(placement);
16076         if (autoPlace) {
16077             placement = placement.replace(autoToken, '') || 'top';
16078         }
16079         
16080         //this.el.detach()
16081         //this.el.setXY([0,0]);
16082         this.el.show();
16083         this.el.dom.style.display='block';
16084         this.el.addClass(placement);
16085         
16086         //this.el.appendTo(on_el);
16087         
16088         var p = this.getPosition();
16089         var box = this.el.getBox();
16090         
16091         if (autoPlace) {
16092             // fixme..
16093         }
16094         var align = Roo.bootstrap.Popover.alignment[placement];
16095         this.el.alignTo(on_el, align[0],align[1]);
16096         //var arrow = this.el.select('.arrow',true).first();
16097         //arrow.set(align[2], 
16098         
16099         this.el.addClass('in');
16100         
16101         
16102         if (this.el.hasClass('fade')) {
16103             // fade it?
16104         }
16105         
16106         this.hoverState = 'in';
16107         
16108         this.fireEvent('show', this);
16109         
16110     },
16111     hide : function()
16112     {
16113         this.el.setXY([0,0]);
16114         this.el.removeClass('in');
16115         this.el.hide();
16116         this.hoverState = null;
16117         
16118         this.fireEvent('hide', this);
16119     }
16120     
16121 });
16122
16123 Roo.bootstrap.Popover.alignment = {
16124     'left' : ['r-l', [-10,0], 'right'],
16125     'right' : ['l-r', [10,0], 'left'],
16126     'bottom' : ['t-b', [0,10], 'top'],
16127     'top' : [ 'b-t', [0,-10], 'bottom']
16128 };
16129
16130  /*
16131  * - LGPL
16132  *
16133  * Progress
16134  * 
16135  */
16136
16137 /**
16138  * @class Roo.bootstrap.Progress
16139  * @extends Roo.bootstrap.Component
16140  * Bootstrap Progress class
16141  * @cfg {Boolean} striped striped of the progress bar
16142  * @cfg {Boolean} active animated of the progress bar
16143  * 
16144  * 
16145  * @constructor
16146  * Create a new Progress
16147  * @param {Object} config The config object
16148  */
16149
16150 Roo.bootstrap.Progress = function(config){
16151     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16152 };
16153
16154 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16155     
16156     striped : false,
16157     active: false,
16158     
16159     getAutoCreate : function(){
16160         var cfg = {
16161             tag: 'div',
16162             cls: 'progress'
16163         };
16164         
16165         
16166         if(this.striped){
16167             cfg.cls += ' progress-striped';
16168         }
16169       
16170         if(this.active){
16171             cfg.cls += ' active';
16172         }
16173         
16174         
16175         return cfg;
16176     }
16177    
16178 });
16179
16180  
16181
16182  /*
16183  * - LGPL
16184  *
16185  * ProgressBar
16186  * 
16187  */
16188
16189 /**
16190  * @class Roo.bootstrap.ProgressBar
16191  * @extends Roo.bootstrap.Component
16192  * Bootstrap ProgressBar class
16193  * @cfg {Number} aria_valuenow aria-value now
16194  * @cfg {Number} aria_valuemin aria-value min
16195  * @cfg {Number} aria_valuemax aria-value max
16196  * @cfg {String} label label for the progress bar
16197  * @cfg {String} panel (success | info | warning | danger )
16198  * @cfg {String} role role of the progress bar
16199  * @cfg {String} sr_only text
16200  * 
16201  * 
16202  * @constructor
16203  * Create a new ProgressBar
16204  * @param {Object} config The config object
16205  */
16206
16207 Roo.bootstrap.ProgressBar = function(config){
16208     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16209 };
16210
16211 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16212     
16213     aria_valuenow : 0,
16214     aria_valuemin : 0,
16215     aria_valuemax : 100,
16216     label : false,
16217     panel : false,
16218     role : false,
16219     sr_only: false,
16220     
16221     getAutoCreate : function()
16222     {
16223         
16224         var cfg = {
16225             tag: 'div',
16226             cls: 'progress-bar',
16227             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16228         };
16229         
16230         if(this.sr_only){
16231             cfg.cn = {
16232                 tag: 'span',
16233                 cls: 'sr-only',
16234                 html: this.sr_only
16235             }
16236         }
16237         
16238         if(this.role){
16239             cfg.role = this.role;
16240         }
16241         
16242         if(this.aria_valuenow){
16243             cfg['aria-valuenow'] = this.aria_valuenow;
16244         }
16245         
16246         if(this.aria_valuemin){
16247             cfg['aria-valuemin'] = this.aria_valuemin;
16248         }
16249         
16250         if(this.aria_valuemax){
16251             cfg['aria-valuemax'] = this.aria_valuemax;
16252         }
16253         
16254         if(this.label && !this.sr_only){
16255             cfg.html = this.label;
16256         }
16257         
16258         if(this.panel){
16259             cfg.cls += ' progress-bar-' + this.panel;
16260         }
16261         
16262         return cfg;
16263     },
16264     
16265     update : function(aria_valuenow)
16266     {
16267         this.aria_valuenow = aria_valuenow;
16268         
16269         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16270     }
16271    
16272 });
16273
16274  
16275
16276  /*
16277  * - LGPL
16278  *
16279  * column
16280  * 
16281  */
16282
16283 /**
16284  * @class Roo.bootstrap.TabGroup
16285  * @extends Roo.bootstrap.Column
16286  * Bootstrap Column class
16287  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16288  * @cfg {Boolean} carousel true to make the group behave like a carousel
16289  * @cfg {Boolean} bullets show bullets for the panels
16290  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16291  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16292  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16293  * 
16294  * @constructor
16295  * Create a new TabGroup
16296  * @param {Object} config The config object
16297  */
16298
16299 Roo.bootstrap.TabGroup = function(config){
16300     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16301     if (!this.navId) {
16302         this.navId = Roo.id();
16303     }
16304     this.tabs = [];
16305     Roo.bootstrap.TabGroup.register(this);
16306     
16307 };
16308
16309 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16310     
16311     carousel : false,
16312     transition : false,
16313     bullets : 0,
16314     timer : 0,
16315     autoslide : false,
16316     slideFn : false,
16317     slideOnTouch : false,
16318     
16319     getAutoCreate : function()
16320     {
16321         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16322         
16323         cfg.cls += ' tab-content';
16324         
16325         if (this.carousel) {
16326             cfg.cls += ' carousel slide';
16327             
16328             cfg.cn = [{
16329                cls : 'carousel-inner'
16330             }];
16331         
16332             if(this.bullets  && !Roo.isTouch){
16333                 
16334                 var bullets = {
16335                     cls : 'carousel-bullets',
16336                     cn : []
16337                 };
16338                
16339                 if(this.bullets_cls){
16340                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16341                 }
16342                  /*
16343                 for (var i = 0; i < this.bullets; i++){
16344                     bullets.cn.push({
16345                         cls : 'bullet bullet-' + i
16346                     });
16347                 }
16348                 */
16349                 bullets.cn.push({
16350                     cls : 'clear'
16351                 });
16352                 
16353                 cfg.cn[0].cn = bullets;
16354             }
16355         }
16356         
16357         return cfg;
16358     },
16359     
16360     initEvents:  function()
16361     {
16362         if(Roo.isTouch && this.slideOnTouch){
16363             this.el.on("touchstart", this.onTouchStart, this);
16364         }
16365         
16366         if(this.autoslide){
16367             var _this = this;
16368             
16369             this.slideFn = window.setInterval(function() {
16370                 _this.showPanelNext();
16371             }, this.timer);
16372         }
16373         
16374     },
16375     
16376     onTouchStart : function(e, el, o)
16377     {
16378         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16379             return;
16380         }
16381         
16382         this.showPanelNext();
16383     },
16384     
16385     getChildContainer : function()
16386     {
16387         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16388     },
16389     
16390     /**
16391     * register a Navigation item
16392     * @param {Roo.bootstrap.NavItem} the navitem to add
16393     */
16394     register : function(item)
16395     {
16396         this.tabs.push( item);
16397         item.navId = this.navId; // not really needed..
16398         this.addBullet();
16399     
16400     },
16401     
16402     getActivePanel : function()
16403     {
16404         var r = false;
16405         Roo.each(this.tabs, function(t) {
16406             if (t.active) {
16407                 r = t;
16408                 return false;
16409             }
16410             return null;
16411         });
16412         return r;
16413         
16414     },
16415     getPanelByName : function(n)
16416     {
16417         var r = false;
16418         Roo.each(this.tabs, function(t) {
16419             if (t.tabId == n) {
16420                 r = t;
16421                 return false;
16422             }
16423             return null;
16424         });
16425         return r;
16426     },
16427     indexOfPanel : function(p)
16428     {
16429         var r = false;
16430         Roo.each(this.tabs, function(t,i) {
16431             if (t.tabId == p.tabId) {
16432                 r = i;
16433                 return false;
16434             }
16435             return null;
16436         });
16437         return r;
16438     },
16439     /**
16440      * show a specific panel
16441      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16442      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16443      */
16444     showPanel : function (pan)
16445     {
16446         if(this.transition || typeof(pan) == 'undefined'){
16447             Roo.log("waiting for the transitionend");
16448             return;
16449         }
16450         
16451         if (typeof(pan) == 'number') {
16452             pan = this.tabs[pan];
16453         }
16454         
16455         if (typeof(pan) == 'string') {
16456             pan = this.getPanelByName(pan);
16457         }
16458         
16459         var cur = this.getActivePanel();
16460         
16461         if(!pan || !cur){
16462             Roo.log('pan or acitve pan is undefined');
16463             return false;
16464         }
16465         
16466         if (pan.tabId == this.getActivePanel().tabId) {
16467             return true;
16468         }
16469         
16470         if (false === cur.fireEvent('beforedeactivate')) {
16471             return false;
16472         }
16473         
16474         if(this.bullets > 0 && !Roo.isTouch){
16475             this.setActiveBullet(this.indexOfPanel(pan));
16476         }
16477         
16478         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16479             
16480             this.transition = true;
16481             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16482             var lr = dir == 'next' ? 'left' : 'right';
16483             pan.el.addClass(dir); // or prev
16484             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16485             cur.el.addClass(lr); // or right
16486             pan.el.addClass(lr);
16487             
16488             var _this = this;
16489             cur.el.on('transitionend', function() {
16490                 Roo.log("trans end?");
16491                 
16492                 pan.el.removeClass([lr,dir]);
16493                 pan.setActive(true);
16494                 
16495                 cur.el.removeClass([lr]);
16496                 cur.setActive(false);
16497                 
16498                 _this.transition = false;
16499                 
16500             }, this, { single:  true } );
16501             
16502             return true;
16503         }
16504         
16505         cur.setActive(false);
16506         pan.setActive(true);
16507         
16508         return true;
16509         
16510     },
16511     showPanelNext : function()
16512     {
16513         var i = this.indexOfPanel(this.getActivePanel());
16514         
16515         if (i >= this.tabs.length - 1 && !this.autoslide) {
16516             return;
16517         }
16518         
16519         if (i >= this.tabs.length - 1 && this.autoslide) {
16520             i = -1;
16521         }
16522         
16523         this.showPanel(this.tabs[i+1]);
16524     },
16525     
16526     showPanelPrev : function()
16527     {
16528         var i = this.indexOfPanel(this.getActivePanel());
16529         
16530         if (i  < 1 && !this.autoslide) {
16531             return;
16532         }
16533         
16534         if (i < 1 && this.autoslide) {
16535             i = this.tabs.length;
16536         }
16537         
16538         this.showPanel(this.tabs[i-1]);
16539     },
16540     
16541     
16542     addBullet: function()
16543     {
16544         if(!this.bullets || Roo.isTouch){
16545             return;
16546         }
16547         var ctr = this.el.select('.carousel-bullets',true).first();
16548         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16549         var bullet = ctr.createChild({
16550             cls : 'bullet bullet-' + i
16551         },ctr.dom.lastChild);
16552         
16553         
16554         var _this = this;
16555         
16556         bullet.on('click', (function(e, el, o, ii, t){
16557
16558             e.preventDefault();
16559
16560             this.showPanel(ii);
16561
16562             if(this.autoslide && this.slideFn){
16563                 clearInterval(this.slideFn);
16564                 this.slideFn = window.setInterval(function() {
16565                     _this.showPanelNext();
16566                 }, this.timer);
16567             }
16568
16569         }).createDelegate(this, [i, bullet], true));
16570                 
16571         
16572     },
16573      
16574     setActiveBullet : function(i)
16575     {
16576         if(Roo.isTouch){
16577             return;
16578         }
16579         
16580         Roo.each(this.el.select('.bullet', true).elements, function(el){
16581             el.removeClass('selected');
16582         });
16583
16584         var bullet = this.el.select('.bullet-' + i, true).first();
16585         
16586         if(!bullet){
16587             return;
16588         }
16589         
16590         bullet.addClass('selected');
16591     }
16592     
16593     
16594   
16595 });
16596
16597  
16598
16599  
16600  
16601 Roo.apply(Roo.bootstrap.TabGroup, {
16602     
16603     groups: {},
16604      /**
16605     * register a Navigation Group
16606     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16607     */
16608     register : function(navgrp)
16609     {
16610         this.groups[navgrp.navId] = navgrp;
16611         
16612     },
16613     /**
16614     * fetch a Navigation Group based on the navigation ID
16615     * if one does not exist , it will get created.
16616     * @param {string} the navgroup to add
16617     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16618     */
16619     get: function(navId) {
16620         if (typeof(this.groups[navId]) == 'undefined') {
16621             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16622         }
16623         return this.groups[navId] ;
16624     }
16625     
16626     
16627     
16628 });
16629
16630  /*
16631  * - LGPL
16632  *
16633  * TabPanel
16634  * 
16635  */
16636
16637 /**
16638  * @class Roo.bootstrap.TabPanel
16639  * @extends Roo.bootstrap.Component
16640  * Bootstrap TabPanel class
16641  * @cfg {Boolean} active panel active
16642  * @cfg {String} html panel content
16643  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16644  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16645  * 
16646  * 
16647  * @constructor
16648  * Create a new TabPanel
16649  * @param {Object} config The config object
16650  */
16651
16652 Roo.bootstrap.TabPanel = function(config){
16653     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16654     this.addEvents({
16655         /**
16656              * @event changed
16657              * Fires when the active status changes
16658              * @param {Roo.bootstrap.TabPanel} this
16659              * @param {Boolean} state the new state
16660             
16661          */
16662         'changed': true,
16663         /**
16664              * @event beforedeactivate
16665              * Fires before a tab is de-activated - can be used to do validation on a form.
16666              * @param {Roo.bootstrap.TabPanel} this
16667              * @return {Boolean} false if there is an error
16668             
16669          */
16670         'beforedeactivate': true
16671      });
16672     
16673     this.tabId = this.tabId || Roo.id();
16674   
16675 };
16676
16677 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16678     
16679     active: false,
16680     html: false,
16681     tabId: false,
16682     navId : false,
16683     
16684     getAutoCreate : function(){
16685         var cfg = {
16686             tag: 'div',
16687             // item is needed for carousel - not sure if it has any effect otherwise
16688             cls: 'tab-pane item',
16689             html: this.html || ''
16690         };
16691         
16692         if(this.active){
16693             cfg.cls += ' active';
16694         }
16695         
16696         if(this.tabId){
16697             cfg.tabId = this.tabId;
16698         }
16699         
16700         
16701         return cfg;
16702     },
16703     
16704     initEvents:  function()
16705     {
16706         var p = this.parent();
16707         this.navId = this.navId || p.navId;
16708         
16709         if (typeof(this.navId) != 'undefined') {
16710             // not really needed.. but just in case.. parent should be a NavGroup.
16711             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16712             
16713             tg.register(this);
16714             
16715             var i = tg.tabs.length - 1;
16716             
16717             if(this.active && tg.bullets > 0 && i < tg.bullets){
16718                 tg.setActiveBullet(i);
16719             }
16720         }
16721         
16722     },
16723     
16724     
16725     onRender : function(ct, position)
16726     {
16727        // Roo.log("Call onRender: " + this.xtype);
16728         
16729         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16730         
16731         
16732         
16733         
16734         
16735     },
16736     
16737     setActive: function(state)
16738     {
16739         Roo.log("panel - set active " + this.tabId + "=" + state);
16740         
16741         this.active = state;
16742         if (!state) {
16743             this.el.removeClass('active');
16744             
16745         } else  if (!this.el.hasClass('active')) {
16746             this.el.addClass('active');
16747         }
16748         
16749         this.fireEvent('changed', this, state);
16750     }
16751     
16752     
16753 });
16754  
16755
16756  
16757
16758  /*
16759  * - LGPL
16760  *
16761  * DateField
16762  * 
16763  */
16764
16765 /**
16766  * @class Roo.bootstrap.DateField
16767  * @extends Roo.bootstrap.Input
16768  * Bootstrap DateField class
16769  * @cfg {Number} weekStart default 0
16770  * @cfg {String} viewMode default empty, (months|years)
16771  * @cfg {String} minViewMode default empty, (months|years)
16772  * @cfg {Number} startDate default -Infinity
16773  * @cfg {Number} endDate default Infinity
16774  * @cfg {Boolean} todayHighlight default false
16775  * @cfg {Boolean} todayBtn default false
16776  * @cfg {Boolean} calendarWeeks default false
16777  * @cfg {Object} daysOfWeekDisabled default empty
16778  * @cfg {Boolean} singleMode default false (true | false)
16779  * 
16780  * @cfg {Boolean} keyboardNavigation default true
16781  * @cfg {String} language default en
16782  * 
16783  * @constructor
16784  * Create a new DateField
16785  * @param {Object} config The config object
16786  */
16787
16788 Roo.bootstrap.DateField = function(config){
16789     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16790      this.addEvents({
16791             /**
16792              * @event show
16793              * Fires when this field show.
16794              * @param {Roo.bootstrap.DateField} this
16795              * @param {Mixed} date The date value
16796              */
16797             show : true,
16798             /**
16799              * @event show
16800              * Fires when this field hide.
16801              * @param {Roo.bootstrap.DateField} this
16802              * @param {Mixed} date The date value
16803              */
16804             hide : true,
16805             /**
16806              * @event select
16807              * Fires when select a date.
16808              * @param {Roo.bootstrap.DateField} this
16809              * @param {Mixed} date The date value
16810              */
16811             select : true,
16812             /**
16813              * @event beforeselect
16814              * Fires when before select a date.
16815              * @param {Roo.bootstrap.DateField} this
16816              * @param {Mixed} date The date value
16817              */
16818             beforeselect : true
16819         });
16820 };
16821
16822 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16823     
16824     /**
16825      * @cfg {String} format
16826      * The default date format string which can be overriden for localization support.  The format must be
16827      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16828      */
16829     format : "m/d/y",
16830     /**
16831      * @cfg {String} altFormats
16832      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16833      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16834      */
16835     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16836     
16837     weekStart : 0,
16838     
16839     viewMode : '',
16840     
16841     minViewMode : '',
16842     
16843     todayHighlight : false,
16844     
16845     todayBtn: false,
16846     
16847     language: 'en',
16848     
16849     keyboardNavigation: true,
16850     
16851     calendarWeeks: false,
16852     
16853     startDate: -Infinity,
16854     
16855     endDate: Infinity,
16856     
16857     daysOfWeekDisabled: [],
16858     
16859     _events: [],
16860     
16861     singleMode : false,
16862     
16863     UTCDate: function()
16864     {
16865         return new Date(Date.UTC.apply(Date, arguments));
16866     },
16867     
16868     UTCToday: function()
16869     {
16870         var today = new Date();
16871         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16872     },
16873     
16874     getDate: function() {
16875             var d = this.getUTCDate();
16876             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16877     },
16878     
16879     getUTCDate: function() {
16880             return this.date;
16881     },
16882     
16883     setDate: function(d) {
16884             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16885     },
16886     
16887     setUTCDate: function(d) {
16888             this.date = d;
16889             this.setValue(this.formatDate(this.date));
16890     },
16891         
16892     onRender: function(ct, position)
16893     {
16894         
16895         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16896         
16897         this.language = this.language || 'en';
16898         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16899         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16900         
16901         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16902         this.format = this.format || 'm/d/y';
16903         this.isInline = false;
16904         this.isInput = true;
16905         this.component = this.el.select('.add-on', true).first() || false;
16906         this.component = (this.component && this.component.length === 0) ? false : this.component;
16907         this.hasInput = this.component && this.inputEL().length;
16908         
16909         if (typeof(this.minViewMode === 'string')) {
16910             switch (this.minViewMode) {
16911                 case 'months':
16912                     this.minViewMode = 1;
16913                     break;
16914                 case 'years':
16915                     this.minViewMode = 2;
16916                     break;
16917                 default:
16918                     this.minViewMode = 0;
16919                     break;
16920             }
16921         }
16922         
16923         if (typeof(this.viewMode === 'string')) {
16924             switch (this.viewMode) {
16925                 case 'months':
16926                     this.viewMode = 1;
16927                     break;
16928                 case 'years':
16929                     this.viewMode = 2;
16930                     break;
16931                 default:
16932                     this.viewMode = 0;
16933                     break;
16934             }
16935         }
16936                 
16937         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16938         
16939 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16940         
16941         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16942         
16943         this.picker().on('mousedown', this.onMousedown, this);
16944         this.picker().on('click', this.onClick, this);
16945         
16946         this.picker().addClass('datepicker-dropdown');
16947         
16948         this.startViewMode = this.viewMode;
16949         
16950         if(this.singleMode){
16951             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16952                 v.setVisibilityMode(Roo.Element.DISPLAY);
16953                 v.hide();
16954             });
16955             
16956             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16957                 v.setStyle('width', '189px');
16958             });
16959         }
16960         
16961         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16962             if(!this.calendarWeeks){
16963                 v.remove();
16964                 return;
16965             }
16966             
16967             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16968             v.attr('colspan', function(i, val){
16969                 return parseInt(val) + 1;
16970             });
16971         });
16972                         
16973         
16974         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16975         
16976         this.setStartDate(this.startDate);
16977         this.setEndDate(this.endDate);
16978         
16979         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16980         
16981         this.fillDow();
16982         this.fillMonths();
16983         this.update();
16984         this.showMode();
16985         
16986         if(this.isInline) {
16987             this.show();
16988         }
16989     },
16990     
16991     picker : function()
16992     {
16993         return this.pickerEl;
16994 //        return this.el.select('.datepicker', true).first();
16995     },
16996     
16997     fillDow: function()
16998     {
16999         var dowCnt = this.weekStart;
17000         
17001         var dow = {
17002             tag: 'tr',
17003             cn: [
17004                 
17005             ]
17006         };
17007         
17008         if(this.calendarWeeks){
17009             dow.cn.push({
17010                 tag: 'th',
17011                 cls: 'cw',
17012                 html: '&nbsp;'
17013             })
17014         }
17015         
17016         while (dowCnt < this.weekStart + 7) {
17017             dow.cn.push({
17018                 tag: 'th',
17019                 cls: 'dow',
17020                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17021             });
17022         }
17023         
17024         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17025     },
17026     
17027     fillMonths: function()
17028     {    
17029         var i = 0;
17030         var months = this.picker().select('>.datepicker-months td', true).first();
17031         
17032         months.dom.innerHTML = '';
17033         
17034         while (i < 12) {
17035             var month = {
17036                 tag: 'span',
17037                 cls: 'month',
17038                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17039             };
17040             
17041             months.createChild(month);
17042         }
17043         
17044     },
17045     
17046     update: function()
17047     {
17048         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;
17049         
17050         if (this.date < this.startDate) {
17051             this.viewDate = new Date(this.startDate);
17052         } else if (this.date > this.endDate) {
17053             this.viewDate = new Date(this.endDate);
17054         } else {
17055             this.viewDate = new Date(this.date);
17056         }
17057         
17058         this.fill();
17059     },
17060     
17061     fill: function() 
17062     {
17063         var d = new Date(this.viewDate),
17064                 year = d.getUTCFullYear(),
17065                 month = d.getUTCMonth(),
17066                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17067                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17068                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17069                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17070                 currentDate = this.date && this.date.valueOf(),
17071                 today = this.UTCToday();
17072         
17073         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17074         
17075 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17076         
17077 //        this.picker.select('>tfoot th.today').
17078 //                                              .text(dates[this.language].today)
17079 //                                              .toggle(this.todayBtn !== false);
17080     
17081         this.updateNavArrows();
17082         this.fillMonths();
17083                                                 
17084         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17085         
17086         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17087          
17088         prevMonth.setUTCDate(day);
17089         
17090         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17091         
17092         var nextMonth = new Date(prevMonth);
17093         
17094         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17095         
17096         nextMonth = nextMonth.valueOf();
17097         
17098         var fillMonths = false;
17099         
17100         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17101         
17102         while(prevMonth.valueOf() < nextMonth) {
17103             var clsName = '';
17104             
17105             if (prevMonth.getUTCDay() === this.weekStart) {
17106                 if(fillMonths){
17107                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17108                 }
17109                     
17110                 fillMonths = {
17111                     tag: 'tr',
17112                     cn: []
17113                 };
17114                 
17115                 if(this.calendarWeeks){
17116                     // ISO 8601: First week contains first thursday.
17117                     // ISO also states week starts on Monday, but we can be more abstract here.
17118                     var
17119                     // Start of current week: based on weekstart/current date
17120                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17121                     // Thursday of this week
17122                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17123                     // First Thursday of year, year from thursday
17124                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17125                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17126                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17127                     
17128                     fillMonths.cn.push({
17129                         tag: 'td',
17130                         cls: 'cw',
17131                         html: calWeek
17132                     });
17133                 }
17134             }
17135             
17136             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17137                 clsName += ' old';
17138             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17139                 clsName += ' new';
17140             }
17141             if (this.todayHighlight &&
17142                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17143                 prevMonth.getUTCMonth() == today.getMonth() &&
17144                 prevMonth.getUTCDate() == today.getDate()) {
17145                 clsName += ' today';
17146             }
17147             
17148             if (currentDate && prevMonth.valueOf() === currentDate) {
17149                 clsName += ' active';
17150             }
17151             
17152             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17153                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17154                     clsName += ' disabled';
17155             }
17156             
17157             fillMonths.cn.push({
17158                 tag: 'td',
17159                 cls: 'day ' + clsName,
17160                 html: prevMonth.getDate()
17161             });
17162             
17163             prevMonth.setDate(prevMonth.getDate()+1);
17164         }
17165           
17166         var currentYear = this.date && this.date.getUTCFullYear();
17167         var currentMonth = this.date && this.date.getUTCMonth();
17168         
17169         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17170         
17171         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17172             v.removeClass('active');
17173             
17174             if(currentYear === year && k === currentMonth){
17175                 v.addClass('active');
17176             }
17177             
17178             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17179                 v.addClass('disabled');
17180             }
17181             
17182         });
17183         
17184         
17185         year = parseInt(year/10, 10) * 10;
17186         
17187         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17188         
17189         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17190         
17191         year -= 1;
17192         for (var i = -1; i < 11; i++) {
17193             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17194                 tag: 'span',
17195                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17196                 html: year
17197             });
17198             
17199             year += 1;
17200         }
17201     },
17202     
17203     showMode: function(dir) 
17204     {
17205         if (dir) {
17206             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17207         }
17208         
17209         Roo.each(this.picker().select('>div',true).elements, function(v){
17210             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17211             v.hide();
17212         });
17213         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17214     },
17215     
17216     place: function()
17217     {
17218         if(this.isInline) {
17219             return;
17220         }
17221         
17222         this.picker().removeClass(['bottom', 'top']);
17223         
17224         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17225             /*
17226              * place to the top of element!
17227              *
17228              */
17229             
17230             this.picker().addClass('top');
17231             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17232             
17233             return;
17234         }
17235         
17236         this.picker().addClass('bottom');
17237         
17238         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17239     },
17240     
17241     parseDate : function(value)
17242     {
17243         if(!value || value instanceof Date){
17244             return value;
17245         }
17246         var v = Date.parseDate(value, this.format);
17247         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17248             v = Date.parseDate(value, 'Y-m-d');
17249         }
17250         if(!v && this.altFormats){
17251             if(!this.altFormatsArray){
17252                 this.altFormatsArray = this.altFormats.split("|");
17253             }
17254             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17255                 v = Date.parseDate(value, this.altFormatsArray[i]);
17256             }
17257         }
17258         return v;
17259     },
17260     
17261     formatDate : function(date, fmt)
17262     {   
17263         return (!date || !(date instanceof Date)) ?
17264         date : date.dateFormat(fmt || this.format);
17265     },
17266     
17267     onFocus : function()
17268     {
17269         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17270         this.show();
17271     },
17272     
17273     onBlur : function()
17274     {
17275         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17276         
17277         var d = this.inputEl().getValue();
17278         
17279         this.setValue(d);
17280                 
17281         this.hide();
17282     },
17283     
17284     show : function()
17285     {
17286         this.picker().show();
17287         this.update();
17288         this.place();
17289         
17290         this.fireEvent('show', this, this.date);
17291     },
17292     
17293     hide : function()
17294     {
17295         if(this.isInline) {
17296             return;
17297         }
17298         this.picker().hide();
17299         this.viewMode = this.startViewMode;
17300         this.showMode();
17301         
17302         this.fireEvent('hide', this, this.date);
17303         
17304     },
17305     
17306     onMousedown: function(e)
17307     {
17308         e.stopPropagation();
17309         e.preventDefault();
17310     },
17311     
17312     keyup: function(e)
17313     {
17314         Roo.bootstrap.DateField.superclass.keyup.call(this);
17315         this.update();
17316     },
17317
17318     setValue: function(v)
17319     {
17320         if(this.fireEvent('beforeselect', this, v) !== false){
17321             var d = new Date(this.parseDate(v) ).clearTime();
17322         
17323             if(isNaN(d.getTime())){
17324                 this.date = this.viewDate = '';
17325                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17326                 return;
17327             }
17328
17329             v = this.formatDate(d);
17330
17331             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17332
17333             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17334
17335             this.update();
17336
17337             this.fireEvent('select', this, this.date);
17338         }
17339     },
17340     
17341     getValue: function()
17342     {
17343         return this.formatDate(this.date);
17344     },
17345     
17346     fireKey: function(e)
17347     {
17348         if (!this.picker().isVisible()){
17349             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17350                 this.show();
17351             }
17352             return;
17353         }
17354         
17355         var dateChanged = false,
17356         dir, day, month,
17357         newDate, newViewDate;
17358         
17359         switch(e.keyCode){
17360             case 27: // escape
17361                 this.hide();
17362                 e.preventDefault();
17363                 break;
17364             case 37: // left
17365             case 39: // right
17366                 if (!this.keyboardNavigation) {
17367                     break;
17368                 }
17369                 dir = e.keyCode == 37 ? -1 : 1;
17370                 
17371                 if (e.ctrlKey){
17372                     newDate = this.moveYear(this.date, dir);
17373                     newViewDate = this.moveYear(this.viewDate, dir);
17374                 } else if (e.shiftKey){
17375                     newDate = this.moveMonth(this.date, dir);
17376                     newViewDate = this.moveMonth(this.viewDate, dir);
17377                 } else {
17378                     newDate = new Date(this.date);
17379                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17380                     newViewDate = new Date(this.viewDate);
17381                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17382                 }
17383                 if (this.dateWithinRange(newDate)){
17384                     this.date = newDate;
17385                     this.viewDate = newViewDate;
17386                     this.setValue(this.formatDate(this.date));
17387 //                    this.update();
17388                     e.preventDefault();
17389                     dateChanged = true;
17390                 }
17391                 break;
17392             case 38: // up
17393             case 40: // down
17394                 if (!this.keyboardNavigation) {
17395                     break;
17396                 }
17397                 dir = e.keyCode == 38 ? -1 : 1;
17398                 if (e.ctrlKey){
17399                     newDate = this.moveYear(this.date, dir);
17400                     newViewDate = this.moveYear(this.viewDate, dir);
17401                 } else if (e.shiftKey){
17402                     newDate = this.moveMonth(this.date, dir);
17403                     newViewDate = this.moveMonth(this.viewDate, dir);
17404                 } else {
17405                     newDate = new Date(this.date);
17406                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17407                     newViewDate = new Date(this.viewDate);
17408                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17409                 }
17410                 if (this.dateWithinRange(newDate)){
17411                     this.date = newDate;
17412                     this.viewDate = newViewDate;
17413                     this.setValue(this.formatDate(this.date));
17414 //                    this.update();
17415                     e.preventDefault();
17416                     dateChanged = true;
17417                 }
17418                 break;
17419             case 13: // enter
17420                 this.setValue(this.formatDate(this.date));
17421                 this.hide();
17422                 e.preventDefault();
17423                 break;
17424             case 9: // tab
17425                 this.setValue(this.formatDate(this.date));
17426                 this.hide();
17427                 break;
17428             case 16: // shift
17429             case 17: // ctrl
17430             case 18: // alt
17431                 break;
17432             default :
17433                 this.hide();
17434                 
17435         }
17436     },
17437     
17438     
17439     onClick: function(e) 
17440     {
17441         e.stopPropagation();
17442         e.preventDefault();
17443         
17444         var target = e.getTarget();
17445         
17446         if(target.nodeName.toLowerCase() === 'i'){
17447             target = Roo.get(target).dom.parentNode;
17448         }
17449         
17450         var nodeName = target.nodeName;
17451         var className = target.className;
17452         var html = target.innerHTML;
17453         //Roo.log(nodeName);
17454         
17455         switch(nodeName.toLowerCase()) {
17456             case 'th':
17457                 switch(className) {
17458                     case 'switch':
17459                         this.showMode(1);
17460                         break;
17461                     case 'prev':
17462                     case 'next':
17463                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17464                         switch(this.viewMode){
17465                                 case 0:
17466                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17467                                         break;
17468                                 case 1:
17469                                 case 2:
17470                                         this.viewDate = this.moveYear(this.viewDate, dir);
17471                                         break;
17472                         }
17473                         this.fill();
17474                         break;
17475                     case 'today':
17476                         var date = new Date();
17477                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17478 //                        this.fill()
17479                         this.setValue(this.formatDate(this.date));
17480                         
17481                         this.hide();
17482                         break;
17483                 }
17484                 break;
17485             case 'span':
17486                 if (className.indexOf('disabled') < 0) {
17487                     this.viewDate.setUTCDate(1);
17488                     if (className.indexOf('month') > -1) {
17489                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17490                     } else {
17491                         var year = parseInt(html, 10) || 0;
17492                         this.viewDate.setUTCFullYear(year);
17493                         
17494                     }
17495                     
17496                     if(this.singleMode){
17497                         this.setValue(this.formatDate(this.viewDate));
17498                         this.hide();
17499                         return;
17500                     }
17501                     
17502                     this.showMode(-1);
17503                     this.fill();
17504                 }
17505                 break;
17506                 
17507             case 'td':
17508                 //Roo.log(className);
17509                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17510                     var day = parseInt(html, 10) || 1;
17511                     var year = this.viewDate.getUTCFullYear(),
17512                         month = this.viewDate.getUTCMonth();
17513
17514                     if (className.indexOf('old') > -1) {
17515                         if(month === 0 ){
17516                             month = 11;
17517                             year -= 1;
17518                         }else{
17519                             month -= 1;
17520                         }
17521                     } else if (className.indexOf('new') > -1) {
17522                         if (month == 11) {
17523                             month = 0;
17524                             year += 1;
17525                         } else {
17526                             month += 1;
17527                         }
17528                     }
17529                     //Roo.log([year,month,day]);
17530                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17531                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17532 //                    this.fill();
17533                     //Roo.log(this.formatDate(this.date));
17534                     this.setValue(this.formatDate(this.date));
17535                     this.hide();
17536                 }
17537                 break;
17538         }
17539     },
17540     
17541     setStartDate: function(startDate)
17542     {
17543         this.startDate = startDate || -Infinity;
17544         if (this.startDate !== -Infinity) {
17545             this.startDate = this.parseDate(this.startDate);
17546         }
17547         this.update();
17548         this.updateNavArrows();
17549     },
17550
17551     setEndDate: function(endDate)
17552     {
17553         this.endDate = endDate || Infinity;
17554         if (this.endDate !== Infinity) {
17555             this.endDate = this.parseDate(this.endDate);
17556         }
17557         this.update();
17558         this.updateNavArrows();
17559     },
17560     
17561     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17562     {
17563         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17564         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17565             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17566         }
17567         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17568             return parseInt(d, 10);
17569         });
17570         this.update();
17571         this.updateNavArrows();
17572     },
17573     
17574     updateNavArrows: function() 
17575     {
17576         if(this.singleMode){
17577             return;
17578         }
17579         
17580         var d = new Date(this.viewDate),
17581         year = d.getUTCFullYear(),
17582         month = d.getUTCMonth();
17583         
17584         Roo.each(this.picker().select('.prev', true).elements, function(v){
17585             v.show();
17586             switch (this.viewMode) {
17587                 case 0:
17588
17589                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17590                         v.hide();
17591                     }
17592                     break;
17593                 case 1:
17594                 case 2:
17595                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17596                         v.hide();
17597                     }
17598                     break;
17599             }
17600         });
17601         
17602         Roo.each(this.picker().select('.next', true).elements, function(v){
17603             v.show();
17604             switch (this.viewMode) {
17605                 case 0:
17606
17607                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17608                         v.hide();
17609                     }
17610                     break;
17611                 case 1:
17612                 case 2:
17613                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17614                         v.hide();
17615                     }
17616                     break;
17617             }
17618         })
17619     },
17620     
17621     moveMonth: function(date, dir)
17622     {
17623         if (!dir) {
17624             return date;
17625         }
17626         var new_date = new Date(date.valueOf()),
17627         day = new_date.getUTCDate(),
17628         month = new_date.getUTCMonth(),
17629         mag = Math.abs(dir),
17630         new_month, test;
17631         dir = dir > 0 ? 1 : -1;
17632         if (mag == 1){
17633             test = dir == -1
17634             // If going back one month, make sure month is not current month
17635             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17636             ? function(){
17637                 return new_date.getUTCMonth() == month;
17638             }
17639             // If going forward one month, make sure month is as expected
17640             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17641             : function(){
17642                 return new_date.getUTCMonth() != new_month;
17643             };
17644             new_month = month + dir;
17645             new_date.setUTCMonth(new_month);
17646             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17647             if (new_month < 0 || new_month > 11) {
17648                 new_month = (new_month + 12) % 12;
17649             }
17650         } else {
17651             // For magnitudes >1, move one month at a time...
17652             for (var i=0; i<mag; i++) {
17653                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17654                 new_date = this.moveMonth(new_date, dir);
17655             }
17656             // ...then reset the day, keeping it in the new month
17657             new_month = new_date.getUTCMonth();
17658             new_date.setUTCDate(day);
17659             test = function(){
17660                 return new_month != new_date.getUTCMonth();
17661             };
17662         }
17663         // Common date-resetting loop -- if date is beyond end of month, make it
17664         // end of month
17665         while (test()){
17666             new_date.setUTCDate(--day);
17667             new_date.setUTCMonth(new_month);
17668         }
17669         return new_date;
17670     },
17671
17672     moveYear: function(date, dir)
17673     {
17674         return this.moveMonth(date, dir*12);
17675     },
17676
17677     dateWithinRange: function(date)
17678     {
17679         return date >= this.startDate && date <= this.endDate;
17680     },
17681
17682     
17683     remove: function() 
17684     {
17685         this.picker().remove();
17686     }
17687    
17688 });
17689
17690 Roo.apply(Roo.bootstrap.DateField,  {
17691     
17692     head : {
17693         tag: 'thead',
17694         cn: [
17695         {
17696             tag: 'tr',
17697             cn: [
17698             {
17699                 tag: 'th',
17700                 cls: 'prev',
17701                 html: '<i class="fa fa-arrow-left"/>'
17702             },
17703             {
17704                 tag: 'th',
17705                 cls: 'switch',
17706                 colspan: '5'
17707             },
17708             {
17709                 tag: 'th',
17710                 cls: 'next',
17711                 html: '<i class="fa fa-arrow-right"/>'
17712             }
17713
17714             ]
17715         }
17716         ]
17717     },
17718     
17719     content : {
17720         tag: 'tbody',
17721         cn: [
17722         {
17723             tag: 'tr',
17724             cn: [
17725             {
17726                 tag: 'td',
17727                 colspan: '7'
17728             }
17729             ]
17730         }
17731         ]
17732     },
17733     
17734     footer : {
17735         tag: 'tfoot',
17736         cn: [
17737         {
17738             tag: 'tr',
17739             cn: [
17740             {
17741                 tag: 'th',
17742                 colspan: '7',
17743                 cls: 'today'
17744             }
17745                     
17746             ]
17747         }
17748         ]
17749     },
17750     
17751     dates:{
17752         en: {
17753             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17754             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17755             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17756             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17757             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17758             today: "Today"
17759         }
17760     },
17761     
17762     modes: [
17763     {
17764         clsName: 'days',
17765         navFnc: 'Month',
17766         navStep: 1
17767     },
17768     {
17769         clsName: 'months',
17770         navFnc: 'FullYear',
17771         navStep: 1
17772     },
17773     {
17774         clsName: 'years',
17775         navFnc: 'FullYear',
17776         navStep: 10
17777     }]
17778 });
17779
17780 Roo.apply(Roo.bootstrap.DateField,  {
17781   
17782     template : {
17783         tag: 'div',
17784         cls: 'datepicker dropdown-menu roo-dynamic',
17785         cn: [
17786         {
17787             tag: 'div',
17788             cls: 'datepicker-days',
17789             cn: [
17790             {
17791                 tag: 'table',
17792                 cls: 'table-condensed',
17793                 cn:[
17794                 Roo.bootstrap.DateField.head,
17795                 {
17796                     tag: 'tbody'
17797                 },
17798                 Roo.bootstrap.DateField.footer
17799                 ]
17800             }
17801             ]
17802         },
17803         {
17804             tag: 'div',
17805             cls: 'datepicker-months',
17806             cn: [
17807             {
17808                 tag: 'table',
17809                 cls: 'table-condensed',
17810                 cn:[
17811                 Roo.bootstrap.DateField.head,
17812                 Roo.bootstrap.DateField.content,
17813                 Roo.bootstrap.DateField.footer
17814                 ]
17815             }
17816             ]
17817         },
17818         {
17819             tag: 'div',
17820             cls: 'datepicker-years',
17821             cn: [
17822             {
17823                 tag: 'table',
17824                 cls: 'table-condensed',
17825                 cn:[
17826                 Roo.bootstrap.DateField.head,
17827                 Roo.bootstrap.DateField.content,
17828                 Roo.bootstrap.DateField.footer
17829                 ]
17830             }
17831             ]
17832         }
17833         ]
17834     }
17835 });
17836
17837  
17838
17839  /*
17840  * - LGPL
17841  *
17842  * TimeField
17843  * 
17844  */
17845
17846 /**
17847  * @class Roo.bootstrap.TimeField
17848  * @extends Roo.bootstrap.Input
17849  * Bootstrap DateField class
17850  * 
17851  * 
17852  * @constructor
17853  * Create a new TimeField
17854  * @param {Object} config The config object
17855  */
17856
17857 Roo.bootstrap.TimeField = function(config){
17858     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17859     this.addEvents({
17860             /**
17861              * @event show
17862              * Fires when this field show.
17863              * @param {Roo.bootstrap.DateField} thisthis
17864              * @param {Mixed} date The date value
17865              */
17866             show : true,
17867             /**
17868              * @event show
17869              * Fires when this field hide.
17870              * @param {Roo.bootstrap.DateField} this
17871              * @param {Mixed} date The date value
17872              */
17873             hide : true,
17874             /**
17875              * @event select
17876              * Fires when select a date.
17877              * @param {Roo.bootstrap.DateField} this
17878              * @param {Mixed} date The date value
17879              */
17880             select : true
17881         });
17882 };
17883
17884 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17885     
17886     /**
17887      * @cfg {String} format
17888      * The default time format string which can be overriden for localization support.  The format must be
17889      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17890      */
17891     format : "H:i",
17892        
17893     onRender: function(ct, position)
17894     {
17895         
17896         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17897                 
17898         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17899         
17900         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17901         
17902         this.pop = this.picker().select('>.datepicker-time',true).first();
17903         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17904         
17905         this.picker().on('mousedown', this.onMousedown, this);
17906         this.picker().on('click', this.onClick, this);
17907         
17908         this.picker().addClass('datepicker-dropdown');
17909     
17910         this.fillTime();
17911         this.update();
17912             
17913         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17914         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17915         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17916         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17917         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17918         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17919
17920     },
17921     
17922     fireKey: function(e){
17923         if (!this.picker().isVisible()){
17924             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17925                 this.show();
17926             }
17927             return;
17928         }
17929
17930         e.preventDefault();
17931         
17932         switch(e.keyCode){
17933             case 27: // escape
17934                 this.hide();
17935                 break;
17936             case 37: // left
17937             case 39: // right
17938                 this.onTogglePeriod();
17939                 break;
17940             case 38: // up
17941                 this.onIncrementMinutes();
17942                 break;
17943             case 40: // down
17944                 this.onDecrementMinutes();
17945                 break;
17946             case 13: // enter
17947             case 9: // tab
17948                 this.setTime();
17949                 break;
17950         }
17951     },
17952     
17953     onClick: function(e) {
17954         e.stopPropagation();
17955         e.preventDefault();
17956     },
17957     
17958     picker : function()
17959     {
17960         return this.el.select('.datepicker', true).first();
17961     },
17962     
17963     fillTime: function()
17964     {    
17965         var time = this.pop.select('tbody', true).first();
17966         
17967         time.dom.innerHTML = '';
17968         
17969         time.createChild({
17970             tag: 'tr',
17971             cn: [
17972                 {
17973                     tag: 'td',
17974                     cn: [
17975                         {
17976                             tag: 'a',
17977                             href: '#',
17978                             cls: 'btn',
17979                             cn: [
17980                                 {
17981                                     tag: 'span',
17982                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17983                                 }
17984                             ]
17985                         } 
17986                     ]
17987                 },
17988                 {
17989                     tag: 'td',
17990                     cls: 'separator'
17991                 },
17992                 {
17993                     tag: 'td',
17994                     cn: [
17995                         {
17996                             tag: 'a',
17997                             href: '#',
17998                             cls: 'btn',
17999                             cn: [
18000                                 {
18001                                     tag: 'span',
18002                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18003                                 }
18004                             ]
18005                         }
18006                     ]
18007                 },
18008                 {
18009                     tag: 'td',
18010                     cls: 'separator'
18011                 }
18012             ]
18013         });
18014         
18015         time.createChild({
18016             tag: 'tr',
18017             cn: [
18018                 {
18019                     tag: 'td',
18020                     cn: [
18021                         {
18022                             tag: 'span',
18023                             cls: 'timepicker-hour',
18024                             html: '00'
18025                         }  
18026                     ]
18027                 },
18028                 {
18029                     tag: 'td',
18030                     cls: 'separator',
18031                     html: ':'
18032                 },
18033                 {
18034                     tag: 'td',
18035                     cn: [
18036                         {
18037                             tag: 'span',
18038                             cls: 'timepicker-minute',
18039                             html: '00'
18040                         }  
18041                     ]
18042                 },
18043                 {
18044                     tag: 'td',
18045                     cls: 'separator'
18046                 },
18047                 {
18048                     tag: 'td',
18049                     cn: [
18050                         {
18051                             tag: 'button',
18052                             type: 'button',
18053                             cls: 'btn btn-primary period',
18054                             html: 'AM'
18055                             
18056                         }
18057                     ]
18058                 }
18059             ]
18060         });
18061         
18062         time.createChild({
18063             tag: 'tr',
18064             cn: [
18065                 {
18066                     tag: 'td',
18067                     cn: [
18068                         {
18069                             tag: 'a',
18070                             href: '#',
18071                             cls: 'btn',
18072                             cn: [
18073                                 {
18074                                     tag: 'span',
18075                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18076                                 }
18077                             ]
18078                         }
18079                     ]
18080                 },
18081                 {
18082                     tag: 'td',
18083                     cls: 'separator'
18084                 },
18085                 {
18086                     tag: 'td',
18087                     cn: [
18088                         {
18089                             tag: 'a',
18090                             href: '#',
18091                             cls: 'btn',
18092                             cn: [
18093                                 {
18094                                     tag: 'span',
18095                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18096                                 }
18097                             ]
18098                         }
18099                     ]
18100                 },
18101                 {
18102                     tag: 'td',
18103                     cls: 'separator'
18104                 }
18105             ]
18106         });
18107         
18108     },
18109     
18110     update: function()
18111     {
18112         
18113         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18114         
18115         this.fill();
18116     },
18117     
18118     fill: function() 
18119     {
18120         var hours = this.time.getHours();
18121         var minutes = this.time.getMinutes();
18122         var period = 'AM';
18123         
18124         if(hours > 11){
18125             period = 'PM';
18126         }
18127         
18128         if(hours == 0){
18129             hours = 12;
18130         }
18131         
18132         
18133         if(hours > 12){
18134             hours = hours - 12;
18135         }
18136         
18137         if(hours < 10){
18138             hours = '0' + hours;
18139         }
18140         
18141         if(minutes < 10){
18142             minutes = '0' + minutes;
18143         }
18144         
18145         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18146         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18147         this.pop.select('button', true).first().dom.innerHTML = period;
18148         
18149     },
18150     
18151     place: function()
18152     {   
18153         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18154         
18155         var cls = ['bottom'];
18156         
18157         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18158             cls.pop();
18159             cls.push('top');
18160         }
18161         
18162         cls.push('right');
18163         
18164         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18165             cls.pop();
18166             cls.push('left');
18167         }
18168         
18169         this.picker().addClass(cls.join('-'));
18170         
18171         var _this = this;
18172         
18173         Roo.each(cls, function(c){
18174             if(c == 'bottom'){
18175                 _this.picker().setTop(_this.inputEl().getHeight());
18176                 return;
18177             }
18178             if(c == 'top'){
18179                 _this.picker().setTop(0 - _this.picker().getHeight());
18180                 return;
18181             }
18182             
18183             if(c == 'left'){
18184                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18185                 return;
18186             }
18187             if(c == 'right'){
18188                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18189                 return;
18190             }
18191         });
18192         
18193     },
18194   
18195     onFocus : function()
18196     {
18197         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18198         this.show();
18199     },
18200     
18201     onBlur : function()
18202     {
18203         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18204         this.hide();
18205     },
18206     
18207     show : function()
18208     {
18209         this.picker().show();
18210         this.pop.show();
18211         this.update();
18212         this.place();
18213         
18214         this.fireEvent('show', this, this.date);
18215     },
18216     
18217     hide : function()
18218     {
18219         this.picker().hide();
18220         this.pop.hide();
18221         
18222         this.fireEvent('hide', this, this.date);
18223     },
18224     
18225     setTime : function()
18226     {
18227         this.hide();
18228         this.setValue(this.time.format(this.format));
18229         
18230         this.fireEvent('select', this, this.date);
18231         
18232         
18233     },
18234     
18235     onMousedown: function(e){
18236         e.stopPropagation();
18237         e.preventDefault();
18238     },
18239     
18240     onIncrementHours: function()
18241     {
18242         Roo.log('onIncrementHours');
18243         this.time = this.time.add(Date.HOUR, 1);
18244         this.update();
18245         
18246     },
18247     
18248     onDecrementHours: function()
18249     {
18250         Roo.log('onDecrementHours');
18251         this.time = this.time.add(Date.HOUR, -1);
18252         this.update();
18253     },
18254     
18255     onIncrementMinutes: function()
18256     {
18257         Roo.log('onIncrementMinutes');
18258         this.time = this.time.add(Date.MINUTE, 1);
18259         this.update();
18260     },
18261     
18262     onDecrementMinutes: function()
18263     {
18264         Roo.log('onDecrementMinutes');
18265         this.time = this.time.add(Date.MINUTE, -1);
18266         this.update();
18267     },
18268     
18269     onTogglePeriod: function()
18270     {
18271         Roo.log('onTogglePeriod');
18272         this.time = this.time.add(Date.HOUR, 12);
18273         this.update();
18274     }
18275     
18276    
18277 });
18278
18279 Roo.apply(Roo.bootstrap.TimeField,  {
18280     
18281     content : {
18282         tag: 'tbody',
18283         cn: [
18284             {
18285                 tag: 'tr',
18286                 cn: [
18287                 {
18288                     tag: 'td',
18289                     colspan: '7'
18290                 }
18291                 ]
18292             }
18293         ]
18294     },
18295     
18296     footer : {
18297         tag: 'tfoot',
18298         cn: [
18299             {
18300                 tag: 'tr',
18301                 cn: [
18302                 {
18303                     tag: 'th',
18304                     colspan: '7',
18305                     cls: '',
18306                     cn: [
18307                         {
18308                             tag: 'button',
18309                             cls: 'btn btn-info ok',
18310                             html: 'OK'
18311                         }
18312                     ]
18313                 }
18314
18315                 ]
18316             }
18317         ]
18318     }
18319 });
18320
18321 Roo.apply(Roo.bootstrap.TimeField,  {
18322   
18323     template : {
18324         tag: 'div',
18325         cls: 'datepicker dropdown-menu',
18326         cn: [
18327             {
18328                 tag: 'div',
18329                 cls: 'datepicker-time',
18330                 cn: [
18331                 {
18332                     tag: 'table',
18333                     cls: 'table-condensed',
18334                     cn:[
18335                     Roo.bootstrap.TimeField.content,
18336                     Roo.bootstrap.TimeField.footer
18337                     ]
18338                 }
18339                 ]
18340             }
18341         ]
18342     }
18343 });
18344
18345  
18346
18347  /*
18348  * - LGPL
18349  *
18350  * MonthField
18351  * 
18352  */
18353
18354 /**
18355  * @class Roo.bootstrap.MonthField
18356  * @extends Roo.bootstrap.Input
18357  * Bootstrap MonthField class
18358  * 
18359  * @cfg {String} language default en
18360  * 
18361  * @constructor
18362  * Create a new MonthField
18363  * @param {Object} config The config object
18364  */
18365
18366 Roo.bootstrap.MonthField = function(config){
18367     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18368     
18369     this.addEvents({
18370         /**
18371          * @event show
18372          * Fires when this field show.
18373          * @param {Roo.bootstrap.MonthField} this
18374          * @param {Mixed} date The date value
18375          */
18376         show : true,
18377         /**
18378          * @event show
18379          * Fires when this field hide.
18380          * @param {Roo.bootstrap.MonthField} this
18381          * @param {Mixed} date The date value
18382          */
18383         hide : true,
18384         /**
18385          * @event select
18386          * Fires when select a date.
18387          * @param {Roo.bootstrap.MonthField} this
18388          * @param {String} oldvalue The old value
18389          * @param {String} newvalue The new value
18390          */
18391         select : true
18392     });
18393 };
18394
18395 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18396     
18397     onRender: function(ct, position)
18398     {
18399         
18400         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18401         
18402         this.language = this.language || 'en';
18403         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18404         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18405         
18406         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18407         this.isInline = false;
18408         this.isInput = true;
18409         this.component = this.el.select('.add-on', true).first() || false;
18410         this.component = (this.component && this.component.length === 0) ? false : this.component;
18411         this.hasInput = this.component && this.inputEL().length;
18412         
18413         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18414         
18415         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18416         
18417         this.picker().on('mousedown', this.onMousedown, this);
18418         this.picker().on('click', this.onClick, this);
18419         
18420         this.picker().addClass('datepicker-dropdown');
18421         
18422         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18423             v.setStyle('width', '189px');
18424         });
18425         
18426         this.fillMonths();
18427         
18428         this.update();
18429         
18430         if(this.isInline) {
18431             this.show();
18432         }
18433         
18434     },
18435     
18436     setValue: function(v, suppressEvent)
18437     {   
18438         var o = this.getValue();
18439         
18440         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18441         
18442         this.update();
18443
18444         if(suppressEvent !== true){
18445             this.fireEvent('select', this, o, v);
18446         }
18447         
18448     },
18449     
18450     getValue: function()
18451     {
18452         return this.value;
18453     },
18454     
18455     onClick: function(e) 
18456     {
18457         e.stopPropagation();
18458         e.preventDefault();
18459         
18460         var target = e.getTarget();
18461         
18462         if(target.nodeName.toLowerCase() === 'i'){
18463             target = Roo.get(target).dom.parentNode;
18464         }
18465         
18466         var nodeName = target.nodeName;
18467         var className = target.className;
18468         var html = target.innerHTML;
18469         
18470         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18471             return;
18472         }
18473         
18474         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18475         
18476         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18477         
18478         this.hide();
18479                         
18480     },
18481     
18482     picker : function()
18483     {
18484         return this.pickerEl;
18485     },
18486     
18487     fillMonths: function()
18488     {    
18489         var i = 0;
18490         var months = this.picker().select('>.datepicker-months td', true).first();
18491         
18492         months.dom.innerHTML = '';
18493         
18494         while (i < 12) {
18495             var month = {
18496                 tag: 'span',
18497                 cls: 'month',
18498                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18499             };
18500             
18501             months.createChild(month);
18502         }
18503         
18504     },
18505     
18506     update: function()
18507     {
18508         var _this = this;
18509         
18510         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18511             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18512         }
18513         
18514         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18515             e.removeClass('active');
18516             
18517             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18518                 e.addClass('active');
18519             }
18520         })
18521     },
18522     
18523     place: function()
18524     {
18525         if(this.isInline) {
18526             return;
18527         }
18528         
18529         this.picker().removeClass(['bottom', 'top']);
18530         
18531         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18532             /*
18533              * place to the top of element!
18534              *
18535              */
18536             
18537             this.picker().addClass('top');
18538             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18539             
18540             return;
18541         }
18542         
18543         this.picker().addClass('bottom');
18544         
18545         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18546     },
18547     
18548     onFocus : function()
18549     {
18550         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18551         this.show();
18552     },
18553     
18554     onBlur : function()
18555     {
18556         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18557         
18558         var d = this.inputEl().getValue();
18559         
18560         this.setValue(d);
18561                 
18562         this.hide();
18563     },
18564     
18565     show : function()
18566     {
18567         this.picker().show();
18568         this.picker().select('>.datepicker-months', true).first().show();
18569         this.update();
18570         this.place();
18571         
18572         this.fireEvent('show', this, this.date);
18573     },
18574     
18575     hide : function()
18576     {
18577         if(this.isInline) {
18578             return;
18579         }
18580         this.picker().hide();
18581         this.fireEvent('hide', this, this.date);
18582         
18583     },
18584     
18585     onMousedown: function(e)
18586     {
18587         e.stopPropagation();
18588         e.preventDefault();
18589     },
18590     
18591     keyup: function(e)
18592     {
18593         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18594         this.update();
18595     },
18596
18597     fireKey: function(e)
18598     {
18599         if (!this.picker().isVisible()){
18600             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18601                 this.show();
18602             }
18603             return;
18604         }
18605         
18606         var dir;
18607         
18608         switch(e.keyCode){
18609             case 27: // escape
18610                 this.hide();
18611                 e.preventDefault();
18612                 break;
18613             case 37: // left
18614             case 39: // right
18615                 dir = e.keyCode == 37 ? -1 : 1;
18616                 
18617                 this.vIndex = this.vIndex + dir;
18618                 
18619                 if(this.vIndex < 0){
18620                     this.vIndex = 0;
18621                 }
18622                 
18623                 if(this.vIndex > 11){
18624                     this.vIndex = 11;
18625                 }
18626                 
18627                 if(isNaN(this.vIndex)){
18628                     this.vIndex = 0;
18629                 }
18630                 
18631                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18632                 
18633                 break;
18634             case 38: // up
18635             case 40: // down
18636                 
18637                 dir = e.keyCode == 38 ? -1 : 1;
18638                 
18639                 this.vIndex = this.vIndex + dir * 4;
18640                 
18641                 if(this.vIndex < 0){
18642                     this.vIndex = 0;
18643                 }
18644                 
18645                 if(this.vIndex > 11){
18646                     this.vIndex = 11;
18647                 }
18648                 
18649                 if(isNaN(this.vIndex)){
18650                     this.vIndex = 0;
18651                 }
18652                 
18653                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18654                 break;
18655                 
18656             case 13: // enter
18657                 
18658                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18659                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18660                 }
18661                 
18662                 this.hide();
18663                 e.preventDefault();
18664                 break;
18665             case 9: // tab
18666                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18667                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18668                 }
18669                 this.hide();
18670                 break;
18671             case 16: // shift
18672             case 17: // ctrl
18673             case 18: // alt
18674                 break;
18675             default :
18676                 this.hide();
18677                 
18678         }
18679     },
18680     
18681     remove: function() 
18682     {
18683         this.picker().remove();
18684     }
18685    
18686 });
18687
18688 Roo.apply(Roo.bootstrap.MonthField,  {
18689     
18690     content : {
18691         tag: 'tbody',
18692         cn: [
18693         {
18694             tag: 'tr',
18695             cn: [
18696             {
18697                 tag: 'td',
18698                 colspan: '7'
18699             }
18700             ]
18701         }
18702         ]
18703     },
18704     
18705     dates:{
18706         en: {
18707             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18708             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18709         }
18710     }
18711 });
18712
18713 Roo.apply(Roo.bootstrap.MonthField,  {
18714   
18715     template : {
18716         tag: 'div',
18717         cls: 'datepicker dropdown-menu roo-dynamic',
18718         cn: [
18719             {
18720                 tag: 'div',
18721                 cls: 'datepicker-months',
18722                 cn: [
18723                 {
18724                     tag: 'table',
18725                     cls: 'table-condensed',
18726                     cn:[
18727                         Roo.bootstrap.DateField.content
18728                     ]
18729                 }
18730                 ]
18731             }
18732         ]
18733     }
18734 });
18735
18736  
18737
18738  
18739  /*
18740  * - LGPL
18741  *
18742  * CheckBox
18743  * 
18744  */
18745
18746 /**
18747  * @class Roo.bootstrap.CheckBox
18748  * @extends Roo.bootstrap.Input
18749  * Bootstrap CheckBox class
18750  * 
18751  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18752  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18753  * @cfg {String} boxLabel The text that appears beside the checkbox
18754  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18755  * @cfg {Boolean} checked initnal the element
18756  * @cfg {Boolean} inline inline the element (default false)
18757  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18758  * 
18759  * @constructor
18760  * Create a new CheckBox
18761  * @param {Object} config The config object
18762  */
18763
18764 Roo.bootstrap.CheckBox = function(config){
18765     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18766    
18767     this.addEvents({
18768         /**
18769         * @event check
18770         * Fires when the element is checked or unchecked.
18771         * @param {Roo.bootstrap.CheckBox} this This input
18772         * @param {Boolean} checked The new checked value
18773         */
18774        check : true
18775     });
18776     
18777 };
18778
18779 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18780   
18781     inputType: 'checkbox',
18782     inputValue: 1,
18783     valueOff: 0,
18784     boxLabel: false,
18785     checked: false,
18786     weight : false,
18787     inline: false,
18788     
18789     getAutoCreate : function()
18790     {
18791         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18792         
18793         var id = Roo.id();
18794         
18795         var cfg = {};
18796         
18797         cfg.cls = 'form-group ' + this.inputType; //input-group
18798         
18799         if(this.inline){
18800             cfg.cls += ' ' + this.inputType + '-inline';
18801         }
18802         
18803         var input =  {
18804             tag: 'input',
18805             id : id,
18806             type : this.inputType,
18807             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18808             cls : 'roo-' + this.inputType, //'form-box',
18809             placeholder : this.placeholder || ''
18810             
18811         };
18812         
18813         if (this.weight) { // Validity check?
18814             cfg.cls += " " + this.inputType + "-" + this.weight;
18815         }
18816         
18817         if (this.disabled) {
18818             input.disabled=true;
18819         }
18820         
18821         if(this.checked){
18822             input.checked = this.checked;
18823         }
18824         
18825         if (this.name) {
18826             input.name = this.name;
18827         }
18828         
18829         if (this.size) {
18830             input.cls += ' input-' + this.size;
18831         }
18832         
18833         var settings=this;
18834         
18835         ['xs','sm','md','lg'].map(function(size){
18836             if (settings[size]) {
18837                 cfg.cls += ' col-' + size + '-' + settings[size];
18838             }
18839         });
18840         
18841         var inputblock = input;
18842          
18843         if (this.before || this.after) {
18844             
18845             inputblock = {
18846                 cls : 'input-group',
18847                 cn :  [] 
18848             };
18849             
18850             if (this.before) {
18851                 inputblock.cn.push({
18852                     tag :'span',
18853                     cls : 'input-group-addon',
18854                     html : this.before
18855                 });
18856             }
18857             
18858             inputblock.cn.push(input);
18859             
18860             if (this.after) {
18861                 inputblock.cn.push({
18862                     tag :'span',
18863                     cls : 'input-group-addon',
18864                     html : this.after
18865                 });
18866             }
18867             
18868         }
18869         
18870         if (align ==='left' && this.fieldLabel.length) {
18871 //                Roo.log("left and has label");
18872                 cfg.cn = [
18873                     
18874                     {
18875                         tag: 'label',
18876                         'for' :  id,
18877                         cls : 'control-label col-md-' + this.labelWidth,
18878                         html : this.fieldLabel
18879                         
18880                     },
18881                     {
18882                         cls : "col-md-" + (12 - this.labelWidth), 
18883                         cn: [
18884                             inputblock
18885                         ]
18886                     }
18887                     
18888                 ];
18889         } else if ( this.fieldLabel.length) {
18890 //                Roo.log(" label");
18891                 cfg.cn = [
18892                    
18893                     {
18894                         tag: this.boxLabel ? 'span' : 'label',
18895                         'for': id,
18896                         cls: 'control-label box-input-label',
18897                         //cls : 'input-group-addon',
18898                         html : this.fieldLabel
18899                         
18900                     },
18901                     
18902                     inputblock
18903                     
18904                 ];
18905
18906         } else {
18907             
18908 //                Roo.log(" no label && no align");
18909                 cfg.cn = [  inputblock ] ;
18910                 
18911                 
18912         }
18913         
18914         if(this.boxLabel){
18915              var boxLabelCfg = {
18916                 tag: 'label',
18917                 //'for': id, // box label is handled by onclick - so no for...
18918                 cls: 'box-label',
18919                 html: this.boxLabel
18920             };
18921             
18922             if(this.tooltip){
18923                 boxLabelCfg.tooltip = this.tooltip;
18924             }
18925              
18926             cfg.cn.push(boxLabelCfg);
18927         }
18928         
18929         
18930        
18931         return cfg;
18932         
18933     },
18934     
18935     /**
18936      * return the real input element.
18937      */
18938     inputEl: function ()
18939     {
18940         return this.el.select('input.roo-' + this.inputType,true).first();
18941     },
18942     
18943     labelEl: function()
18944     {
18945         return this.el.select('label.control-label',true).first();
18946     },
18947     /* depricated... */
18948     
18949     label: function()
18950     {
18951         return this.labelEl();
18952     },
18953     
18954     boxLabelEl: function()
18955     {
18956         return this.el.select('label.box-label',true).first();
18957     },
18958     
18959     initEvents : function()
18960     {
18961 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18962         
18963         this.inputEl().on('click', this.onClick,  this);
18964         
18965         if (this.boxLabel) { 
18966             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18967         }
18968         
18969         this.startValue = this.getValue();
18970         
18971         if(this.groupId){
18972             Roo.bootstrap.CheckBox.register(this);
18973         }
18974     },
18975     
18976     onClick : function()
18977     {   
18978         this.setChecked(!this.checked);
18979     },
18980     
18981     setChecked : function(state,suppressEvent)
18982     {
18983         this.startValue = this.getValue();
18984         
18985         if(this.inputType == 'radio'){
18986             
18987             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18988                 e.dom.checked = false;
18989             });
18990             
18991             this.inputEl().dom.checked = true;
18992             
18993             this.inputEl().dom.value = this.inputValue;
18994             
18995             if(suppressEvent !== true){
18996                 this.fireEvent('check', this, true);
18997             }
18998             
18999             this.validate();
19000             
19001             return;
19002         }
19003         
19004         this.checked = state;
19005         
19006         this.inputEl().dom.checked = state;
19007         
19008         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19009         
19010         if(suppressEvent !== true){
19011             this.fireEvent('check', this, state);
19012         }
19013         
19014         this.validate();
19015     },
19016     
19017     getValue : function()
19018     {
19019         if(this.inputType == 'radio'){
19020             return this.getGroupValue();
19021         }
19022         
19023         return this.inputEl().getValue();
19024         
19025     },
19026     
19027     getGroupValue : function()
19028     {
19029         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19030             return '';
19031         }
19032         
19033         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19034     },
19035     
19036     setValue : function(v,suppressEvent)
19037     {
19038         if(this.inputType == 'radio'){
19039             this.setGroupValue(v, suppressEvent);
19040             return;
19041         }
19042         
19043         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19044         
19045         this.validate();
19046     },
19047     
19048     setGroupValue : function(v, suppressEvent)
19049     {
19050         this.startValue = this.getValue();
19051         
19052         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19053             e.dom.checked = false;
19054             
19055             if(e.dom.value == v){
19056                 e.dom.checked = true;
19057             }
19058         });
19059         
19060         if(suppressEvent !== true){
19061             this.fireEvent('check', this, true);
19062         }
19063
19064         this.validate();
19065         
19066         return;
19067     },
19068     
19069     validate : function()
19070     {
19071         if(
19072                 this.disabled || 
19073                 (this.inputType == 'radio' && this.validateRadio()) ||
19074                 (this.inputType == 'checkbox' && this.validateCheckbox())
19075         ){
19076             this.markValid();
19077             return true;
19078         }
19079         
19080         this.markInvalid();
19081         return false;
19082     },
19083     
19084     validateRadio : function()
19085     {
19086         var valid = false;
19087         
19088         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19089             if(!e.dom.checked){
19090                 return;
19091             }
19092             
19093             valid = true;
19094             
19095             return false;
19096         });
19097         
19098         return valid;
19099     },
19100     
19101     validateCheckbox : function()
19102     {
19103         if(!this.groupId){
19104             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19105         }
19106         
19107         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19108         
19109         if(!group){
19110             return false;
19111         }
19112         
19113         var r = false;
19114         
19115         for(var i in group){
19116             if(r){
19117                 break;
19118             }
19119             
19120             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19121         }
19122         
19123         return r;
19124     },
19125     
19126     /**
19127      * Mark this field as valid
19128      */
19129     markValid : function()
19130     {
19131         if(this.allowBlank){
19132             return;
19133         }
19134         
19135         var _this = this;
19136         
19137         this.fireEvent('valid', this);
19138         
19139         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19140         
19141         if(this.groupId){
19142             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19143         }
19144         
19145         if(label){
19146             label.markValid();
19147         }
19148         
19149         if(this.inputType == 'radio'){
19150             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19151                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19152                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19153             });
19154             
19155             return;
19156         }
19157         
19158         if(!this.groupId){
19159             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19160             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19161             return;
19162         }
19163         
19164         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19165             
19166         if(!group){
19167             return;
19168         }
19169         
19170         for(var i in group){
19171             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19172             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19173         }
19174     },
19175     
19176      /**
19177      * Mark this field as invalid
19178      * @param {String} msg The validation message
19179      */
19180     markInvalid : function(msg)
19181     {
19182         if(this.allowBlank){
19183             return;
19184         }
19185         
19186         var _this = this;
19187         
19188         this.fireEvent('invalid', this, msg);
19189         
19190         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19191         
19192         if(this.groupId){
19193             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19194         }
19195         
19196         if(label){
19197             label.markInvalid();
19198         }
19199             
19200         if(this.inputType == 'radio'){
19201             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19202                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19203                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19204             });
19205             
19206             return;
19207         }
19208         
19209         if(!this.groupId){
19210             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19211             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19212             return;
19213         }
19214         
19215         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19216         
19217         if(!group){
19218             return;
19219         }
19220         
19221         for(var i in group){
19222             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19223             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19224         }
19225         
19226     }
19227     
19228 });
19229
19230 Roo.apply(Roo.bootstrap.CheckBox, {
19231     
19232     groups: {},
19233     
19234      /**
19235     * register a CheckBox Group
19236     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19237     */
19238     register : function(checkbox)
19239     {
19240         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19241             this.groups[checkbox.groupId] = {};
19242         }
19243         
19244         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19245             return;
19246         }
19247         
19248         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19249         
19250     },
19251     /**
19252     * fetch a CheckBox Group based on the group ID
19253     * @param {string} the group ID
19254     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19255     */
19256     get: function(groupId) {
19257         if (typeof(this.groups[groupId]) == 'undefined') {
19258             return false;
19259         }
19260         
19261         return this.groups[groupId] ;
19262     }
19263     
19264     
19265 });
19266 /*
19267  * - LGPL
19268  *
19269  * Radio
19270  *
19271  *
19272  * not inline
19273  *<div class="radio">
19274   <label>
19275     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19276     Option one is this and that&mdash;be sure to include why it's great
19277   </label>
19278 </div>
19279  *
19280  *
19281  *inline
19282  *<span>
19283  *<label class="radio-inline">fieldLabel</label>
19284  *<label class="radio-inline">
19285   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19286 </label>
19287 <span>
19288  * 
19289  * 
19290  */
19291
19292 /**
19293  * @class Roo.bootstrap.Radio
19294  * @extends Roo.bootstrap.CheckBox
19295  * Bootstrap Radio class
19296
19297  * @constructor
19298  * Create a new Radio
19299  * @param {Object} config The config object
19300  */
19301
19302 Roo.bootstrap.Radio = function(config){
19303     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19304    
19305 };
19306
19307 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19308     
19309     inputType: 'radio',
19310     inputValue: '',
19311     valueOff: '',
19312     
19313     getAutoCreate : function()
19314     {
19315         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19316         align = align || 'left'; // default...
19317         
19318         
19319         
19320         var id = Roo.id();
19321         
19322         var cfg = {
19323                 tag : this.inline ? 'span' : 'div',
19324                 cls : '',
19325                 cn : []
19326         };
19327         
19328         var inline = this.inline ? ' radio-inline' : '';
19329         
19330         var lbl = {
19331                 tag: 'label' ,
19332                 // does not need for, as we wrap the input with it..
19333                 'for' : id,
19334                 cls : 'control-label box-label' + inline,
19335                 cn : []
19336         };
19337         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19338         
19339         var fieldLabel = {
19340             tag: 'label' ,
19341             //cls : 'control-label' + inline,
19342             html : this.fieldLabel,
19343             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19344         };
19345         
19346  
19347         
19348         
19349         var input =  {
19350             tag: 'input',
19351             id : id,
19352             type : this.inputType,
19353             //value : (!this.checked) ? this.valueOff : this.inputValue,
19354             value : this.inputValue,
19355             cls : 'roo-radio',
19356             placeholder : this.placeholder || '' // ?? needed????
19357             
19358         };
19359         if (this.weight) { // Validity check?
19360             input.cls += " radio-" + this.weight;
19361         }
19362         if (this.disabled) {
19363             input.disabled=true;
19364         }
19365         
19366         if(this.checked){
19367             input.checked = this.checked;
19368         }
19369         
19370         if (this.name) {
19371             input.name = this.name;
19372         }
19373         
19374         if (this.size) {
19375             input.cls += ' input-' + this.size;
19376         }
19377         
19378         //?? can span's inline have a width??
19379         
19380         var settings=this;
19381         ['xs','sm','md','lg'].map(function(size){
19382             if (settings[size]) {
19383                 cfg.cls += ' col-' + size + '-' + settings[size];
19384             }
19385         });
19386         
19387         var inputblock = input;
19388         
19389         if (this.before || this.after) {
19390             
19391             inputblock = {
19392                 cls : 'input-group',
19393                 tag : 'span',
19394                 cn :  [] 
19395             };
19396             if (this.before) {
19397                 inputblock.cn.push({
19398                     tag :'span',
19399                     cls : 'input-group-addon',
19400                     html : this.before
19401                 });
19402             }
19403             inputblock.cn.push(input);
19404             if (this.after) {
19405                 inputblock.cn.push({
19406                     tag :'span',
19407                     cls : 'input-group-addon',
19408                     html : this.after
19409                 });
19410             }
19411             
19412         };
19413         
19414         
19415         if (this.fieldLabel && this.fieldLabel.length) {
19416             cfg.cn.push(fieldLabel);
19417         }
19418        
19419         // normal bootstrap puts the input inside the label.
19420         // however with our styled version - it has to go after the input.
19421        
19422         //lbl.cn.push(inputblock);
19423         
19424         var lblwrap =  {
19425             tag: 'span',
19426             cls: 'radio' + inline,
19427             cn: [
19428                 inputblock,
19429                 lbl
19430             ]
19431         };
19432         
19433         cfg.cn.push( lblwrap);
19434         
19435         if(this.boxLabel){
19436             lbl.cn.push({
19437                 tag: 'span',
19438                 html: this.boxLabel
19439             })
19440         }
19441          
19442         
19443         return cfg;
19444         
19445     },
19446     
19447     initEvents : function()
19448     {
19449 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19450         
19451         this.inputEl().on('click', this.onClick,  this);
19452         if (this.boxLabel) {
19453             //Roo.log('find label');
19454             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19455         }
19456         
19457     },
19458     
19459     inputEl: function ()
19460     {
19461         return this.el.select('input.roo-radio',true).first();
19462     },
19463     onClick : function()
19464     {   
19465         Roo.log("click");
19466         this.setChecked(true);
19467     },
19468     
19469     setChecked : function(state,suppressEvent)
19470     {
19471         if(state){
19472             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19473                 v.dom.checked = false;
19474             });
19475         }
19476         Roo.log(this.inputEl().dom);
19477         this.checked = state;
19478         this.inputEl().dom.checked = state;
19479         
19480         if(suppressEvent !== true){
19481             this.fireEvent('check', this, state);
19482         }
19483         
19484         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19485         
19486     },
19487     
19488     getGroupValue : function()
19489     {
19490         var value = '';
19491         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19492             if(v.dom.checked == true){
19493                 value = v.dom.value;
19494             }
19495         });
19496         
19497         return value;
19498     },
19499     
19500     /**
19501      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19502      * @return {Mixed} value The field value
19503      */
19504     getValue : function(){
19505         return this.getGroupValue();
19506     }
19507     
19508 });
19509
19510  
19511 //<script type="text/javascript">
19512
19513 /*
19514  * Based  Ext JS Library 1.1.1
19515  * Copyright(c) 2006-2007, Ext JS, LLC.
19516  * LGPL
19517  *
19518  */
19519  
19520 /**
19521  * @class Roo.HtmlEditorCore
19522  * @extends Roo.Component
19523  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19524  *
19525  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19526  */
19527
19528 Roo.HtmlEditorCore = function(config){
19529     
19530     
19531     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19532     
19533     
19534     this.addEvents({
19535         /**
19536          * @event initialize
19537          * Fires when the editor is fully initialized (including the iframe)
19538          * @param {Roo.HtmlEditorCore} this
19539          */
19540         initialize: true,
19541         /**
19542          * @event activate
19543          * Fires when the editor is first receives the focus. Any insertion must wait
19544          * until after this event.
19545          * @param {Roo.HtmlEditorCore} this
19546          */
19547         activate: true,
19548          /**
19549          * @event beforesync
19550          * Fires before the textarea is updated with content from the editor iframe. Return false
19551          * to cancel the sync.
19552          * @param {Roo.HtmlEditorCore} this
19553          * @param {String} html
19554          */
19555         beforesync: true,
19556          /**
19557          * @event beforepush
19558          * Fires before the iframe editor is updated with content from the textarea. Return false
19559          * to cancel the push.
19560          * @param {Roo.HtmlEditorCore} this
19561          * @param {String} html
19562          */
19563         beforepush: true,
19564          /**
19565          * @event sync
19566          * Fires when the textarea is updated with content from the editor iframe.
19567          * @param {Roo.HtmlEditorCore} this
19568          * @param {String} html
19569          */
19570         sync: true,
19571          /**
19572          * @event push
19573          * Fires when the iframe editor is updated with content from the textarea.
19574          * @param {Roo.HtmlEditorCore} this
19575          * @param {String} html
19576          */
19577         push: true,
19578         
19579         /**
19580          * @event editorevent
19581          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19582          * @param {Roo.HtmlEditorCore} this
19583          */
19584         editorevent: true
19585         
19586     });
19587     
19588     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19589     
19590     // defaults : white / black...
19591     this.applyBlacklists();
19592     
19593     
19594     
19595 };
19596
19597
19598 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19599
19600
19601      /**
19602      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19603      */
19604     
19605     owner : false,
19606     
19607      /**
19608      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19609      *                        Roo.resizable.
19610      */
19611     resizable : false,
19612      /**
19613      * @cfg {Number} height (in pixels)
19614      */   
19615     height: 300,
19616    /**
19617      * @cfg {Number} width (in pixels)
19618      */   
19619     width: 500,
19620     
19621     /**
19622      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19623      * 
19624      */
19625     stylesheets: false,
19626     
19627     // id of frame..
19628     frameId: false,
19629     
19630     // private properties
19631     validationEvent : false,
19632     deferHeight: true,
19633     initialized : false,
19634     activated : false,
19635     sourceEditMode : false,
19636     onFocus : Roo.emptyFn,
19637     iframePad:3,
19638     hideMode:'offsets',
19639     
19640     clearUp: true,
19641     
19642     // blacklist + whitelisted elements..
19643     black: false,
19644     white: false,
19645      
19646     
19647
19648     /**
19649      * Protected method that will not generally be called directly. It
19650      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19651      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19652      */
19653     getDocMarkup : function(){
19654         // body styles..
19655         var st = '';
19656         
19657         // inherit styels from page...?? 
19658         if (this.stylesheets === false) {
19659             
19660             Roo.get(document.head).select('style').each(function(node) {
19661                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19662             });
19663             
19664             Roo.get(document.head).select('link').each(function(node) { 
19665                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19666             });
19667             
19668         } else if (!this.stylesheets.length) {
19669                 // simple..
19670                 st = '<style type="text/css">' +
19671                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19672                    '</style>';
19673         } else { 
19674             
19675         }
19676         
19677         st +=  '<style type="text/css">' +
19678             'IMG { cursor: pointer } ' +
19679         '</style>';
19680
19681         
19682         return '<html><head>' + st  +
19683             //<style type="text/css">' +
19684             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19685             //'</style>' +
19686             ' </head><body class="roo-htmleditor-body"></body></html>';
19687     },
19688
19689     // private
19690     onRender : function(ct, position)
19691     {
19692         var _t = this;
19693         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19694         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19695         
19696         
19697         this.el.dom.style.border = '0 none';
19698         this.el.dom.setAttribute('tabIndex', -1);
19699         this.el.addClass('x-hidden hide');
19700         
19701         
19702         
19703         if(Roo.isIE){ // fix IE 1px bogus margin
19704             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19705         }
19706        
19707         
19708         this.frameId = Roo.id();
19709         
19710          
19711         
19712         var iframe = this.owner.wrap.createChild({
19713             tag: 'iframe',
19714             cls: 'form-control', // bootstrap..
19715             id: this.frameId,
19716             name: this.frameId,
19717             frameBorder : 'no',
19718             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19719         }, this.el
19720         );
19721         
19722         
19723         this.iframe = iframe.dom;
19724
19725          this.assignDocWin();
19726         
19727         this.doc.designMode = 'on';
19728        
19729         this.doc.open();
19730         this.doc.write(this.getDocMarkup());
19731         this.doc.close();
19732
19733         
19734         var task = { // must defer to wait for browser to be ready
19735             run : function(){
19736                 //console.log("run task?" + this.doc.readyState);
19737                 this.assignDocWin();
19738                 if(this.doc.body || this.doc.readyState == 'complete'){
19739                     try {
19740                         this.doc.designMode="on";
19741                     } catch (e) {
19742                         return;
19743                     }
19744                     Roo.TaskMgr.stop(task);
19745                     this.initEditor.defer(10, this);
19746                 }
19747             },
19748             interval : 10,
19749             duration: 10000,
19750             scope: this
19751         };
19752         Roo.TaskMgr.start(task);
19753
19754     },
19755
19756     // private
19757     onResize : function(w, h)
19758     {
19759          Roo.log('resize: ' +w + ',' + h );
19760         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19761         if(!this.iframe){
19762             return;
19763         }
19764         if(typeof w == 'number'){
19765             
19766             this.iframe.style.width = w + 'px';
19767         }
19768         if(typeof h == 'number'){
19769             
19770             this.iframe.style.height = h + 'px';
19771             if(this.doc){
19772                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19773             }
19774         }
19775         
19776     },
19777
19778     /**
19779      * Toggles the editor between standard and source edit mode.
19780      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19781      */
19782     toggleSourceEdit : function(sourceEditMode){
19783         
19784         this.sourceEditMode = sourceEditMode === true;
19785         
19786         if(this.sourceEditMode){
19787  
19788             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19789             
19790         }else{
19791             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19792             //this.iframe.className = '';
19793             this.deferFocus();
19794         }
19795         //this.setSize(this.owner.wrap.getSize());
19796         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19797     },
19798
19799     
19800   
19801
19802     /**
19803      * Protected method that will not generally be called directly. If you need/want
19804      * custom HTML cleanup, this is the method you should override.
19805      * @param {String} html The HTML to be cleaned
19806      * return {String} The cleaned HTML
19807      */
19808     cleanHtml : function(html){
19809         html = String(html);
19810         if(html.length > 5){
19811             if(Roo.isSafari){ // strip safari nonsense
19812                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19813             }
19814         }
19815         if(html == '&nbsp;'){
19816             html = '';
19817         }
19818         return html;
19819     },
19820
19821     /**
19822      * HTML Editor -> Textarea
19823      * Protected method that will not generally be called directly. Syncs the contents
19824      * of the editor iframe with the textarea.
19825      */
19826     syncValue : function(){
19827         if(this.initialized){
19828             var bd = (this.doc.body || this.doc.documentElement);
19829             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19830             var html = bd.innerHTML;
19831             if(Roo.isSafari){
19832                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19833                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19834                 if(m && m[1]){
19835                     html = '<div style="'+m[0]+'">' + html + '</div>';
19836                 }
19837             }
19838             html = this.cleanHtml(html);
19839             // fix up the special chars.. normaly like back quotes in word...
19840             // however we do not want to do this with chinese..
19841             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19842                 var cc = b.charCodeAt();
19843                 if (
19844                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19845                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19846                     (cc >= 0xf900 && cc < 0xfb00 )
19847                 ) {
19848                         return b;
19849                 }
19850                 return "&#"+cc+";" 
19851             });
19852             if(this.owner.fireEvent('beforesync', this, html) !== false){
19853                 this.el.dom.value = html;
19854                 this.owner.fireEvent('sync', this, html);
19855             }
19856         }
19857     },
19858
19859     /**
19860      * Protected method that will not generally be called directly. Pushes the value of the textarea
19861      * into the iframe editor.
19862      */
19863     pushValue : function(){
19864         if(this.initialized){
19865             var v = this.el.dom.value.trim();
19866             
19867 //            if(v.length < 1){
19868 //                v = '&#160;';
19869 //            }
19870             
19871             if(this.owner.fireEvent('beforepush', this, v) !== false){
19872                 var d = (this.doc.body || this.doc.documentElement);
19873                 d.innerHTML = v;
19874                 this.cleanUpPaste();
19875                 this.el.dom.value = d.innerHTML;
19876                 this.owner.fireEvent('push', this, v);
19877             }
19878         }
19879     },
19880
19881     // private
19882     deferFocus : function(){
19883         this.focus.defer(10, this);
19884     },
19885
19886     // doc'ed in Field
19887     focus : function(){
19888         if(this.win && !this.sourceEditMode){
19889             this.win.focus();
19890         }else{
19891             this.el.focus();
19892         }
19893     },
19894     
19895     assignDocWin: function()
19896     {
19897         var iframe = this.iframe;
19898         
19899          if(Roo.isIE){
19900             this.doc = iframe.contentWindow.document;
19901             this.win = iframe.contentWindow;
19902         } else {
19903 //            if (!Roo.get(this.frameId)) {
19904 //                return;
19905 //            }
19906 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19907 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19908             
19909             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19910                 return;
19911             }
19912             
19913             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19914             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19915         }
19916     },
19917     
19918     // private
19919     initEditor : function(){
19920         //console.log("INIT EDITOR");
19921         this.assignDocWin();
19922         
19923         
19924         
19925         this.doc.designMode="on";
19926         this.doc.open();
19927         this.doc.write(this.getDocMarkup());
19928         this.doc.close();
19929         
19930         var dbody = (this.doc.body || this.doc.documentElement);
19931         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19932         // this copies styles from the containing element into thsi one..
19933         // not sure why we need all of this..
19934         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19935         
19936         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19937         //ss['background-attachment'] = 'fixed'; // w3c
19938         dbody.bgProperties = 'fixed'; // ie
19939         //Roo.DomHelper.applyStyles(dbody, ss);
19940         Roo.EventManager.on(this.doc, {
19941             //'mousedown': this.onEditorEvent,
19942             'mouseup': this.onEditorEvent,
19943             'dblclick': this.onEditorEvent,
19944             'click': this.onEditorEvent,
19945             'keyup': this.onEditorEvent,
19946             buffer:100,
19947             scope: this
19948         });
19949         if(Roo.isGecko){
19950             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19951         }
19952         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19953             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19954         }
19955         this.initialized = true;
19956
19957         this.owner.fireEvent('initialize', this);
19958         this.pushValue();
19959     },
19960
19961     // private
19962     onDestroy : function(){
19963         
19964         
19965         
19966         if(this.rendered){
19967             
19968             //for (var i =0; i < this.toolbars.length;i++) {
19969             //    // fixme - ask toolbars for heights?
19970             //    this.toolbars[i].onDestroy();
19971            // }
19972             
19973             //this.wrap.dom.innerHTML = '';
19974             //this.wrap.remove();
19975         }
19976     },
19977
19978     // private
19979     onFirstFocus : function(){
19980         
19981         this.assignDocWin();
19982         
19983         
19984         this.activated = true;
19985          
19986     
19987         if(Roo.isGecko){ // prevent silly gecko errors
19988             this.win.focus();
19989             var s = this.win.getSelection();
19990             if(!s.focusNode || s.focusNode.nodeType != 3){
19991                 var r = s.getRangeAt(0);
19992                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19993                 r.collapse(true);
19994                 this.deferFocus();
19995             }
19996             try{
19997                 this.execCmd('useCSS', true);
19998                 this.execCmd('styleWithCSS', false);
19999             }catch(e){}
20000         }
20001         this.owner.fireEvent('activate', this);
20002     },
20003
20004     // private
20005     adjustFont: function(btn){
20006         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20007         //if(Roo.isSafari){ // safari
20008         //    adjust *= 2;
20009        // }
20010         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20011         if(Roo.isSafari){ // safari
20012             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20013             v =  (v < 10) ? 10 : v;
20014             v =  (v > 48) ? 48 : v;
20015             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20016             
20017         }
20018         
20019         
20020         v = Math.max(1, v+adjust);
20021         
20022         this.execCmd('FontSize', v  );
20023     },
20024
20025     onEditorEvent : function(e)
20026     {
20027         this.owner.fireEvent('editorevent', this, e);
20028       //  this.updateToolbar();
20029         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20030     },
20031
20032     insertTag : function(tg)
20033     {
20034         // could be a bit smarter... -> wrap the current selected tRoo..
20035         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20036             
20037             range = this.createRange(this.getSelection());
20038             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20039             wrappingNode.appendChild(range.extractContents());
20040             range.insertNode(wrappingNode);
20041
20042             return;
20043             
20044             
20045             
20046         }
20047         this.execCmd("formatblock",   tg);
20048         
20049     },
20050     
20051     insertText : function(txt)
20052     {
20053         
20054         
20055         var range = this.createRange();
20056         range.deleteContents();
20057                //alert(Sender.getAttribute('label'));
20058                
20059         range.insertNode(this.doc.createTextNode(txt));
20060     } ,
20061     
20062      
20063
20064     /**
20065      * Executes a Midas editor command on the editor document and performs necessary focus and
20066      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20067      * @param {String} cmd The Midas command
20068      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20069      */
20070     relayCmd : function(cmd, value){
20071         this.win.focus();
20072         this.execCmd(cmd, value);
20073         this.owner.fireEvent('editorevent', this);
20074         //this.updateToolbar();
20075         this.owner.deferFocus();
20076     },
20077
20078     /**
20079      * Executes a Midas editor command directly on the editor document.
20080      * For visual commands, you should use {@link #relayCmd} instead.
20081      * <b>This should only be called after the editor is initialized.</b>
20082      * @param {String} cmd The Midas command
20083      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20084      */
20085     execCmd : function(cmd, value){
20086         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20087         this.syncValue();
20088     },
20089  
20090  
20091    
20092     /**
20093      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20094      * to insert tRoo.
20095      * @param {String} text | dom node.. 
20096      */
20097     insertAtCursor : function(text)
20098     {
20099         
20100         
20101         
20102         if(!this.activated){
20103             return;
20104         }
20105         /*
20106         if(Roo.isIE){
20107             this.win.focus();
20108             var r = this.doc.selection.createRange();
20109             if(r){
20110                 r.collapse(true);
20111                 r.pasteHTML(text);
20112                 this.syncValue();
20113                 this.deferFocus();
20114             
20115             }
20116             return;
20117         }
20118         */
20119         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20120             this.win.focus();
20121             
20122             
20123             // from jquery ui (MIT licenced)
20124             var range, node;
20125             var win = this.win;
20126             
20127             if (win.getSelection && win.getSelection().getRangeAt) {
20128                 range = win.getSelection().getRangeAt(0);
20129                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20130                 range.insertNode(node);
20131             } else if (win.document.selection && win.document.selection.createRange) {
20132                 // no firefox support
20133                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20134                 win.document.selection.createRange().pasteHTML(txt);
20135             } else {
20136                 // no firefox support
20137                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20138                 this.execCmd('InsertHTML', txt);
20139             } 
20140             
20141             this.syncValue();
20142             
20143             this.deferFocus();
20144         }
20145     },
20146  // private
20147     mozKeyPress : function(e){
20148         if(e.ctrlKey){
20149             var c = e.getCharCode(), cmd;
20150           
20151             if(c > 0){
20152                 c = String.fromCharCode(c).toLowerCase();
20153                 switch(c){
20154                     case 'b':
20155                         cmd = 'bold';
20156                         break;
20157                     case 'i':
20158                         cmd = 'italic';
20159                         break;
20160                     
20161                     case 'u':
20162                         cmd = 'underline';
20163                         break;
20164                     
20165                     case 'v':
20166                         this.cleanUpPaste.defer(100, this);
20167                         return;
20168                         
20169                 }
20170                 if(cmd){
20171                     this.win.focus();
20172                     this.execCmd(cmd);
20173                     this.deferFocus();
20174                     e.preventDefault();
20175                 }
20176                 
20177             }
20178         }
20179     },
20180
20181     // private
20182     fixKeys : function(){ // load time branching for fastest keydown performance
20183         if(Roo.isIE){
20184             return function(e){
20185                 var k = e.getKey(), r;
20186                 if(k == e.TAB){
20187                     e.stopEvent();
20188                     r = this.doc.selection.createRange();
20189                     if(r){
20190                         r.collapse(true);
20191                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20192                         this.deferFocus();
20193                     }
20194                     return;
20195                 }
20196                 
20197                 if(k == e.ENTER){
20198                     r = this.doc.selection.createRange();
20199                     if(r){
20200                         var target = r.parentElement();
20201                         if(!target || target.tagName.toLowerCase() != 'li'){
20202                             e.stopEvent();
20203                             r.pasteHTML('<br />');
20204                             r.collapse(false);
20205                             r.select();
20206                         }
20207                     }
20208                 }
20209                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20210                     this.cleanUpPaste.defer(100, this);
20211                     return;
20212                 }
20213                 
20214                 
20215             };
20216         }else if(Roo.isOpera){
20217             return function(e){
20218                 var k = e.getKey();
20219                 if(k == e.TAB){
20220                     e.stopEvent();
20221                     this.win.focus();
20222                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20223                     this.deferFocus();
20224                 }
20225                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20226                     this.cleanUpPaste.defer(100, this);
20227                     return;
20228                 }
20229                 
20230             };
20231         }else if(Roo.isSafari){
20232             return function(e){
20233                 var k = e.getKey();
20234                 
20235                 if(k == e.TAB){
20236                     e.stopEvent();
20237                     this.execCmd('InsertText','\t');
20238                     this.deferFocus();
20239                     return;
20240                 }
20241                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20242                     this.cleanUpPaste.defer(100, this);
20243                     return;
20244                 }
20245                 
20246              };
20247         }
20248     }(),
20249     
20250     getAllAncestors: function()
20251     {
20252         var p = this.getSelectedNode();
20253         var a = [];
20254         if (!p) {
20255             a.push(p); // push blank onto stack..
20256             p = this.getParentElement();
20257         }
20258         
20259         
20260         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20261             a.push(p);
20262             p = p.parentNode;
20263         }
20264         a.push(this.doc.body);
20265         return a;
20266     },
20267     lastSel : false,
20268     lastSelNode : false,
20269     
20270     
20271     getSelection : function() 
20272     {
20273         this.assignDocWin();
20274         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20275     },
20276     
20277     getSelectedNode: function() 
20278     {
20279         // this may only work on Gecko!!!
20280         
20281         // should we cache this!!!!
20282         
20283         
20284         
20285          
20286         var range = this.createRange(this.getSelection()).cloneRange();
20287         
20288         if (Roo.isIE) {
20289             var parent = range.parentElement();
20290             while (true) {
20291                 var testRange = range.duplicate();
20292                 testRange.moveToElementText(parent);
20293                 if (testRange.inRange(range)) {
20294                     break;
20295                 }
20296                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20297                     break;
20298                 }
20299                 parent = parent.parentElement;
20300             }
20301             return parent;
20302         }
20303         
20304         // is ancestor a text element.
20305         var ac =  range.commonAncestorContainer;
20306         if (ac.nodeType == 3) {
20307             ac = ac.parentNode;
20308         }
20309         
20310         var ar = ac.childNodes;
20311          
20312         var nodes = [];
20313         var other_nodes = [];
20314         var has_other_nodes = false;
20315         for (var i=0;i<ar.length;i++) {
20316             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20317                 continue;
20318             }
20319             // fullly contained node.
20320             
20321             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20322                 nodes.push(ar[i]);
20323                 continue;
20324             }
20325             
20326             // probably selected..
20327             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20328                 other_nodes.push(ar[i]);
20329                 continue;
20330             }
20331             // outer..
20332             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20333                 continue;
20334             }
20335             
20336             
20337             has_other_nodes = true;
20338         }
20339         if (!nodes.length && other_nodes.length) {
20340             nodes= other_nodes;
20341         }
20342         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20343             return false;
20344         }
20345         
20346         return nodes[0];
20347     },
20348     createRange: function(sel)
20349     {
20350         // this has strange effects when using with 
20351         // top toolbar - not sure if it's a great idea.
20352         //this.editor.contentWindow.focus();
20353         if (typeof sel != "undefined") {
20354             try {
20355                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20356             } catch(e) {
20357                 return this.doc.createRange();
20358             }
20359         } else {
20360             return this.doc.createRange();
20361         }
20362     },
20363     getParentElement: function()
20364     {
20365         
20366         this.assignDocWin();
20367         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20368         
20369         var range = this.createRange(sel);
20370          
20371         try {
20372             var p = range.commonAncestorContainer;
20373             while (p.nodeType == 3) { // text node
20374                 p = p.parentNode;
20375             }
20376             return p;
20377         } catch (e) {
20378             return null;
20379         }
20380     
20381     },
20382     /***
20383      *
20384      * Range intersection.. the hard stuff...
20385      *  '-1' = before
20386      *  '0' = hits..
20387      *  '1' = after.
20388      *         [ -- selected range --- ]
20389      *   [fail]                        [fail]
20390      *
20391      *    basically..
20392      *      if end is before start or  hits it. fail.
20393      *      if start is after end or hits it fail.
20394      *
20395      *   if either hits (but other is outside. - then it's not 
20396      *   
20397      *    
20398      **/
20399     
20400     
20401     // @see http://www.thismuchiknow.co.uk/?p=64.
20402     rangeIntersectsNode : function(range, node)
20403     {
20404         var nodeRange = node.ownerDocument.createRange();
20405         try {
20406             nodeRange.selectNode(node);
20407         } catch (e) {
20408             nodeRange.selectNodeContents(node);
20409         }
20410     
20411         var rangeStartRange = range.cloneRange();
20412         rangeStartRange.collapse(true);
20413     
20414         var rangeEndRange = range.cloneRange();
20415         rangeEndRange.collapse(false);
20416     
20417         var nodeStartRange = nodeRange.cloneRange();
20418         nodeStartRange.collapse(true);
20419     
20420         var nodeEndRange = nodeRange.cloneRange();
20421         nodeEndRange.collapse(false);
20422     
20423         return rangeStartRange.compareBoundaryPoints(
20424                  Range.START_TO_START, nodeEndRange) == -1 &&
20425                rangeEndRange.compareBoundaryPoints(
20426                  Range.START_TO_START, nodeStartRange) == 1;
20427         
20428          
20429     },
20430     rangeCompareNode : function(range, node)
20431     {
20432         var nodeRange = node.ownerDocument.createRange();
20433         try {
20434             nodeRange.selectNode(node);
20435         } catch (e) {
20436             nodeRange.selectNodeContents(node);
20437         }
20438         
20439         
20440         range.collapse(true);
20441     
20442         nodeRange.collapse(true);
20443      
20444         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20445         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20446          
20447         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20448         
20449         var nodeIsBefore   =  ss == 1;
20450         var nodeIsAfter    = ee == -1;
20451         
20452         if (nodeIsBefore && nodeIsAfter) {
20453             return 0; // outer
20454         }
20455         if (!nodeIsBefore && nodeIsAfter) {
20456             return 1; //right trailed.
20457         }
20458         
20459         if (nodeIsBefore && !nodeIsAfter) {
20460             return 2;  // left trailed.
20461         }
20462         // fully contined.
20463         return 3;
20464     },
20465
20466     // private? - in a new class?
20467     cleanUpPaste :  function()
20468     {
20469         // cleans up the whole document..
20470         Roo.log('cleanuppaste');
20471         
20472         this.cleanUpChildren(this.doc.body);
20473         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20474         if (clean != this.doc.body.innerHTML) {
20475             this.doc.body.innerHTML = clean;
20476         }
20477         
20478     },
20479     
20480     cleanWordChars : function(input) {// change the chars to hex code
20481         var he = Roo.HtmlEditorCore;
20482         
20483         var output = input;
20484         Roo.each(he.swapCodes, function(sw) { 
20485             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20486             
20487             output = output.replace(swapper, sw[1]);
20488         });
20489         
20490         return output;
20491     },
20492     
20493     
20494     cleanUpChildren : function (n)
20495     {
20496         if (!n.childNodes.length) {
20497             return;
20498         }
20499         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20500            this.cleanUpChild(n.childNodes[i]);
20501         }
20502     },
20503     
20504     
20505         
20506     
20507     cleanUpChild : function (node)
20508     {
20509         var ed = this;
20510         //console.log(node);
20511         if (node.nodeName == "#text") {
20512             // clean up silly Windows -- stuff?
20513             return; 
20514         }
20515         if (node.nodeName == "#comment") {
20516             node.parentNode.removeChild(node);
20517             // clean up silly Windows -- stuff?
20518             return; 
20519         }
20520         var lcname = node.tagName.toLowerCase();
20521         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20522         // whitelist of tags..
20523         
20524         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20525             // remove node.
20526             node.parentNode.removeChild(node);
20527             return;
20528             
20529         }
20530         
20531         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20532         
20533         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20534         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20535         
20536         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20537         //    remove_keep_children = true;
20538         //}
20539         
20540         if (remove_keep_children) {
20541             this.cleanUpChildren(node);
20542             // inserts everything just before this node...
20543             while (node.childNodes.length) {
20544                 var cn = node.childNodes[0];
20545                 node.removeChild(cn);
20546                 node.parentNode.insertBefore(cn, node);
20547             }
20548             node.parentNode.removeChild(node);
20549             return;
20550         }
20551         
20552         if (!node.attributes || !node.attributes.length) {
20553             this.cleanUpChildren(node);
20554             return;
20555         }
20556         
20557         function cleanAttr(n,v)
20558         {
20559             
20560             if (v.match(/^\./) || v.match(/^\//)) {
20561                 return;
20562             }
20563             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20564                 return;
20565             }
20566             if (v.match(/^#/)) {
20567                 return;
20568             }
20569 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20570             node.removeAttribute(n);
20571             
20572         }
20573         
20574         var cwhite = this.cwhite;
20575         var cblack = this.cblack;
20576             
20577         function cleanStyle(n,v)
20578         {
20579             if (v.match(/expression/)) { //XSS?? should we even bother..
20580                 node.removeAttribute(n);
20581                 return;
20582             }
20583             
20584             var parts = v.split(/;/);
20585             var clean = [];
20586             
20587             Roo.each(parts, function(p) {
20588                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20589                 if (!p.length) {
20590                     return true;
20591                 }
20592                 var l = p.split(':').shift().replace(/\s+/g,'');
20593                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20594                 
20595                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20596 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20597                     //node.removeAttribute(n);
20598                     return true;
20599                 }
20600                 //Roo.log()
20601                 // only allow 'c whitelisted system attributes'
20602                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20603 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20604                     //node.removeAttribute(n);
20605                     return true;
20606                 }
20607                 
20608                 
20609                  
20610                 
20611                 clean.push(p);
20612                 return true;
20613             });
20614             if (clean.length) { 
20615                 node.setAttribute(n, clean.join(';'));
20616             } else {
20617                 node.removeAttribute(n);
20618             }
20619             
20620         }
20621         
20622         
20623         for (var i = node.attributes.length-1; i > -1 ; i--) {
20624             var a = node.attributes[i];
20625             //console.log(a);
20626             
20627             if (a.name.toLowerCase().substr(0,2)=='on')  {
20628                 node.removeAttribute(a.name);
20629                 continue;
20630             }
20631             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20632                 node.removeAttribute(a.name);
20633                 continue;
20634             }
20635             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20636                 cleanAttr(a.name,a.value); // fixme..
20637                 continue;
20638             }
20639             if (a.name == 'style') {
20640                 cleanStyle(a.name,a.value);
20641                 continue;
20642             }
20643             /// clean up MS crap..
20644             // tecnically this should be a list of valid class'es..
20645             
20646             
20647             if (a.name == 'class') {
20648                 if (a.value.match(/^Mso/)) {
20649                     node.className = '';
20650                 }
20651                 
20652                 if (a.value.match(/body/)) {
20653                     node.className = '';
20654                 }
20655                 continue;
20656             }
20657             
20658             // style cleanup!?
20659             // class cleanup?
20660             
20661         }
20662         
20663         
20664         this.cleanUpChildren(node);
20665         
20666         
20667     },
20668     
20669     /**
20670      * Clean up MS wordisms...
20671      */
20672     cleanWord : function(node)
20673     {
20674         
20675         
20676         if (!node) {
20677             this.cleanWord(this.doc.body);
20678             return;
20679         }
20680         if (node.nodeName == "#text") {
20681             // clean up silly Windows -- stuff?
20682             return; 
20683         }
20684         if (node.nodeName == "#comment") {
20685             node.parentNode.removeChild(node);
20686             // clean up silly Windows -- stuff?
20687             return; 
20688         }
20689         
20690         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20691             node.parentNode.removeChild(node);
20692             return;
20693         }
20694         
20695         // remove - but keep children..
20696         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20697             while (node.childNodes.length) {
20698                 var cn = node.childNodes[0];
20699                 node.removeChild(cn);
20700                 node.parentNode.insertBefore(cn, node);
20701             }
20702             node.parentNode.removeChild(node);
20703             this.iterateChildren(node, this.cleanWord);
20704             return;
20705         }
20706         // clean styles
20707         if (node.className.length) {
20708             
20709             var cn = node.className.split(/\W+/);
20710             var cna = [];
20711             Roo.each(cn, function(cls) {
20712                 if (cls.match(/Mso[a-zA-Z]+/)) {
20713                     return;
20714                 }
20715                 cna.push(cls);
20716             });
20717             node.className = cna.length ? cna.join(' ') : '';
20718             if (!cna.length) {
20719                 node.removeAttribute("class");
20720             }
20721         }
20722         
20723         if (node.hasAttribute("lang")) {
20724             node.removeAttribute("lang");
20725         }
20726         
20727         if (node.hasAttribute("style")) {
20728             
20729             var styles = node.getAttribute("style").split(";");
20730             var nstyle = [];
20731             Roo.each(styles, function(s) {
20732                 if (!s.match(/:/)) {
20733                     return;
20734                 }
20735                 var kv = s.split(":");
20736                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20737                     return;
20738                 }
20739                 // what ever is left... we allow.
20740                 nstyle.push(s);
20741             });
20742             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20743             if (!nstyle.length) {
20744                 node.removeAttribute('style');
20745             }
20746         }
20747         this.iterateChildren(node, this.cleanWord);
20748         
20749         
20750         
20751     },
20752     /**
20753      * iterateChildren of a Node, calling fn each time, using this as the scole..
20754      * @param {DomNode} node node to iterate children of.
20755      * @param {Function} fn method of this class to call on each item.
20756      */
20757     iterateChildren : function(node, fn)
20758     {
20759         if (!node.childNodes.length) {
20760                 return;
20761         }
20762         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20763            fn.call(this, node.childNodes[i])
20764         }
20765     },
20766     
20767     
20768     /**
20769      * cleanTableWidths.
20770      *
20771      * Quite often pasting from word etc.. results in tables with column and widths.
20772      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20773      *
20774      */
20775     cleanTableWidths : function(node)
20776     {
20777          
20778          
20779         if (!node) {
20780             this.cleanTableWidths(this.doc.body);
20781             return;
20782         }
20783         
20784         // ignore list...
20785         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20786             return; 
20787         }
20788         Roo.log(node.tagName);
20789         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20790             this.iterateChildren(node, this.cleanTableWidths);
20791             return;
20792         }
20793         if (node.hasAttribute('width')) {
20794             node.removeAttribute('width');
20795         }
20796         
20797          
20798         if (node.hasAttribute("style")) {
20799             // pretty basic...
20800             
20801             var styles = node.getAttribute("style").split(";");
20802             var nstyle = [];
20803             Roo.each(styles, function(s) {
20804                 if (!s.match(/:/)) {
20805                     return;
20806                 }
20807                 var kv = s.split(":");
20808                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20809                     return;
20810                 }
20811                 // what ever is left... we allow.
20812                 nstyle.push(s);
20813             });
20814             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20815             if (!nstyle.length) {
20816                 node.removeAttribute('style');
20817             }
20818         }
20819         
20820         this.iterateChildren(node, this.cleanTableWidths);
20821         
20822         
20823     },
20824     
20825     
20826     
20827     
20828     domToHTML : function(currentElement, depth, nopadtext) {
20829         
20830         depth = depth || 0;
20831         nopadtext = nopadtext || false;
20832     
20833         if (!currentElement) {
20834             return this.domToHTML(this.doc.body);
20835         }
20836         
20837         //Roo.log(currentElement);
20838         var j;
20839         var allText = false;
20840         var nodeName = currentElement.nodeName;
20841         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20842         
20843         if  (nodeName == '#text') {
20844             
20845             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20846         }
20847         
20848         
20849         var ret = '';
20850         if (nodeName != 'BODY') {
20851              
20852             var i = 0;
20853             // Prints the node tagName, such as <A>, <IMG>, etc
20854             if (tagName) {
20855                 var attr = [];
20856                 for(i = 0; i < currentElement.attributes.length;i++) {
20857                     // quoting?
20858                     var aname = currentElement.attributes.item(i).name;
20859                     if (!currentElement.attributes.item(i).value.length) {
20860                         continue;
20861                     }
20862                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20863                 }
20864                 
20865                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20866             } 
20867             else {
20868                 
20869                 // eack
20870             }
20871         } else {
20872             tagName = false;
20873         }
20874         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20875             return ret;
20876         }
20877         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20878             nopadtext = true;
20879         }
20880         
20881         
20882         // Traverse the tree
20883         i = 0;
20884         var currentElementChild = currentElement.childNodes.item(i);
20885         var allText = true;
20886         var innerHTML  = '';
20887         lastnode = '';
20888         while (currentElementChild) {
20889             // Formatting code (indent the tree so it looks nice on the screen)
20890             var nopad = nopadtext;
20891             if (lastnode == 'SPAN') {
20892                 nopad  = true;
20893             }
20894             // text
20895             if  (currentElementChild.nodeName == '#text') {
20896                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20897                 toadd = nopadtext ? toadd : toadd.trim();
20898                 if (!nopad && toadd.length > 80) {
20899                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20900                 }
20901                 innerHTML  += toadd;
20902                 
20903                 i++;
20904                 currentElementChild = currentElement.childNodes.item(i);
20905                 lastNode = '';
20906                 continue;
20907             }
20908             allText = false;
20909             
20910             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20911                 
20912             // Recursively traverse the tree structure of the child node
20913             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20914             lastnode = currentElementChild.nodeName;
20915             i++;
20916             currentElementChild=currentElement.childNodes.item(i);
20917         }
20918         
20919         ret += innerHTML;
20920         
20921         if (!allText) {
20922                 // The remaining code is mostly for formatting the tree
20923             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20924         }
20925         
20926         
20927         if (tagName) {
20928             ret+= "</"+tagName+">";
20929         }
20930         return ret;
20931         
20932     },
20933         
20934     applyBlacklists : function()
20935     {
20936         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20937         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20938         
20939         this.white = [];
20940         this.black = [];
20941         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20942             if (b.indexOf(tag) > -1) {
20943                 return;
20944             }
20945             this.white.push(tag);
20946             
20947         }, this);
20948         
20949         Roo.each(w, function(tag) {
20950             if (b.indexOf(tag) > -1) {
20951                 return;
20952             }
20953             if (this.white.indexOf(tag) > -1) {
20954                 return;
20955             }
20956             this.white.push(tag);
20957             
20958         }, this);
20959         
20960         
20961         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20962             if (w.indexOf(tag) > -1) {
20963                 return;
20964             }
20965             this.black.push(tag);
20966             
20967         }, this);
20968         
20969         Roo.each(b, function(tag) {
20970             if (w.indexOf(tag) > -1) {
20971                 return;
20972             }
20973             if (this.black.indexOf(tag) > -1) {
20974                 return;
20975             }
20976             this.black.push(tag);
20977             
20978         }, this);
20979         
20980         
20981         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20982         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20983         
20984         this.cwhite = [];
20985         this.cblack = [];
20986         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20987             if (b.indexOf(tag) > -1) {
20988                 return;
20989             }
20990             this.cwhite.push(tag);
20991             
20992         }, this);
20993         
20994         Roo.each(w, function(tag) {
20995             if (b.indexOf(tag) > -1) {
20996                 return;
20997             }
20998             if (this.cwhite.indexOf(tag) > -1) {
20999                 return;
21000             }
21001             this.cwhite.push(tag);
21002             
21003         }, this);
21004         
21005         
21006         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21007             if (w.indexOf(tag) > -1) {
21008                 return;
21009             }
21010             this.cblack.push(tag);
21011             
21012         }, this);
21013         
21014         Roo.each(b, function(tag) {
21015             if (w.indexOf(tag) > -1) {
21016                 return;
21017             }
21018             if (this.cblack.indexOf(tag) > -1) {
21019                 return;
21020             }
21021             this.cblack.push(tag);
21022             
21023         }, this);
21024     },
21025     
21026     setStylesheets : function(stylesheets)
21027     {
21028         if(typeof(stylesheets) == 'string'){
21029             Roo.get(this.iframe.contentDocument.head).createChild({
21030                 tag : 'link',
21031                 rel : 'stylesheet',
21032                 type : 'text/css',
21033                 href : stylesheets
21034             });
21035             
21036             return;
21037         }
21038         var _this = this;
21039      
21040         Roo.each(stylesheets, function(s) {
21041             if(!s.length){
21042                 return;
21043             }
21044             
21045             Roo.get(_this.iframe.contentDocument.head).createChild({
21046                 tag : 'link',
21047                 rel : 'stylesheet',
21048                 type : 'text/css',
21049                 href : s
21050             });
21051         });
21052
21053         
21054     },
21055     
21056     removeStylesheets : function()
21057     {
21058         var _this = this;
21059         
21060         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21061             s.remove();
21062         });
21063     }
21064     
21065     // hide stuff that is not compatible
21066     /**
21067      * @event blur
21068      * @hide
21069      */
21070     /**
21071      * @event change
21072      * @hide
21073      */
21074     /**
21075      * @event focus
21076      * @hide
21077      */
21078     /**
21079      * @event specialkey
21080      * @hide
21081      */
21082     /**
21083      * @cfg {String} fieldClass @hide
21084      */
21085     /**
21086      * @cfg {String} focusClass @hide
21087      */
21088     /**
21089      * @cfg {String} autoCreate @hide
21090      */
21091     /**
21092      * @cfg {String} inputType @hide
21093      */
21094     /**
21095      * @cfg {String} invalidClass @hide
21096      */
21097     /**
21098      * @cfg {String} invalidText @hide
21099      */
21100     /**
21101      * @cfg {String} msgFx @hide
21102      */
21103     /**
21104      * @cfg {String} validateOnBlur @hide
21105      */
21106 });
21107
21108 Roo.HtmlEditorCore.white = [
21109         'area', 'br', 'img', 'input', 'hr', 'wbr',
21110         
21111        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21112        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21113        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21114        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21115        'table',   'ul',         'xmp', 
21116        
21117        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21118       'thead',   'tr', 
21119      
21120       'dir', 'menu', 'ol', 'ul', 'dl',
21121        
21122       'embed',  'object'
21123 ];
21124
21125
21126 Roo.HtmlEditorCore.black = [
21127     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21128         'applet', // 
21129         'base',   'basefont', 'bgsound', 'blink',  'body', 
21130         'frame',  'frameset', 'head',    'html',   'ilayer', 
21131         'iframe', 'layer',  'link',     'meta',    'object',   
21132         'script', 'style' ,'title',  'xml' // clean later..
21133 ];
21134 Roo.HtmlEditorCore.clean = [
21135     'script', 'style', 'title', 'xml'
21136 ];
21137 Roo.HtmlEditorCore.remove = [
21138     'font'
21139 ];
21140 // attributes..
21141
21142 Roo.HtmlEditorCore.ablack = [
21143     'on'
21144 ];
21145     
21146 Roo.HtmlEditorCore.aclean = [ 
21147     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21148 ];
21149
21150 // protocols..
21151 Roo.HtmlEditorCore.pwhite= [
21152         'http',  'https',  'mailto'
21153 ];
21154
21155 // white listed style attributes.
21156 Roo.HtmlEditorCore.cwhite= [
21157       //  'text-align', /// default is to allow most things..
21158       
21159          
21160 //        'font-size'//??
21161 ];
21162
21163 // black listed style attributes.
21164 Roo.HtmlEditorCore.cblack= [
21165       //  'font-size' -- this can be set by the project 
21166 ];
21167
21168
21169 Roo.HtmlEditorCore.swapCodes   =[ 
21170     [    8211, "--" ], 
21171     [    8212, "--" ], 
21172     [    8216,  "'" ],  
21173     [    8217, "'" ],  
21174     [    8220, '"' ],  
21175     [    8221, '"' ],  
21176     [    8226, "*" ],  
21177     [    8230, "..." ]
21178 ]; 
21179
21180     /*
21181  * - LGPL
21182  *
21183  * HtmlEditor
21184  * 
21185  */
21186
21187 /**
21188  * @class Roo.bootstrap.HtmlEditor
21189  * @extends Roo.bootstrap.TextArea
21190  * Bootstrap HtmlEditor class
21191
21192  * @constructor
21193  * Create a new HtmlEditor
21194  * @param {Object} config The config object
21195  */
21196
21197 Roo.bootstrap.HtmlEditor = function(config){
21198     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21199     if (!this.toolbars) {
21200         this.toolbars = [];
21201     }
21202     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21203     this.addEvents({
21204             /**
21205              * @event initialize
21206              * Fires when the editor is fully initialized (including the iframe)
21207              * @param {HtmlEditor} this
21208              */
21209             initialize: true,
21210             /**
21211              * @event activate
21212              * Fires when the editor is first receives the focus. Any insertion must wait
21213              * until after this event.
21214              * @param {HtmlEditor} this
21215              */
21216             activate: true,
21217              /**
21218              * @event beforesync
21219              * Fires before the textarea is updated with content from the editor iframe. Return false
21220              * to cancel the sync.
21221              * @param {HtmlEditor} this
21222              * @param {String} html
21223              */
21224             beforesync: true,
21225              /**
21226              * @event beforepush
21227              * Fires before the iframe editor is updated with content from the textarea. Return false
21228              * to cancel the push.
21229              * @param {HtmlEditor} this
21230              * @param {String} html
21231              */
21232             beforepush: true,
21233              /**
21234              * @event sync
21235              * Fires when the textarea is updated with content from the editor iframe.
21236              * @param {HtmlEditor} this
21237              * @param {String} html
21238              */
21239             sync: true,
21240              /**
21241              * @event push
21242              * Fires when the iframe editor is updated with content from the textarea.
21243              * @param {HtmlEditor} this
21244              * @param {String} html
21245              */
21246             push: true,
21247              /**
21248              * @event editmodechange
21249              * Fires when the editor switches edit modes
21250              * @param {HtmlEditor} this
21251              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21252              */
21253             editmodechange: true,
21254             /**
21255              * @event editorevent
21256              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21257              * @param {HtmlEditor} this
21258              */
21259             editorevent: true,
21260             /**
21261              * @event firstfocus
21262              * Fires when on first focus - needed by toolbars..
21263              * @param {HtmlEditor} this
21264              */
21265             firstfocus: true,
21266             /**
21267              * @event autosave
21268              * Auto save the htmlEditor value as a file into Events
21269              * @param {HtmlEditor} this
21270              */
21271             autosave: true,
21272             /**
21273              * @event savedpreview
21274              * preview the saved version of htmlEditor
21275              * @param {HtmlEditor} this
21276              */
21277             savedpreview: true
21278         });
21279 };
21280
21281
21282 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21283     
21284     
21285       /**
21286      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21287      */
21288     toolbars : false,
21289    
21290      /**
21291      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21292      *                        Roo.resizable.
21293      */
21294     resizable : false,
21295      /**
21296      * @cfg {Number} height (in pixels)
21297      */   
21298     height: 300,
21299    /**
21300      * @cfg {Number} width (in pixels)
21301      */   
21302     width: false,
21303     
21304     /**
21305      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21306      * 
21307      */
21308     stylesheets: false,
21309     
21310     // id of frame..
21311     frameId: false,
21312     
21313     // private properties
21314     validationEvent : false,
21315     deferHeight: true,
21316     initialized : false,
21317     activated : false,
21318     
21319     onFocus : Roo.emptyFn,
21320     iframePad:3,
21321     hideMode:'offsets',
21322     
21323     
21324     tbContainer : false,
21325     
21326     toolbarContainer :function() {
21327         return this.wrap.select('.x-html-editor-tb',true).first();
21328     },
21329
21330     /**
21331      * Protected method that will not generally be called directly. It
21332      * is called when the editor creates its toolbar. Override this method if you need to
21333      * add custom toolbar buttons.
21334      * @param {HtmlEditor} editor
21335      */
21336     createToolbar : function(){
21337         
21338         Roo.log("create toolbars");
21339         
21340         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21341         this.toolbars[0].render(this.toolbarContainer());
21342         
21343         return;
21344         
21345 //        if (!editor.toolbars || !editor.toolbars.length) {
21346 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21347 //        }
21348 //        
21349 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21350 //            editor.toolbars[i] = Roo.factory(
21351 //                    typeof(editor.toolbars[i]) == 'string' ?
21352 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21353 //                Roo.bootstrap.HtmlEditor);
21354 //            editor.toolbars[i].init(editor);
21355 //        }
21356     },
21357
21358      
21359     // private
21360     onRender : function(ct, position)
21361     {
21362        // Roo.log("Call onRender: " + this.xtype);
21363         var _t = this;
21364         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21365       
21366         this.wrap = this.inputEl().wrap({
21367             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21368         });
21369         
21370         this.editorcore.onRender(ct, position);
21371          
21372         if (this.resizable) {
21373             this.resizeEl = new Roo.Resizable(this.wrap, {
21374                 pinned : true,
21375                 wrap: true,
21376                 dynamic : true,
21377                 minHeight : this.height,
21378                 height: this.height,
21379                 handles : this.resizable,
21380                 width: this.width,
21381                 listeners : {
21382                     resize : function(r, w, h) {
21383                         _t.onResize(w,h); // -something
21384                     }
21385                 }
21386             });
21387             
21388         }
21389         this.createToolbar(this);
21390        
21391         
21392         if(!this.width && this.resizable){
21393             this.setSize(this.wrap.getSize());
21394         }
21395         if (this.resizeEl) {
21396             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21397             // should trigger onReize..
21398         }
21399         
21400     },
21401
21402     // private
21403     onResize : function(w, h)
21404     {
21405         Roo.log('resize: ' +w + ',' + h );
21406         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21407         var ew = false;
21408         var eh = false;
21409         
21410         if(this.inputEl() ){
21411             if(typeof w == 'number'){
21412                 var aw = w - this.wrap.getFrameWidth('lr');
21413                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21414                 ew = aw;
21415             }
21416             if(typeof h == 'number'){
21417                  var tbh = -11;  // fixme it needs to tool bar size!
21418                 for (var i =0; i < this.toolbars.length;i++) {
21419                     // fixme - ask toolbars for heights?
21420                     tbh += this.toolbars[i].el.getHeight();
21421                     //if (this.toolbars[i].footer) {
21422                     //    tbh += this.toolbars[i].footer.el.getHeight();
21423                     //}
21424                 }
21425               
21426                 
21427                 
21428                 
21429                 
21430                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21431                 ah -= 5; // knock a few pixes off for look..
21432                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21433                 var eh = ah;
21434             }
21435         }
21436         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21437         this.editorcore.onResize(ew,eh);
21438         
21439     },
21440
21441     /**
21442      * Toggles the editor between standard and source edit mode.
21443      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21444      */
21445     toggleSourceEdit : function(sourceEditMode)
21446     {
21447         this.editorcore.toggleSourceEdit(sourceEditMode);
21448         
21449         if(this.editorcore.sourceEditMode){
21450             Roo.log('editor - showing textarea');
21451             
21452 //            Roo.log('in');
21453 //            Roo.log(this.syncValue());
21454             this.syncValue();
21455             this.inputEl().removeClass(['hide', 'x-hidden']);
21456             this.inputEl().dom.removeAttribute('tabIndex');
21457             this.inputEl().focus();
21458         }else{
21459             Roo.log('editor - hiding textarea');
21460 //            Roo.log('out')
21461 //            Roo.log(this.pushValue()); 
21462             this.pushValue();
21463             
21464             this.inputEl().addClass(['hide', 'x-hidden']);
21465             this.inputEl().dom.setAttribute('tabIndex', -1);
21466             //this.deferFocus();
21467         }
21468          
21469         if(this.resizable){
21470             this.setSize(this.wrap.getSize());
21471         }
21472         
21473         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21474     },
21475  
21476     // private (for BoxComponent)
21477     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21478
21479     // private (for BoxComponent)
21480     getResizeEl : function(){
21481         return this.wrap;
21482     },
21483
21484     // private (for BoxComponent)
21485     getPositionEl : function(){
21486         return this.wrap;
21487     },
21488
21489     // private
21490     initEvents : function(){
21491         this.originalValue = this.getValue();
21492     },
21493
21494 //    /**
21495 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21496 //     * @method
21497 //     */
21498 //    markInvalid : Roo.emptyFn,
21499 //    /**
21500 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21501 //     * @method
21502 //     */
21503 //    clearInvalid : Roo.emptyFn,
21504
21505     setValue : function(v){
21506         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21507         this.editorcore.pushValue();
21508     },
21509
21510      
21511     // private
21512     deferFocus : function(){
21513         this.focus.defer(10, this);
21514     },
21515
21516     // doc'ed in Field
21517     focus : function(){
21518         this.editorcore.focus();
21519         
21520     },
21521       
21522
21523     // private
21524     onDestroy : function(){
21525         
21526         
21527         
21528         if(this.rendered){
21529             
21530             for (var i =0; i < this.toolbars.length;i++) {
21531                 // fixme - ask toolbars for heights?
21532                 this.toolbars[i].onDestroy();
21533             }
21534             
21535             this.wrap.dom.innerHTML = '';
21536             this.wrap.remove();
21537         }
21538     },
21539
21540     // private
21541     onFirstFocus : function(){
21542         //Roo.log("onFirstFocus");
21543         this.editorcore.onFirstFocus();
21544          for (var i =0; i < this.toolbars.length;i++) {
21545             this.toolbars[i].onFirstFocus();
21546         }
21547         
21548     },
21549     
21550     // private
21551     syncValue : function()
21552     {   
21553         this.editorcore.syncValue();
21554     },
21555     
21556     pushValue : function()
21557     {   
21558         this.editorcore.pushValue();
21559     }
21560      
21561     
21562     // hide stuff that is not compatible
21563     /**
21564      * @event blur
21565      * @hide
21566      */
21567     /**
21568      * @event change
21569      * @hide
21570      */
21571     /**
21572      * @event focus
21573      * @hide
21574      */
21575     /**
21576      * @event specialkey
21577      * @hide
21578      */
21579     /**
21580      * @cfg {String} fieldClass @hide
21581      */
21582     /**
21583      * @cfg {String} focusClass @hide
21584      */
21585     /**
21586      * @cfg {String} autoCreate @hide
21587      */
21588     /**
21589      * @cfg {String} inputType @hide
21590      */
21591     /**
21592      * @cfg {String} invalidClass @hide
21593      */
21594     /**
21595      * @cfg {String} invalidText @hide
21596      */
21597     /**
21598      * @cfg {String} msgFx @hide
21599      */
21600     /**
21601      * @cfg {String} validateOnBlur @hide
21602      */
21603 });
21604  
21605     
21606    
21607    
21608    
21609       
21610 Roo.namespace('Roo.bootstrap.htmleditor');
21611 /**
21612  * @class Roo.bootstrap.HtmlEditorToolbar1
21613  * Basic Toolbar
21614  * 
21615  * Usage:
21616  *
21617  new Roo.bootstrap.HtmlEditor({
21618     ....
21619     toolbars : [
21620         new Roo.bootstrap.HtmlEditorToolbar1({
21621             disable : { fonts: 1 , format: 1, ..., ... , ...],
21622             btns : [ .... ]
21623         })
21624     }
21625      
21626  * 
21627  * @cfg {Object} disable List of elements to disable..
21628  * @cfg {Array} btns List of additional buttons.
21629  * 
21630  * 
21631  * NEEDS Extra CSS? 
21632  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21633  */
21634  
21635 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21636 {
21637     
21638     Roo.apply(this, config);
21639     
21640     // default disabled, based on 'good practice'..
21641     this.disable = this.disable || {};
21642     Roo.applyIf(this.disable, {
21643         fontSize : true,
21644         colors : true,
21645         specialElements : true
21646     });
21647     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21648     
21649     this.editor = config.editor;
21650     this.editorcore = config.editor.editorcore;
21651     
21652     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21653     
21654     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21655     // dont call parent... till later.
21656 }
21657 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21658      
21659     bar : true,
21660     
21661     editor : false,
21662     editorcore : false,
21663     
21664     
21665     formats : [
21666         "p" ,  
21667         "h1","h2","h3","h4","h5","h6", 
21668         "pre", "code", 
21669         "abbr", "acronym", "address", "cite", "samp", "var",
21670         'div','span'
21671     ],
21672     
21673     onRender : function(ct, position)
21674     {
21675        // Roo.log("Call onRender: " + this.xtype);
21676         
21677        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21678        Roo.log(this.el);
21679        this.el.dom.style.marginBottom = '0';
21680        var _this = this;
21681        var editorcore = this.editorcore;
21682        var editor= this.editor;
21683        
21684        var children = [];
21685        var btn = function(id,cmd , toggle, handler){
21686        
21687             var  event = toggle ? 'toggle' : 'click';
21688        
21689             var a = {
21690                 size : 'sm',
21691                 xtype: 'Button',
21692                 xns: Roo.bootstrap,
21693                 glyphicon : id,
21694                 cmd : id || cmd,
21695                 enableToggle:toggle !== false,
21696                 //html : 'submit'
21697                 pressed : toggle ? false : null,
21698                 listeners : {}
21699             };
21700             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21701                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21702             };
21703             children.push(a);
21704             return a;
21705        }
21706         
21707         var style = {
21708                 xtype: 'Button',
21709                 size : 'sm',
21710                 xns: Roo.bootstrap,
21711                 glyphicon : 'font',
21712                 //html : 'submit'
21713                 menu : {
21714                     xtype: 'Menu',
21715                     xns: Roo.bootstrap,
21716                     items:  []
21717                 }
21718         };
21719         Roo.each(this.formats, function(f) {
21720             style.menu.items.push({
21721                 xtype :'MenuItem',
21722                 xns: Roo.bootstrap,
21723                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21724                 tagname : f,
21725                 listeners : {
21726                     click : function()
21727                     {
21728                         editorcore.insertTag(this.tagname);
21729                         editor.focus();
21730                     }
21731                 }
21732                 
21733             });
21734         });
21735          children.push(style);   
21736             
21737             
21738         btn('bold',false,true);
21739         btn('italic',false,true);
21740         btn('align-left', 'justifyleft',true);
21741         btn('align-center', 'justifycenter',true);
21742         btn('align-right' , 'justifyright',true);
21743         btn('link', false, false, function(btn) {
21744             //Roo.log("create link?");
21745             var url = prompt(this.createLinkText, this.defaultLinkValue);
21746             if(url && url != 'http:/'+'/'){
21747                 this.editorcore.relayCmd('createlink', url);
21748             }
21749         }),
21750         btn('list','insertunorderedlist',true);
21751         btn('pencil', false,true, function(btn){
21752                 Roo.log(this);
21753                 
21754                 this.toggleSourceEdit(btn.pressed);
21755         });
21756         /*
21757         var cog = {
21758                 xtype: 'Button',
21759                 size : 'sm',
21760                 xns: Roo.bootstrap,
21761                 glyphicon : 'cog',
21762                 //html : 'submit'
21763                 menu : {
21764                     xtype: 'Menu',
21765                     xns: Roo.bootstrap,
21766                     items:  []
21767                 }
21768         };
21769         
21770         cog.menu.items.push({
21771             xtype :'MenuItem',
21772             xns: Roo.bootstrap,
21773             html : Clean styles,
21774             tagname : f,
21775             listeners : {
21776                 click : function()
21777                 {
21778                     editorcore.insertTag(this.tagname);
21779                     editor.focus();
21780                 }
21781             }
21782             
21783         });
21784        */
21785         
21786          
21787        this.xtype = 'NavSimplebar';
21788         
21789         for(var i=0;i< children.length;i++) {
21790             
21791             this.buttons.add(this.addxtypeChild(children[i]));
21792             
21793         }
21794         
21795         editor.on('editorevent', this.updateToolbar, this);
21796     },
21797     onBtnClick : function(id)
21798     {
21799        this.editorcore.relayCmd(id);
21800        this.editorcore.focus();
21801     },
21802     
21803     /**
21804      * Protected method that will not generally be called directly. It triggers
21805      * a toolbar update by reading the markup state of the current selection in the editor.
21806      */
21807     updateToolbar: function(){
21808
21809         if(!this.editorcore.activated){
21810             this.editor.onFirstFocus(); // is this neeed?
21811             return;
21812         }
21813
21814         var btns = this.buttons; 
21815         var doc = this.editorcore.doc;
21816         btns.get('bold').setActive(doc.queryCommandState('bold'));
21817         btns.get('italic').setActive(doc.queryCommandState('italic'));
21818         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21819         
21820         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21821         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21822         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21823         
21824         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21825         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21826          /*
21827         
21828         var ans = this.editorcore.getAllAncestors();
21829         if (this.formatCombo) {
21830             
21831             
21832             var store = this.formatCombo.store;
21833             this.formatCombo.setValue("");
21834             for (var i =0; i < ans.length;i++) {
21835                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21836                     // select it..
21837                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21838                     break;
21839                 }
21840             }
21841         }
21842         
21843         
21844         
21845         // hides menus... - so this cant be on a menu...
21846         Roo.bootstrap.MenuMgr.hideAll();
21847         */
21848         Roo.bootstrap.MenuMgr.hideAll();
21849         //this.editorsyncValue();
21850     },
21851     onFirstFocus: function() {
21852         this.buttons.each(function(item){
21853            item.enable();
21854         });
21855     },
21856     toggleSourceEdit : function(sourceEditMode){
21857         
21858           
21859         if(sourceEditMode){
21860             Roo.log("disabling buttons");
21861            this.buttons.each( function(item){
21862                 if(item.cmd != 'pencil'){
21863                     item.disable();
21864                 }
21865             });
21866           
21867         }else{
21868             Roo.log("enabling buttons");
21869             if(this.editorcore.initialized){
21870                 this.buttons.each( function(item){
21871                     item.enable();
21872                 });
21873             }
21874             
21875         }
21876         Roo.log("calling toggole on editor");
21877         // tell the editor that it's been pressed..
21878         this.editor.toggleSourceEdit(sourceEditMode);
21879        
21880     }
21881 });
21882
21883
21884
21885
21886
21887 /**
21888  * @class Roo.bootstrap.Table.AbstractSelectionModel
21889  * @extends Roo.util.Observable
21890  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21891  * implemented by descendant classes.  This class should not be directly instantiated.
21892  * @constructor
21893  */
21894 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21895     this.locked = false;
21896     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21897 };
21898
21899
21900 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21901     /** @ignore Called by the grid automatically. Do not call directly. */
21902     init : function(grid){
21903         this.grid = grid;
21904         this.initEvents();
21905     },
21906
21907     /**
21908      * Locks the selections.
21909      */
21910     lock : function(){
21911         this.locked = true;
21912     },
21913
21914     /**
21915      * Unlocks the selections.
21916      */
21917     unlock : function(){
21918         this.locked = false;
21919     },
21920
21921     /**
21922      * Returns true if the selections are locked.
21923      * @return {Boolean}
21924      */
21925     isLocked : function(){
21926         return this.locked;
21927     }
21928 });
21929 /**
21930  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21931  * @class Roo.bootstrap.Table.RowSelectionModel
21932  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21933  * It supports multiple selections and keyboard selection/navigation. 
21934  * @constructor
21935  * @param {Object} config
21936  */
21937
21938 Roo.bootstrap.Table.RowSelectionModel = function(config){
21939     Roo.apply(this, config);
21940     this.selections = new Roo.util.MixedCollection(false, function(o){
21941         return o.id;
21942     });
21943
21944     this.last = false;
21945     this.lastActive = false;
21946
21947     this.addEvents({
21948         /**
21949              * @event selectionchange
21950              * Fires when the selection changes
21951              * @param {SelectionModel} this
21952              */
21953             "selectionchange" : true,
21954         /**
21955              * @event afterselectionchange
21956              * Fires after the selection changes (eg. by key press or clicking)
21957              * @param {SelectionModel} this
21958              */
21959             "afterselectionchange" : true,
21960         /**
21961              * @event beforerowselect
21962              * Fires when a row is selected being selected, return false to cancel.
21963              * @param {SelectionModel} this
21964              * @param {Number} rowIndex The selected index
21965              * @param {Boolean} keepExisting False if other selections will be cleared
21966              */
21967             "beforerowselect" : true,
21968         /**
21969              * @event rowselect
21970              * Fires when a row is selected.
21971              * @param {SelectionModel} this
21972              * @param {Number} rowIndex The selected index
21973              * @param {Roo.data.Record} r The record
21974              */
21975             "rowselect" : true,
21976         /**
21977              * @event rowdeselect
21978              * Fires when a row is deselected.
21979              * @param {SelectionModel} this
21980              * @param {Number} rowIndex The selected index
21981              */
21982         "rowdeselect" : true
21983     });
21984     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21985     this.locked = false;
21986 };
21987
21988 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21989     /**
21990      * @cfg {Boolean} singleSelect
21991      * True to allow selection of only one row at a time (defaults to false)
21992      */
21993     singleSelect : false,
21994
21995     // private
21996     initEvents : function(){
21997
21998         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21999             this.grid.on("mousedown", this.handleMouseDown, this);
22000         }else{ // allow click to work like normal
22001             this.grid.on("rowclick", this.handleDragableRowClick, this);
22002         }
22003
22004         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22005             "up" : function(e){
22006                 if(!e.shiftKey){
22007                     this.selectPrevious(e.shiftKey);
22008                 }else if(this.last !== false && this.lastActive !== false){
22009                     var last = this.last;
22010                     this.selectRange(this.last,  this.lastActive-1);
22011                     this.grid.getView().focusRow(this.lastActive);
22012                     if(last !== false){
22013                         this.last = last;
22014                     }
22015                 }else{
22016                     this.selectFirstRow();
22017                 }
22018                 this.fireEvent("afterselectionchange", this);
22019             },
22020             "down" : function(e){
22021                 if(!e.shiftKey){
22022                     this.selectNext(e.shiftKey);
22023                 }else if(this.last !== false && this.lastActive !== false){
22024                     var last = this.last;
22025                     this.selectRange(this.last,  this.lastActive+1);
22026                     this.grid.getView().focusRow(this.lastActive);
22027                     if(last !== false){
22028                         this.last = last;
22029                     }
22030                 }else{
22031                     this.selectFirstRow();
22032                 }
22033                 this.fireEvent("afterselectionchange", this);
22034             },
22035             scope: this
22036         });
22037
22038         var view = this.grid.view;
22039         view.on("refresh", this.onRefresh, this);
22040         view.on("rowupdated", this.onRowUpdated, this);
22041         view.on("rowremoved", this.onRemove, this);
22042     },
22043
22044     // private
22045     onRefresh : function(){
22046         var ds = this.grid.dataSource, i, v = this.grid.view;
22047         var s = this.selections;
22048         s.each(function(r){
22049             if((i = ds.indexOfId(r.id)) != -1){
22050                 v.onRowSelect(i);
22051             }else{
22052                 s.remove(r);
22053             }
22054         });
22055     },
22056
22057     // private
22058     onRemove : function(v, index, r){
22059         this.selections.remove(r);
22060     },
22061
22062     // private
22063     onRowUpdated : function(v, index, r){
22064         if(this.isSelected(r)){
22065             v.onRowSelect(index);
22066         }
22067     },
22068
22069     /**
22070      * Select records.
22071      * @param {Array} records The records to select
22072      * @param {Boolean} keepExisting (optional) True to keep existing selections
22073      */
22074     selectRecords : function(records, keepExisting){
22075         if(!keepExisting){
22076             this.clearSelections();
22077         }
22078         var ds = this.grid.dataSource;
22079         for(var i = 0, len = records.length; i < len; i++){
22080             this.selectRow(ds.indexOf(records[i]), true);
22081         }
22082     },
22083
22084     /**
22085      * Gets the number of selected rows.
22086      * @return {Number}
22087      */
22088     getCount : function(){
22089         return this.selections.length;
22090     },
22091
22092     /**
22093      * Selects the first row in the grid.
22094      */
22095     selectFirstRow : function(){
22096         this.selectRow(0);
22097     },
22098
22099     /**
22100      * Select the last row.
22101      * @param {Boolean} keepExisting (optional) True to keep existing selections
22102      */
22103     selectLastRow : function(keepExisting){
22104         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22105     },
22106
22107     /**
22108      * Selects the row immediately following the last selected row.
22109      * @param {Boolean} keepExisting (optional) True to keep existing selections
22110      */
22111     selectNext : function(keepExisting){
22112         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22113             this.selectRow(this.last+1, keepExisting);
22114             this.grid.getView().focusRow(this.last);
22115         }
22116     },
22117
22118     /**
22119      * Selects the row that precedes the last selected row.
22120      * @param {Boolean} keepExisting (optional) True to keep existing selections
22121      */
22122     selectPrevious : function(keepExisting){
22123         if(this.last){
22124             this.selectRow(this.last-1, keepExisting);
22125             this.grid.getView().focusRow(this.last);
22126         }
22127     },
22128
22129     /**
22130      * Returns the selected records
22131      * @return {Array} Array of selected records
22132      */
22133     getSelections : function(){
22134         return [].concat(this.selections.items);
22135     },
22136
22137     /**
22138      * Returns the first selected record.
22139      * @return {Record}
22140      */
22141     getSelected : function(){
22142         return this.selections.itemAt(0);
22143     },
22144
22145
22146     /**
22147      * Clears all selections.
22148      */
22149     clearSelections : function(fast){
22150         if(this.locked) {
22151             return;
22152         }
22153         if(fast !== true){
22154             var ds = this.grid.dataSource;
22155             var s = this.selections;
22156             s.each(function(r){
22157                 this.deselectRow(ds.indexOfId(r.id));
22158             }, this);
22159             s.clear();
22160         }else{
22161             this.selections.clear();
22162         }
22163         this.last = false;
22164     },
22165
22166
22167     /**
22168      * Selects all rows.
22169      */
22170     selectAll : function(){
22171         if(this.locked) {
22172             return;
22173         }
22174         this.selections.clear();
22175         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22176             this.selectRow(i, true);
22177         }
22178     },
22179
22180     /**
22181      * Returns True if there is a selection.
22182      * @return {Boolean}
22183      */
22184     hasSelection : function(){
22185         return this.selections.length > 0;
22186     },
22187
22188     /**
22189      * Returns True if the specified row is selected.
22190      * @param {Number/Record} record The record or index of the record to check
22191      * @return {Boolean}
22192      */
22193     isSelected : function(index){
22194         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22195         return (r && this.selections.key(r.id) ? true : false);
22196     },
22197
22198     /**
22199      * Returns True if the specified record id is selected.
22200      * @param {String} id The id of record to check
22201      * @return {Boolean}
22202      */
22203     isIdSelected : function(id){
22204         return (this.selections.key(id) ? true : false);
22205     },
22206
22207     // private
22208     handleMouseDown : function(e, t){
22209         var view = this.grid.getView(), rowIndex;
22210         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22211             return;
22212         };
22213         if(e.shiftKey && this.last !== false){
22214             var last = this.last;
22215             this.selectRange(last, rowIndex, e.ctrlKey);
22216             this.last = last; // reset the last
22217             view.focusRow(rowIndex);
22218         }else{
22219             var isSelected = this.isSelected(rowIndex);
22220             if(e.button !== 0 && isSelected){
22221                 view.focusRow(rowIndex);
22222             }else if(e.ctrlKey && isSelected){
22223                 this.deselectRow(rowIndex);
22224             }else if(!isSelected){
22225                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22226                 view.focusRow(rowIndex);
22227             }
22228         }
22229         this.fireEvent("afterselectionchange", this);
22230     },
22231     // private
22232     handleDragableRowClick :  function(grid, rowIndex, e) 
22233     {
22234         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22235             this.selectRow(rowIndex, false);
22236             grid.view.focusRow(rowIndex);
22237              this.fireEvent("afterselectionchange", this);
22238         }
22239     },
22240     
22241     /**
22242      * Selects multiple rows.
22243      * @param {Array} rows Array of the indexes of the row to select
22244      * @param {Boolean} keepExisting (optional) True to keep existing selections
22245      */
22246     selectRows : function(rows, keepExisting){
22247         if(!keepExisting){
22248             this.clearSelections();
22249         }
22250         for(var i = 0, len = rows.length; i < len; i++){
22251             this.selectRow(rows[i], true);
22252         }
22253     },
22254
22255     /**
22256      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22257      * @param {Number} startRow The index of the first row in the range
22258      * @param {Number} endRow The index of the last row in the range
22259      * @param {Boolean} keepExisting (optional) True to retain existing selections
22260      */
22261     selectRange : function(startRow, endRow, keepExisting){
22262         if(this.locked) {
22263             return;
22264         }
22265         if(!keepExisting){
22266             this.clearSelections();
22267         }
22268         if(startRow <= endRow){
22269             for(var i = startRow; i <= endRow; i++){
22270                 this.selectRow(i, true);
22271             }
22272         }else{
22273             for(var i = startRow; i >= endRow; i--){
22274                 this.selectRow(i, true);
22275             }
22276         }
22277     },
22278
22279     /**
22280      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22281      * @param {Number} startRow The index of the first row in the range
22282      * @param {Number} endRow The index of the last row in the range
22283      */
22284     deselectRange : function(startRow, endRow, preventViewNotify){
22285         if(this.locked) {
22286             return;
22287         }
22288         for(var i = startRow; i <= endRow; i++){
22289             this.deselectRow(i, preventViewNotify);
22290         }
22291     },
22292
22293     /**
22294      * Selects a row.
22295      * @param {Number} row The index of the row to select
22296      * @param {Boolean} keepExisting (optional) True to keep existing selections
22297      */
22298     selectRow : function(index, keepExisting, preventViewNotify){
22299         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22300             return;
22301         }
22302         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22303             if(!keepExisting || this.singleSelect){
22304                 this.clearSelections();
22305             }
22306             var r = this.grid.dataSource.getAt(index);
22307             this.selections.add(r);
22308             this.last = this.lastActive = index;
22309             if(!preventViewNotify){
22310                 this.grid.getView().onRowSelect(index);
22311             }
22312             this.fireEvent("rowselect", this, index, r);
22313             this.fireEvent("selectionchange", this);
22314         }
22315     },
22316
22317     /**
22318      * Deselects a row.
22319      * @param {Number} row The index of the row to deselect
22320      */
22321     deselectRow : function(index, preventViewNotify){
22322         if(this.locked) {
22323             return;
22324         }
22325         if(this.last == index){
22326             this.last = false;
22327         }
22328         if(this.lastActive == index){
22329             this.lastActive = false;
22330         }
22331         var r = this.grid.dataSource.getAt(index);
22332         this.selections.remove(r);
22333         if(!preventViewNotify){
22334             this.grid.getView().onRowDeselect(index);
22335         }
22336         this.fireEvent("rowdeselect", this, index);
22337         this.fireEvent("selectionchange", this);
22338     },
22339
22340     // private
22341     restoreLast : function(){
22342         if(this._last){
22343             this.last = this._last;
22344         }
22345     },
22346
22347     // private
22348     acceptsNav : function(row, col, cm){
22349         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22350     },
22351
22352     // private
22353     onEditorKey : function(field, e){
22354         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22355         if(k == e.TAB){
22356             e.stopEvent();
22357             ed.completeEdit();
22358             if(e.shiftKey){
22359                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22360             }else{
22361                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22362             }
22363         }else if(k == e.ENTER && !e.ctrlKey){
22364             e.stopEvent();
22365             ed.completeEdit();
22366             if(e.shiftKey){
22367                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22368             }else{
22369                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22370             }
22371         }else if(k == e.ESC){
22372             ed.cancelEdit();
22373         }
22374         if(newCell){
22375             g.startEditing(newCell[0], newCell[1]);
22376         }
22377     }
22378 });/*
22379  * Based on:
22380  * Ext JS Library 1.1.1
22381  * Copyright(c) 2006-2007, Ext JS, LLC.
22382  *
22383  * Originally Released Under LGPL - original licence link has changed is not relivant.
22384  *
22385  * Fork - LGPL
22386  * <script type="text/javascript">
22387  */
22388  
22389 /**
22390  * @class Roo.bootstrap.PagingToolbar
22391  * @extends Roo.bootstrap.NavSimplebar
22392  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22393  * @constructor
22394  * Create a new PagingToolbar
22395  * @param {Object} config The config object
22396  * @param {Roo.data.Store} store
22397  */
22398 Roo.bootstrap.PagingToolbar = function(config)
22399 {
22400     // old args format still supported... - xtype is prefered..
22401         // created from xtype...
22402     
22403     this.ds = config.dataSource;
22404     
22405     if (config.store && !this.ds) {
22406         this.store= Roo.factory(config.store, Roo.data);
22407         this.ds = this.store;
22408         this.ds.xmodule = this.xmodule || false;
22409     }
22410     
22411     this.toolbarItems = [];
22412     if (config.items) {
22413         this.toolbarItems = config.items;
22414     }
22415     
22416     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22417     
22418     this.cursor = 0;
22419     
22420     if (this.ds) { 
22421         this.bind(this.ds);
22422     }
22423     
22424     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22425     
22426 };
22427
22428 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22429     /**
22430      * @cfg {Roo.data.Store} dataSource
22431      * The underlying data store providing the paged data
22432      */
22433     /**
22434      * @cfg {String/HTMLElement/Element} container
22435      * container The id or element that will contain the toolbar
22436      */
22437     /**
22438      * @cfg {Boolean} displayInfo
22439      * True to display the displayMsg (defaults to false)
22440      */
22441     /**
22442      * @cfg {Number} pageSize
22443      * The number of records to display per page (defaults to 20)
22444      */
22445     pageSize: 20,
22446     /**
22447      * @cfg {String} displayMsg
22448      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22449      */
22450     displayMsg : 'Displaying {0} - {1} of {2}',
22451     /**
22452      * @cfg {String} emptyMsg
22453      * The message to display when no records are found (defaults to "No data to display")
22454      */
22455     emptyMsg : 'No data to display',
22456     /**
22457      * Customizable piece of the default paging text (defaults to "Page")
22458      * @type String
22459      */
22460     beforePageText : "Page",
22461     /**
22462      * Customizable piece of the default paging text (defaults to "of %0")
22463      * @type String
22464      */
22465     afterPageText : "of {0}",
22466     /**
22467      * Customizable piece of the default paging text (defaults to "First Page")
22468      * @type String
22469      */
22470     firstText : "First Page",
22471     /**
22472      * Customizable piece of the default paging text (defaults to "Previous Page")
22473      * @type String
22474      */
22475     prevText : "Previous Page",
22476     /**
22477      * Customizable piece of the default paging text (defaults to "Next Page")
22478      * @type String
22479      */
22480     nextText : "Next Page",
22481     /**
22482      * Customizable piece of the default paging text (defaults to "Last Page")
22483      * @type String
22484      */
22485     lastText : "Last Page",
22486     /**
22487      * Customizable piece of the default paging text (defaults to "Refresh")
22488      * @type String
22489      */
22490     refreshText : "Refresh",
22491
22492     buttons : false,
22493     // private
22494     onRender : function(ct, position) 
22495     {
22496         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22497         this.navgroup.parentId = this.id;
22498         this.navgroup.onRender(this.el, null);
22499         // add the buttons to the navgroup
22500         
22501         if(this.displayInfo){
22502             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22503             this.displayEl = this.el.select('.x-paging-info', true).first();
22504 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22505 //            this.displayEl = navel.el.select('span',true).first();
22506         }
22507         
22508         var _this = this;
22509         
22510         if(this.buttons){
22511             Roo.each(_this.buttons, function(e){ // this might need to use render????
22512                Roo.factory(e).onRender(_this.el, null);
22513             });
22514         }
22515             
22516         Roo.each(_this.toolbarItems, function(e) {
22517             _this.navgroup.addItem(e);
22518         });
22519         
22520         
22521         this.first = this.navgroup.addItem({
22522             tooltip: this.firstText,
22523             cls: "prev",
22524             icon : 'fa fa-backward',
22525             disabled: true,
22526             preventDefault: true,
22527             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22528         });
22529         
22530         this.prev =  this.navgroup.addItem({
22531             tooltip: this.prevText,
22532             cls: "prev",
22533             icon : 'fa fa-step-backward',
22534             disabled: true,
22535             preventDefault: true,
22536             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22537         });
22538     //this.addSeparator();
22539         
22540         
22541         var field = this.navgroup.addItem( {
22542             tagtype : 'span',
22543             cls : 'x-paging-position',
22544             
22545             html : this.beforePageText  +
22546                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22547                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22548          } ); //?? escaped?
22549         
22550         this.field = field.el.select('input', true).first();
22551         this.field.on("keydown", this.onPagingKeydown, this);
22552         this.field.on("focus", function(){this.dom.select();});
22553     
22554     
22555         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22556         //this.field.setHeight(18);
22557         //this.addSeparator();
22558         this.next = this.navgroup.addItem({
22559             tooltip: this.nextText,
22560             cls: "next",
22561             html : ' <i class="fa fa-step-forward">',
22562             disabled: true,
22563             preventDefault: true,
22564             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22565         });
22566         this.last = this.navgroup.addItem({
22567             tooltip: this.lastText,
22568             icon : 'fa fa-forward',
22569             cls: "next",
22570             disabled: true,
22571             preventDefault: true,
22572             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22573         });
22574     //this.addSeparator();
22575         this.loading = this.navgroup.addItem({
22576             tooltip: this.refreshText,
22577             icon: 'fa fa-refresh',
22578             preventDefault: true,
22579             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22580         });
22581         
22582     },
22583
22584     // private
22585     updateInfo : function(){
22586         if(this.displayEl){
22587             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22588             var msg = count == 0 ?
22589                 this.emptyMsg :
22590                 String.format(
22591                     this.displayMsg,
22592                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22593                 );
22594             this.displayEl.update(msg);
22595         }
22596     },
22597
22598     // private
22599     onLoad : function(ds, r, o){
22600        this.cursor = o.params ? o.params.start : 0;
22601        var d = this.getPageData(),
22602             ap = d.activePage,
22603             ps = d.pages;
22604         
22605        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22606        this.field.dom.value = ap;
22607        this.first.setDisabled(ap == 1);
22608        this.prev.setDisabled(ap == 1);
22609        this.next.setDisabled(ap == ps);
22610        this.last.setDisabled(ap == ps);
22611        this.loading.enable();
22612        this.updateInfo();
22613     },
22614
22615     // private
22616     getPageData : function(){
22617         var total = this.ds.getTotalCount();
22618         return {
22619             total : total,
22620             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22621             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22622         };
22623     },
22624
22625     // private
22626     onLoadError : function(){
22627         this.loading.enable();
22628     },
22629
22630     // private
22631     onPagingKeydown : function(e){
22632         var k = e.getKey();
22633         var d = this.getPageData();
22634         if(k == e.RETURN){
22635             var v = this.field.dom.value, pageNum;
22636             if(!v || isNaN(pageNum = parseInt(v, 10))){
22637                 this.field.dom.value = d.activePage;
22638                 return;
22639             }
22640             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22641             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22642             e.stopEvent();
22643         }
22644         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))
22645         {
22646           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22647           this.field.dom.value = pageNum;
22648           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22649           e.stopEvent();
22650         }
22651         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22652         {
22653           var v = this.field.dom.value, pageNum; 
22654           var increment = (e.shiftKey) ? 10 : 1;
22655           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22656                 increment *= -1;
22657           }
22658           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22659             this.field.dom.value = d.activePage;
22660             return;
22661           }
22662           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22663           {
22664             this.field.dom.value = parseInt(v, 10) + increment;
22665             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22666             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22667           }
22668           e.stopEvent();
22669         }
22670     },
22671
22672     // private
22673     beforeLoad : function(){
22674         if(this.loading){
22675             this.loading.disable();
22676         }
22677     },
22678
22679     // private
22680     onClick : function(which){
22681         
22682         var ds = this.ds;
22683         if (!ds) {
22684             return;
22685         }
22686         
22687         switch(which){
22688             case "first":
22689                 ds.load({params:{start: 0, limit: this.pageSize}});
22690             break;
22691             case "prev":
22692                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22693             break;
22694             case "next":
22695                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22696             break;
22697             case "last":
22698                 var total = ds.getTotalCount();
22699                 var extra = total % this.pageSize;
22700                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22701                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22702             break;
22703             case "refresh":
22704                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22705             break;
22706         }
22707     },
22708
22709     /**
22710      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22711      * @param {Roo.data.Store} store The data store to unbind
22712      */
22713     unbind : function(ds){
22714         ds.un("beforeload", this.beforeLoad, this);
22715         ds.un("load", this.onLoad, this);
22716         ds.un("loadexception", this.onLoadError, this);
22717         ds.un("remove", this.updateInfo, this);
22718         ds.un("add", this.updateInfo, this);
22719         this.ds = undefined;
22720     },
22721
22722     /**
22723      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22724      * @param {Roo.data.Store} store The data store to bind
22725      */
22726     bind : function(ds){
22727         ds.on("beforeload", this.beforeLoad, this);
22728         ds.on("load", this.onLoad, this);
22729         ds.on("loadexception", this.onLoadError, this);
22730         ds.on("remove", this.updateInfo, this);
22731         ds.on("add", this.updateInfo, this);
22732         this.ds = ds;
22733     }
22734 });/*
22735  * - LGPL
22736  *
22737  * element
22738  * 
22739  */
22740
22741 /**
22742  * @class Roo.bootstrap.MessageBar
22743  * @extends Roo.bootstrap.Component
22744  * Bootstrap MessageBar class
22745  * @cfg {String} html contents of the MessageBar
22746  * @cfg {String} weight (info | success | warning | danger) default info
22747  * @cfg {String} beforeClass insert the bar before the given class
22748  * @cfg {Boolean} closable (true | false) default false
22749  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22750  * 
22751  * @constructor
22752  * Create a new Element
22753  * @param {Object} config The config object
22754  */
22755
22756 Roo.bootstrap.MessageBar = function(config){
22757     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22758 };
22759
22760 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22761     
22762     html: '',
22763     weight: 'info',
22764     closable: false,
22765     fixed: false,
22766     beforeClass: 'bootstrap-sticky-wrap',
22767     
22768     getAutoCreate : function(){
22769         
22770         var cfg = {
22771             tag: 'div',
22772             cls: 'alert alert-dismissable alert-' + this.weight,
22773             cn: [
22774                 {
22775                     tag: 'span',
22776                     cls: 'message',
22777                     html: this.html || ''
22778                 }
22779             ]
22780         };
22781         
22782         if(this.fixed){
22783             cfg.cls += ' alert-messages-fixed';
22784         }
22785         
22786         if(this.closable){
22787             cfg.cn.push({
22788                 tag: 'button',
22789                 cls: 'close',
22790                 html: 'x'
22791             });
22792         }
22793         
22794         return cfg;
22795     },
22796     
22797     onRender : function(ct, position)
22798     {
22799         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22800         
22801         if(!this.el){
22802             var cfg = Roo.apply({},  this.getAutoCreate());
22803             cfg.id = Roo.id();
22804             
22805             if (this.cls) {
22806                 cfg.cls += ' ' + this.cls;
22807             }
22808             if (this.style) {
22809                 cfg.style = this.style;
22810             }
22811             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22812             
22813             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22814         }
22815         
22816         this.el.select('>button.close').on('click', this.hide, this);
22817         
22818     },
22819     
22820     show : function()
22821     {
22822         if (!this.rendered) {
22823             this.render();
22824         }
22825         
22826         this.el.show();
22827         
22828         this.fireEvent('show', this);
22829         
22830     },
22831     
22832     hide : function()
22833     {
22834         if (!this.rendered) {
22835             this.render();
22836         }
22837         
22838         this.el.hide();
22839         
22840         this.fireEvent('hide', this);
22841     },
22842     
22843     update : function()
22844     {
22845 //        var e = this.el.dom.firstChild;
22846 //        
22847 //        if(this.closable){
22848 //            e = e.nextSibling;
22849 //        }
22850 //        
22851 //        e.data = this.html || '';
22852
22853         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22854     }
22855    
22856 });
22857
22858  
22859
22860      /*
22861  * - LGPL
22862  *
22863  * Graph
22864  * 
22865  */
22866
22867
22868 /**
22869  * @class Roo.bootstrap.Graph
22870  * @extends Roo.bootstrap.Component
22871  * Bootstrap Graph class
22872 > Prameters
22873  -sm {number} sm 4
22874  -md {number} md 5
22875  @cfg {String} graphtype  bar | vbar | pie
22876  @cfg {number} g_x coodinator | centre x (pie)
22877  @cfg {number} g_y coodinator | centre y (pie)
22878  @cfg {number} g_r radius (pie)
22879  @cfg {number} g_height height of the chart (respected by all elements in the set)
22880  @cfg {number} g_width width of the chart (respected by all elements in the set)
22881  @cfg {Object} title The title of the chart
22882     
22883  -{Array}  values
22884  -opts (object) options for the chart 
22885      o {
22886      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22887      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22888      o vgutter (number)
22889      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.
22890      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22891      o to
22892      o stretch (boolean)
22893      o }
22894  -opts (object) options for the pie
22895      o{
22896      o cut
22897      o startAngle (number)
22898      o endAngle (number)
22899      } 
22900  *
22901  * @constructor
22902  * Create a new Input
22903  * @param {Object} config The config object
22904  */
22905
22906 Roo.bootstrap.Graph = function(config){
22907     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22908     
22909     this.addEvents({
22910         // img events
22911         /**
22912          * @event click
22913          * The img click event for the img.
22914          * @param {Roo.EventObject} e
22915          */
22916         "click" : true
22917     });
22918 };
22919
22920 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22921     
22922     sm: 4,
22923     md: 5,
22924     graphtype: 'bar',
22925     g_height: 250,
22926     g_width: 400,
22927     g_x: 50,
22928     g_y: 50,
22929     g_r: 30,
22930     opts:{
22931         //g_colors: this.colors,
22932         g_type: 'soft',
22933         g_gutter: '20%'
22934
22935     },
22936     title : false,
22937
22938     getAutoCreate : function(){
22939         
22940         var cfg = {
22941             tag: 'div',
22942             html : null
22943         };
22944         
22945         
22946         return  cfg;
22947     },
22948
22949     onRender : function(ct,position){
22950         
22951         
22952         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22953         
22954         if (typeof(Raphael) == 'undefined') {
22955             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
22956             return;
22957         }
22958         
22959         this.raphael = Raphael(this.el.dom);
22960         
22961                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22962                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22963                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22964                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22965                 /*
22966                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22967                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22968                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22969                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22970                 
22971                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22972                 r.barchart(330, 10, 300, 220, data1);
22973                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22974                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22975                 */
22976                 
22977                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22978                 // r.barchart(30, 30, 560, 250,  xdata, {
22979                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22980                 //     axis : "0 0 1 1",
22981                 //     axisxlabels :  xdata
22982                 //     //yvalues : cols,
22983                    
22984                 // });
22985 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22986 //        
22987 //        this.load(null,xdata,{
22988 //                axis : "0 0 1 1",
22989 //                axisxlabels :  xdata
22990 //                });
22991
22992     },
22993
22994     load : function(graphtype,xdata,opts)
22995     {
22996         this.raphael.clear();
22997         if(!graphtype) {
22998             graphtype = this.graphtype;
22999         }
23000         if(!opts){
23001             opts = this.opts;
23002         }
23003         var r = this.raphael,
23004             fin = function () {
23005                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23006             },
23007             fout = function () {
23008                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23009             },
23010             pfin = function() {
23011                 this.sector.stop();
23012                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23013
23014                 if (this.label) {
23015                     this.label[0].stop();
23016                     this.label[0].attr({ r: 7.5 });
23017                     this.label[1].attr({ "font-weight": 800 });
23018                 }
23019             },
23020             pfout = function() {
23021                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23022
23023                 if (this.label) {
23024                     this.label[0].animate({ r: 5 }, 500, "bounce");
23025                     this.label[1].attr({ "font-weight": 400 });
23026                 }
23027             };
23028
23029         switch(graphtype){
23030             case 'bar':
23031                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23032                 break;
23033             case 'hbar':
23034                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23035                 break;
23036             case 'pie':
23037 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23038 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23039 //            
23040                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23041                 
23042                 break;
23043
23044         }
23045         
23046         if(this.title){
23047             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23048         }
23049         
23050     },
23051     
23052     setTitle: function(o)
23053     {
23054         this.title = o;
23055     },
23056     
23057     initEvents: function() {
23058         
23059         if(!this.href){
23060             this.el.on('click', this.onClick, this);
23061         }
23062     },
23063     
23064     onClick : function(e)
23065     {
23066         Roo.log('img onclick');
23067         this.fireEvent('click', this, e);
23068     }
23069    
23070 });
23071
23072  
23073 /*
23074  * - LGPL
23075  *
23076  * numberBox
23077  * 
23078  */
23079 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23080
23081 /**
23082  * @class Roo.bootstrap.dash.NumberBox
23083  * @extends Roo.bootstrap.Component
23084  * Bootstrap NumberBox class
23085  * @cfg {String} headline Box headline
23086  * @cfg {String} content Box content
23087  * @cfg {String} icon Box icon
23088  * @cfg {String} footer Footer text
23089  * @cfg {String} fhref Footer href
23090  * 
23091  * @constructor
23092  * Create a new NumberBox
23093  * @param {Object} config The config object
23094  */
23095
23096
23097 Roo.bootstrap.dash.NumberBox = function(config){
23098     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23099     
23100 };
23101
23102 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23103     
23104     headline : '',
23105     content : '',
23106     icon : '',
23107     footer : '',
23108     fhref : '',
23109     ficon : '',
23110     
23111     getAutoCreate : function(){
23112         
23113         var cfg = {
23114             tag : 'div',
23115             cls : 'small-box ',
23116             cn : [
23117                 {
23118                     tag : 'div',
23119                     cls : 'inner',
23120                     cn :[
23121                         {
23122                             tag : 'h3',
23123                             cls : 'roo-headline',
23124                             html : this.headline
23125                         },
23126                         {
23127                             tag : 'p',
23128                             cls : 'roo-content',
23129                             html : this.content
23130                         }
23131                     ]
23132                 }
23133             ]
23134         };
23135         
23136         if(this.icon){
23137             cfg.cn.push({
23138                 tag : 'div',
23139                 cls : 'icon',
23140                 cn :[
23141                     {
23142                         tag : 'i',
23143                         cls : 'ion ' + this.icon
23144                     }
23145                 ]
23146             });
23147         }
23148         
23149         if(this.footer){
23150             var footer = {
23151                 tag : 'a',
23152                 cls : 'small-box-footer',
23153                 href : this.fhref || '#',
23154                 html : this.footer
23155             };
23156             
23157             cfg.cn.push(footer);
23158             
23159         }
23160         
23161         return  cfg;
23162     },
23163
23164     onRender : function(ct,position){
23165         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23166
23167
23168        
23169                 
23170     },
23171
23172     setHeadline: function (value)
23173     {
23174         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23175     },
23176     
23177     setFooter: function (value, href)
23178     {
23179         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23180         
23181         if(href){
23182             this.el.select('a.small-box-footer',true).first().attr('href', href);
23183         }
23184         
23185     },
23186
23187     setContent: function (value)
23188     {
23189         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23190     },
23191
23192     initEvents: function() 
23193     {   
23194         
23195     }
23196     
23197 });
23198
23199  
23200 /*
23201  * - LGPL
23202  *
23203  * TabBox
23204  * 
23205  */
23206 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23207
23208 /**
23209  * @class Roo.bootstrap.dash.TabBox
23210  * @extends Roo.bootstrap.Component
23211  * Bootstrap TabBox class
23212  * @cfg {String} title Title of the TabBox
23213  * @cfg {String} icon Icon of the TabBox
23214  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23215  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23216  * 
23217  * @constructor
23218  * Create a new TabBox
23219  * @param {Object} config The config object
23220  */
23221
23222
23223 Roo.bootstrap.dash.TabBox = function(config){
23224     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23225     this.addEvents({
23226         // raw events
23227         /**
23228          * @event addpane
23229          * When a pane is added
23230          * @param {Roo.bootstrap.dash.TabPane} pane
23231          */
23232         "addpane" : true,
23233         /**
23234          * @event activatepane
23235          * When a pane is activated
23236          * @param {Roo.bootstrap.dash.TabPane} pane
23237          */
23238         "activatepane" : true
23239         
23240          
23241     });
23242     
23243     this.panes = [];
23244 };
23245
23246 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23247
23248     title : '',
23249     icon : false,
23250     showtabs : true,
23251     tabScrollable : false,
23252     
23253     getChildContainer : function()
23254     {
23255         return this.el.select('.tab-content', true).first();
23256     },
23257     
23258     getAutoCreate : function(){
23259         
23260         var header = {
23261             tag: 'li',
23262             cls: 'pull-left header',
23263             html: this.title,
23264             cn : []
23265         };
23266         
23267         if(this.icon){
23268             header.cn.push({
23269                 tag: 'i',
23270                 cls: 'fa ' + this.icon
23271             });
23272         }
23273         
23274         var h = {
23275             tag: 'ul',
23276             cls: 'nav nav-tabs pull-right',
23277             cn: [
23278                 header
23279             ]
23280         };
23281         
23282         if(this.tabScrollable){
23283             h = {
23284                 tag: 'div',
23285                 cls: 'tab-header',
23286                 cn: [
23287                     {
23288                         tag: 'ul',
23289                         cls: 'nav nav-tabs pull-right',
23290                         cn: [
23291                             header
23292                         ]
23293                     }
23294                 ]
23295             };
23296         }
23297         
23298         var cfg = {
23299             tag: 'div',
23300             cls: 'nav-tabs-custom',
23301             cn: [
23302                 h,
23303                 {
23304                     tag: 'div',
23305                     cls: 'tab-content no-padding',
23306                     cn: []
23307                 }
23308             ]
23309         };
23310
23311         return  cfg;
23312     },
23313     initEvents : function()
23314     {
23315         //Roo.log('add add pane handler');
23316         this.on('addpane', this.onAddPane, this);
23317     },
23318      /**
23319      * Updates the box title
23320      * @param {String} html to set the title to.
23321      */
23322     setTitle : function(value)
23323     {
23324         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23325     },
23326     onAddPane : function(pane)
23327     {
23328         this.panes.push(pane);
23329         //Roo.log('addpane');
23330         //Roo.log(pane);
23331         // tabs are rendere left to right..
23332         if(!this.showtabs){
23333             return;
23334         }
23335         
23336         var ctr = this.el.select('.nav-tabs', true).first();
23337          
23338          
23339         var existing = ctr.select('.nav-tab',true);
23340         var qty = existing.getCount();;
23341         
23342         
23343         var tab = ctr.createChild({
23344             tag : 'li',
23345             cls : 'nav-tab' + (qty ? '' : ' active'),
23346             cn : [
23347                 {
23348                     tag : 'a',
23349                     href:'#',
23350                     html : pane.title
23351                 }
23352             ]
23353         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23354         pane.tab = tab;
23355         
23356         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23357         if (!qty) {
23358             pane.el.addClass('active');
23359         }
23360         
23361                 
23362     },
23363     onTabClick : function(ev,un,ob,pane)
23364     {
23365         //Roo.log('tab - prev default');
23366         ev.preventDefault();
23367         
23368         
23369         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23370         pane.tab.addClass('active');
23371         //Roo.log(pane.title);
23372         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23373         // technically we should have a deactivate event.. but maybe add later.
23374         // and it should not de-activate the selected tab...
23375         this.fireEvent('activatepane', pane);
23376         pane.el.addClass('active');
23377         pane.fireEvent('activate');
23378         
23379         
23380     },
23381     
23382     getActivePane : function()
23383     {
23384         var r = false;
23385         Roo.each(this.panes, function(p) {
23386             if(p.el.hasClass('active')){
23387                 r = p;
23388                 return false;
23389             }
23390             
23391             return;
23392         });
23393         
23394         return r;
23395     }
23396     
23397     
23398 });
23399
23400  
23401 /*
23402  * - LGPL
23403  *
23404  * Tab pane
23405  * 
23406  */
23407 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23408 /**
23409  * @class Roo.bootstrap.TabPane
23410  * @extends Roo.bootstrap.Component
23411  * Bootstrap TabPane class
23412  * @cfg {Boolean} active (false | true) Default false
23413  * @cfg {String} title title of panel
23414
23415  * 
23416  * @constructor
23417  * Create a new TabPane
23418  * @param {Object} config The config object
23419  */
23420
23421 Roo.bootstrap.dash.TabPane = function(config){
23422     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23423     
23424     this.addEvents({
23425         // raw events
23426         /**
23427          * @event activate
23428          * When a pane is activated
23429          * @param {Roo.bootstrap.dash.TabPane} pane
23430          */
23431         "activate" : true
23432          
23433     });
23434 };
23435
23436 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23437     
23438     active : false,
23439     title : '',
23440     
23441     // the tabBox that this is attached to.
23442     tab : false,
23443      
23444     getAutoCreate : function() 
23445     {
23446         var cfg = {
23447             tag: 'div',
23448             cls: 'tab-pane'
23449         };
23450         
23451         if(this.active){
23452             cfg.cls += ' active';
23453         }
23454         
23455         return cfg;
23456     },
23457     initEvents  : function()
23458     {
23459         //Roo.log('trigger add pane handler');
23460         this.parent().fireEvent('addpane', this)
23461     },
23462     
23463      /**
23464      * Updates the tab title 
23465      * @param {String} html to set the title to.
23466      */
23467     setTitle: function(str)
23468     {
23469         if (!this.tab) {
23470             return;
23471         }
23472         this.title = str;
23473         this.tab.select('a', true).first().dom.innerHTML = str;
23474         
23475     }
23476     
23477     
23478     
23479 });
23480
23481  
23482
23483
23484  /*
23485  * - LGPL
23486  *
23487  * menu
23488  * 
23489  */
23490 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23491
23492 /**
23493  * @class Roo.bootstrap.menu.Menu
23494  * @extends Roo.bootstrap.Component
23495  * Bootstrap Menu class - container for Menu
23496  * @cfg {String} html Text of the menu
23497  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23498  * @cfg {String} icon Font awesome icon
23499  * @cfg {String} pos Menu align to (top | bottom) default bottom
23500  * 
23501  * 
23502  * @constructor
23503  * Create a new Menu
23504  * @param {Object} config The config object
23505  */
23506
23507
23508 Roo.bootstrap.menu.Menu = function(config){
23509     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23510     
23511     this.addEvents({
23512         /**
23513          * @event beforeshow
23514          * Fires before this menu is displayed
23515          * @param {Roo.bootstrap.menu.Menu} this
23516          */
23517         beforeshow : true,
23518         /**
23519          * @event beforehide
23520          * Fires before this menu is hidden
23521          * @param {Roo.bootstrap.menu.Menu} this
23522          */
23523         beforehide : true,
23524         /**
23525          * @event show
23526          * Fires after this menu is displayed
23527          * @param {Roo.bootstrap.menu.Menu} this
23528          */
23529         show : true,
23530         /**
23531          * @event hide
23532          * Fires after this menu is hidden
23533          * @param {Roo.bootstrap.menu.Menu} this
23534          */
23535         hide : true,
23536         /**
23537          * @event click
23538          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23539          * @param {Roo.bootstrap.menu.Menu} this
23540          * @param {Roo.EventObject} e
23541          */
23542         click : true
23543     });
23544     
23545 };
23546
23547 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23548     
23549     submenu : false,
23550     html : '',
23551     weight : 'default',
23552     icon : false,
23553     pos : 'bottom',
23554     
23555     
23556     getChildContainer : function() {
23557         if(this.isSubMenu){
23558             return this.el;
23559         }
23560         
23561         return this.el.select('ul.dropdown-menu', true).first();  
23562     },
23563     
23564     getAutoCreate : function()
23565     {
23566         var text = [
23567             {
23568                 tag : 'span',
23569                 cls : 'roo-menu-text',
23570                 html : this.html
23571             }
23572         ];
23573         
23574         if(this.icon){
23575             text.unshift({
23576                 tag : 'i',
23577                 cls : 'fa ' + this.icon
23578             })
23579         }
23580         
23581         
23582         var cfg = {
23583             tag : 'div',
23584             cls : 'btn-group',
23585             cn : [
23586                 {
23587                     tag : 'button',
23588                     cls : 'dropdown-button btn btn-' + this.weight,
23589                     cn : text
23590                 },
23591                 {
23592                     tag : 'button',
23593                     cls : 'dropdown-toggle btn btn-' + this.weight,
23594                     cn : [
23595                         {
23596                             tag : 'span',
23597                             cls : 'caret'
23598                         }
23599                     ]
23600                 },
23601                 {
23602                     tag : 'ul',
23603                     cls : 'dropdown-menu'
23604                 }
23605             ]
23606             
23607         };
23608         
23609         if(this.pos == 'top'){
23610             cfg.cls += ' dropup';
23611         }
23612         
23613         if(this.isSubMenu){
23614             cfg = {
23615                 tag : 'ul',
23616                 cls : 'dropdown-menu'
23617             }
23618         }
23619         
23620         return cfg;
23621     },
23622     
23623     onRender : function(ct, position)
23624     {
23625         this.isSubMenu = ct.hasClass('dropdown-submenu');
23626         
23627         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23628     },
23629     
23630     initEvents : function() 
23631     {
23632         if(this.isSubMenu){
23633             return;
23634         }
23635         
23636         this.hidden = true;
23637         
23638         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23639         this.triggerEl.on('click', this.onTriggerPress, this);
23640         
23641         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23642         this.buttonEl.on('click', this.onClick, this);
23643         
23644     },
23645     
23646     list : function()
23647     {
23648         if(this.isSubMenu){
23649             return this.el;
23650         }
23651         
23652         return this.el.select('ul.dropdown-menu', true).first();
23653     },
23654     
23655     onClick : function(e)
23656     {
23657         this.fireEvent("click", this, e);
23658     },
23659     
23660     onTriggerPress  : function(e)
23661     {   
23662         if (this.isVisible()) {
23663             this.hide();
23664         } else {
23665             this.show();
23666         }
23667     },
23668     
23669     isVisible : function(){
23670         return !this.hidden;
23671     },
23672     
23673     show : function()
23674     {
23675         this.fireEvent("beforeshow", this);
23676         
23677         this.hidden = false;
23678         this.el.addClass('open');
23679         
23680         Roo.get(document).on("mouseup", this.onMouseUp, this);
23681         
23682         this.fireEvent("show", this);
23683         
23684         
23685     },
23686     
23687     hide : function()
23688     {
23689         this.fireEvent("beforehide", this);
23690         
23691         this.hidden = true;
23692         this.el.removeClass('open');
23693         
23694         Roo.get(document).un("mouseup", this.onMouseUp);
23695         
23696         this.fireEvent("hide", this);
23697     },
23698     
23699     onMouseUp : function()
23700     {
23701         this.hide();
23702     }
23703     
23704 });
23705
23706  
23707  /*
23708  * - LGPL
23709  *
23710  * menu item
23711  * 
23712  */
23713 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23714
23715 /**
23716  * @class Roo.bootstrap.menu.Item
23717  * @extends Roo.bootstrap.Component
23718  * Bootstrap MenuItem class
23719  * @cfg {Boolean} submenu (true | false) default false
23720  * @cfg {String} html text of the item
23721  * @cfg {String} href the link
23722  * @cfg {Boolean} disable (true | false) default false
23723  * @cfg {Boolean} preventDefault (true | false) default true
23724  * @cfg {String} icon Font awesome icon
23725  * @cfg {String} pos Submenu align to (left | right) default right 
23726  * 
23727  * 
23728  * @constructor
23729  * Create a new Item
23730  * @param {Object} config The config object
23731  */
23732
23733
23734 Roo.bootstrap.menu.Item = function(config){
23735     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23736     this.addEvents({
23737         /**
23738          * @event mouseover
23739          * Fires when the mouse is hovering over this menu
23740          * @param {Roo.bootstrap.menu.Item} this
23741          * @param {Roo.EventObject} e
23742          */
23743         mouseover : true,
23744         /**
23745          * @event mouseout
23746          * Fires when the mouse exits this menu
23747          * @param {Roo.bootstrap.menu.Item} this
23748          * @param {Roo.EventObject} e
23749          */
23750         mouseout : true,
23751         // raw events
23752         /**
23753          * @event click
23754          * The raw click event for the entire grid.
23755          * @param {Roo.EventObject} e
23756          */
23757         click : true
23758     });
23759 };
23760
23761 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23762     
23763     submenu : false,
23764     href : '',
23765     html : '',
23766     preventDefault: true,
23767     disable : false,
23768     icon : false,
23769     pos : 'right',
23770     
23771     getAutoCreate : function()
23772     {
23773         var text = [
23774             {
23775                 tag : 'span',
23776                 cls : 'roo-menu-item-text',
23777                 html : this.html
23778             }
23779         ];
23780         
23781         if(this.icon){
23782             text.unshift({
23783                 tag : 'i',
23784                 cls : 'fa ' + this.icon
23785             })
23786         }
23787         
23788         var cfg = {
23789             tag : 'li',
23790             cn : [
23791                 {
23792                     tag : 'a',
23793                     href : this.href || '#',
23794                     cn : text
23795                 }
23796             ]
23797         };
23798         
23799         if(this.disable){
23800             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23801         }
23802         
23803         if(this.submenu){
23804             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23805             
23806             if(this.pos == 'left'){
23807                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23808             }
23809         }
23810         
23811         return cfg;
23812     },
23813     
23814     initEvents : function() 
23815     {
23816         this.el.on('mouseover', this.onMouseOver, this);
23817         this.el.on('mouseout', this.onMouseOut, this);
23818         
23819         this.el.select('a', true).first().on('click', this.onClick, this);
23820         
23821     },
23822     
23823     onClick : function(e)
23824     {
23825         if(this.preventDefault){
23826             e.preventDefault();
23827         }
23828         
23829         this.fireEvent("click", this, e);
23830     },
23831     
23832     onMouseOver : function(e)
23833     {
23834         if(this.submenu && this.pos == 'left'){
23835             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23836         }
23837         
23838         this.fireEvent("mouseover", this, e);
23839     },
23840     
23841     onMouseOut : function(e)
23842     {
23843         this.fireEvent("mouseout", this, e);
23844     }
23845 });
23846
23847  
23848
23849  /*
23850  * - LGPL
23851  *
23852  * menu separator
23853  * 
23854  */
23855 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23856
23857 /**
23858  * @class Roo.bootstrap.menu.Separator
23859  * @extends Roo.bootstrap.Component
23860  * Bootstrap Separator class
23861  * 
23862  * @constructor
23863  * Create a new Separator
23864  * @param {Object} config The config object
23865  */
23866
23867
23868 Roo.bootstrap.menu.Separator = function(config){
23869     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23870 };
23871
23872 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23873     
23874     getAutoCreate : function(){
23875         var cfg = {
23876             tag : 'li',
23877             cls: 'divider'
23878         };
23879         
23880         return cfg;
23881     }
23882    
23883 });
23884
23885  
23886
23887  /*
23888  * - LGPL
23889  *
23890  * Tooltip
23891  * 
23892  */
23893
23894 /**
23895  * @class Roo.bootstrap.Tooltip
23896  * Bootstrap Tooltip class
23897  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23898  * to determine which dom element triggers the tooltip.
23899  * 
23900  * It needs to add support for additional attributes like tooltip-position
23901  * 
23902  * @constructor
23903  * Create a new Toolti
23904  * @param {Object} config The config object
23905  */
23906
23907 Roo.bootstrap.Tooltip = function(config){
23908     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23909 };
23910
23911 Roo.apply(Roo.bootstrap.Tooltip, {
23912     /**
23913      * @function init initialize tooltip monitoring.
23914      * @static
23915      */
23916     currentEl : false,
23917     currentTip : false,
23918     currentRegion : false,
23919     
23920     //  init : delay?
23921     
23922     init : function()
23923     {
23924         Roo.get(document).on('mouseover', this.enter ,this);
23925         Roo.get(document).on('mouseout', this.leave, this);
23926          
23927         
23928         this.currentTip = new Roo.bootstrap.Tooltip();
23929     },
23930     
23931     enter : function(ev)
23932     {
23933         var dom = ev.getTarget();
23934         
23935         //Roo.log(['enter',dom]);
23936         var el = Roo.fly(dom);
23937         if (this.currentEl) {
23938             //Roo.log(dom);
23939             //Roo.log(this.currentEl);
23940             //Roo.log(this.currentEl.contains(dom));
23941             if (this.currentEl == el) {
23942                 return;
23943             }
23944             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23945                 return;
23946             }
23947
23948         }
23949         
23950         if (this.currentTip.el) {
23951             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23952         }    
23953         //Roo.log(ev);
23954         var bindEl = el;
23955         
23956         // you can not look for children, as if el is the body.. then everythign is the child..
23957         if (!el.attr('tooltip')) { //
23958             if (!el.select("[tooltip]").elements.length) {
23959                 return;
23960             }
23961             // is the mouse over this child...?
23962             bindEl = el.select("[tooltip]").first();
23963             var xy = ev.getXY();
23964             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23965                 //Roo.log("not in region.");
23966                 return;
23967             }
23968             //Roo.log("child element over..");
23969             
23970         }
23971         this.currentEl = bindEl;
23972         this.currentTip.bind(bindEl);
23973         this.currentRegion = Roo.lib.Region.getRegion(dom);
23974         this.currentTip.enter();
23975         
23976     },
23977     leave : function(ev)
23978     {
23979         var dom = ev.getTarget();
23980         //Roo.log(['leave',dom]);
23981         if (!this.currentEl) {
23982             return;
23983         }
23984         
23985         
23986         if (dom != this.currentEl.dom) {
23987             return;
23988         }
23989         var xy = ev.getXY();
23990         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23991             return;
23992         }
23993         // only activate leave if mouse cursor is outside... bounding box..
23994         
23995         
23996         
23997         
23998         if (this.currentTip) {
23999             this.currentTip.leave();
24000         }
24001         //Roo.log('clear currentEl');
24002         this.currentEl = false;
24003         
24004         
24005     },
24006     alignment : {
24007         'left' : ['r-l', [-2,0], 'right'],
24008         'right' : ['l-r', [2,0], 'left'],
24009         'bottom' : ['t-b', [0,2], 'top'],
24010         'top' : [ 'b-t', [0,-2], 'bottom']
24011     }
24012     
24013 });
24014
24015
24016 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24017     
24018     
24019     bindEl : false,
24020     
24021     delay : null, // can be { show : 300 , hide: 500}
24022     
24023     timeout : null,
24024     
24025     hoverState : null, //???
24026     
24027     placement : 'bottom', 
24028     
24029     getAutoCreate : function(){
24030     
24031         var cfg = {
24032            cls : 'tooltip',
24033            role : 'tooltip',
24034            cn : [
24035                 {
24036                     cls : 'tooltip-arrow'
24037                 },
24038                 {
24039                     cls : 'tooltip-inner'
24040                 }
24041            ]
24042         };
24043         
24044         return cfg;
24045     },
24046     bind : function(el)
24047     {
24048         this.bindEl = el;
24049     },
24050       
24051     
24052     enter : function () {
24053        
24054         if (this.timeout != null) {
24055             clearTimeout(this.timeout);
24056         }
24057         
24058         this.hoverState = 'in';
24059          //Roo.log("enter - show");
24060         if (!this.delay || !this.delay.show) {
24061             this.show();
24062             return;
24063         }
24064         var _t = this;
24065         this.timeout = setTimeout(function () {
24066             if (_t.hoverState == 'in') {
24067                 _t.show();
24068             }
24069         }, this.delay.show);
24070     },
24071     leave : function()
24072     {
24073         clearTimeout(this.timeout);
24074     
24075         this.hoverState = 'out';
24076          if (!this.delay || !this.delay.hide) {
24077             this.hide();
24078             return;
24079         }
24080        
24081         var _t = this;
24082         this.timeout = setTimeout(function () {
24083             //Roo.log("leave - timeout");
24084             
24085             if (_t.hoverState == 'out') {
24086                 _t.hide();
24087                 Roo.bootstrap.Tooltip.currentEl = false;
24088             }
24089         }, delay);
24090     },
24091     
24092     show : function ()
24093     {
24094         if (!this.el) {
24095             this.render(document.body);
24096         }
24097         // set content.
24098         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24099         
24100         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24101         
24102         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24103         
24104         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24105         
24106         var placement = typeof this.placement == 'function' ?
24107             this.placement.call(this, this.el, on_el) :
24108             this.placement;
24109             
24110         var autoToken = /\s?auto?\s?/i;
24111         var autoPlace = autoToken.test(placement);
24112         if (autoPlace) {
24113             placement = placement.replace(autoToken, '') || 'top';
24114         }
24115         
24116         //this.el.detach()
24117         //this.el.setXY([0,0]);
24118         this.el.show();
24119         //this.el.dom.style.display='block';
24120         
24121         //this.el.appendTo(on_el);
24122         
24123         var p = this.getPosition();
24124         var box = this.el.getBox();
24125         
24126         if (autoPlace) {
24127             // fixme..
24128         }
24129         
24130         var align = Roo.bootstrap.Tooltip.alignment[placement];
24131         
24132         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24133         
24134         if(placement == 'top' || placement == 'bottom'){
24135             if(xy[0] < 0){
24136                 placement = 'right';
24137             }
24138             
24139             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24140                 placement = 'left';
24141             }
24142         }
24143         
24144         align = Roo.bootstrap.Tooltip.alignment[placement];
24145         
24146         this.el.alignTo(this.bindEl, align[0],align[1]);
24147         //var arrow = this.el.select('.arrow',true).first();
24148         //arrow.set(align[2], 
24149         
24150         this.el.addClass(placement);
24151         
24152         this.el.addClass('in fade');
24153         
24154         this.hoverState = null;
24155         
24156         if (this.el.hasClass('fade')) {
24157             // fade it?
24158         }
24159         
24160     },
24161     hide : function()
24162     {
24163          
24164         if (!this.el) {
24165             return;
24166         }
24167         //this.el.setXY([0,0]);
24168         this.el.removeClass('in');
24169         //this.el.hide();
24170         
24171     }
24172     
24173 });
24174  
24175
24176  /*
24177  * - LGPL
24178  *
24179  * Location Picker
24180  * 
24181  */
24182
24183 /**
24184  * @class Roo.bootstrap.LocationPicker
24185  * @extends Roo.bootstrap.Component
24186  * Bootstrap LocationPicker class
24187  * @cfg {Number} latitude Position when init default 0
24188  * @cfg {Number} longitude Position when init default 0
24189  * @cfg {Number} zoom default 15
24190  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24191  * @cfg {Boolean} mapTypeControl default false
24192  * @cfg {Boolean} disableDoubleClickZoom default false
24193  * @cfg {Boolean} scrollwheel default true
24194  * @cfg {Boolean} streetViewControl default false
24195  * @cfg {Number} radius default 0
24196  * @cfg {String} locationName
24197  * @cfg {Boolean} draggable default true
24198  * @cfg {Boolean} enableAutocomplete default false
24199  * @cfg {Boolean} enableReverseGeocode default true
24200  * @cfg {String} markerTitle
24201  * 
24202  * @constructor
24203  * Create a new LocationPicker
24204  * @param {Object} config The config object
24205  */
24206
24207
24208 Roo.bootstrap.LocationPicker = function(config){
24209     
24210     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24211     
24212     this.addEvents({
24213         /**
24214          * @event initial
24215          * Fires when the picker initialized.
24216          * @param {Roo.bootstrap.LocationPicker} this
24217          * @param {Google Location} location
24218          */
24219         initial : true,
24220         /**
24221          * @event positionchanged
24222          * Fires when the picker position changed.
24223          * @param {Roo.bootstrap.LocationPicker} this
24224          * @param {Google Location} location
24225          */
24226         positionchanged : true,
24227         /**
24228          * @event resize
24229          * Fires when the map resize.
24230          * @param {Roo.bootstrap.LocationPicker} this
24231          */
24232         resize : true,
24233         /**
24234          * @event show
24235          * Fires when the map show.
24236          * @param {Roo.bootstrap.LocationPicker} this
24237          */
24238         show : true,
24239         /**
24240          * @event hide
24241          * Fires when the map hide.
24242          * @param {Roo.bootstrap.LocationPicker} this
24243          */
24244         hide : true,
24245         /**
24246          * @event mapClick
24247          * Fires when click the map.
24248          * @param {Roo.bootstrap.LocationPicker} this
24249          * @param {Map event} e
24250          */
24251         mapClick : true,
24252         /**
24253          * @event mapRightClick
24254          * Fires when right click the map.
24255          * @param {Roo.bootstrap.LocationPicker} this
24256          * @param {Map event} e
24257          */
24258         mapRightClick : true,
24259         /**
24260          * @event markerClick
24261          * Fires when click the marker.
24262          * @param {Roo.bootstrap.LocationPicker} this
24263          * @param {Map event} e
24264          */
24265         markerClick : true,
24266         /**
24267          * @event markerRightClick
24268          * Fires when right click the marker.
24269          * @param {Roo.bootstrap.LocationPicker} this
24270          * @param {Map event} e
24271          */
24272         markerRightClick : true,
24273         /**
24274          * @event OverlayViewDraw
24275          * Fires when OverlayView Draw
24276          * @param {Roo.bootstrap.LocationPicker} this
24277          */
24278         OverlayViewDraw : true,
24279         /**
24280          * @event OverlayViewOnAdd
24281          * Fires when OverlayView Draw
24282          * @param {Roo.bootstrap.LocationPicker} this
24283          */
24284         OverlayViewOnAdd : true,
24285         /**
24286          * @event OverlayViewOnRemove
24287          * Fires when OverlayView Draw
24288          * @param {Roo.bootstrap.LocationPicker} this
24289          */
24290         OverlayViewOnRemove : true,
24291         /**
24292          * @event OverlayViewShow
24293          * Fires when OverlayView Draw
24294          * @param {Roo.bootstrap.LocationPicker} this
24295          * @param {Pixel} cpx
24296          */
24297         OverlayViewShow : true,
24298         /**
24299          * @event OverlayViewHide
24300          * Fires when OverlayView Draw
24301          * @param {Roo.bootstrap.LocationPicker} this
24302          */
24303         OverlayViewHide : true,
24304         /**
24305          * @event loadexception
24306          * Fires when load google lib failed.
24307          * @param {Roo.bootstrap.LocationPicker} this
24308          */
24309         loadexception : true
24310     });
24311         
24312 };
24313
24314 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24315     
24316     gMapContext: false,
24317     
24318     latitude: 0,
24319     longitude: 0,
24320     zoom: 15,
24321     mapTypeId: false,
24322     mapTypeControl: false,
24323     disableDoubleClickZoom: false,
24324     scrollwheel: true,
24325     streetViewControl: false,
24326     radius: 0,
24327     locationName: '',
24328     draggable: true,
24329     enableAutocomplete: false,
24330     enableReverseGeocode: true,
24331     markerTitle: '',
24332     
24333     getAutoCreate: function()
24334     {
24335
24336         var cfg = {
24337             tag: 'div',
24338             cls: 'roo-location-picker'
24339         };
24340         
24341         return cfg
24342     },
24343     
24344     initEvents: function(ct, position)
24345     {       
24346         if(!this.el.getWidth() || this.isApplied()){
24347             return;
24348         }
24349         
24350         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24351         
24352         this.initial();
24353     },
24354     
24355     initial: function()
24356     {
24357         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24358             this.fireEvent('loadexception', this);
24359             return;
24360         }
24361         
24362         if(!this.mapTypeId){
24363             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24364         }
24365         
24366         this.gMapContext = this.GMapContext();
24367         
24368         this.initOverlayView();
24369         
24370         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24371         
24372         var _this = this;
24373                 
24374         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24375             _this.setPosition(_this.gMapContext.marker.position);
24376         });
24377         
24378         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24379             _this.fireEvent('mapClick', this, event);
24380             
24381         });
24382
24383         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24384             _this.fireEvent('mapRightClick', this, event);
24385             
24386         });
24387         
24388         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24389             _this.fireEvent('markerClick', this, event);
24390             
24391         });
24392
24393         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24394             _this.fireEvent('markerRightClick', this, event);
24395             
24396         });
24397         
24398         this.setPosition(this.gMapContext.location);
24399         
24400         this.fireEvent('initial', this, this.gMapContext.location);
24401     },
24402     
24403     initOverlayView: function()
24404     {
24405         var _this = this;
24406         
24407         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24408             
24409             draw: function()
24410             {
24411                 _this.fireEvent('OverlayViewDraw', _this);
24412             },
24413             
24414             onAdd: function()
24415             {
24416                 _this.fireEvent('OverlayViewOnAdd', _this);
24417             },
24418             
24419             onRemove: function()
24420             {
24421                 _this.fireEvent('OverlayViewOnRemove', _this);
24422             },
24423             
24424             show: function(cpx)
24425             {
24426                 _this.fireEvent('OverlayViewShow', _this, cpx);
24427             },
24428             
24429             hide: function()
24430             {
24431                 _this.fireEvent('OverlayViewHide', _this);
24432             }
24433             
24434         });
24435     },
24436     
24437     fromLatLngToContainerPixel: function(event)
24438     {
24439         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24440     },
24441     
24442     isApplied: function() 
24443     {
24444         return this.getGmapContext() == false ? false : true;
24445     },
24446     
24447     getGmapContext: function() 
24448     {
24449         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24450     },
24451     
24452     GMapContext: function() 
24453     {
24454         var position = new google.maps.LatLng(this.latitude, this.longitude);
24455         
24456         var _map = new google.maps.Map(this.el.dom, {
24457             center: position,
24458             zoom: this.zoom,
24459             mapTypeId: this.mapTypeId,
24460             mapTypeControl: this.mapTypeControl,
24461             disableDoubleClickZoom: this.disableDoubleClickZoom,
24462             scrollwheel: this.scrollwheel,
24463             streetViewControl: this.streetViewControl,
24464             locationName: this.locationName,
24465             draggable: this.draggable,
24466             enableAutocomplete: this.enableAutocomplete,
24467             enableReverseGeocode: this.enableReverseGeocode
24468         });
24469         
24470         var _marker = new google.maps.Marker({
24471             position: position,
24472             map: _map,
24473             title: this.markerTitle,
24474             draggable: this.draggable
24475         });
24476         
24477         return {
24478             map: _map,
24479             marker: _marker,
24480             circle: null,
24481             location: position,
24482             radius: this.radius,
24483             locationName: this.locationName,
24484             addressComponents: {
24485                 formatted_address: null,
24486                 addressLine1: null,
24487                 addressLine2: null,
24488                 streetName: null,
24489                 streetNumber: null,
24490                 city: null,
24491                 district: null,
24492                 state: null,
24493                 stateOrProvince: null
24494             },
24495             settings: this,
24496             domContainer: this.el.dom,
24497             geodecoder: new google.maps.Geocoder()
24498         };
24499     },
24500     
24501     drawCircle: function(center, radius, options) 
24502     {
24503         if (this.gMapContext.circle != null) {
24504             this.gMapContext.circle.setMap(null);
24505         }
24506         if (radius > 0) {
24507             radius *= 1;
24508             options = Roo.apply({}, options, {
24509                 strokeColor: "#0000FF",
24510                 strokeOpacity: .35,
24511                 strokeWeight: 2,
24512                 fillColor: "#0000FF",
24513                 fillOpacity: .2
24514             });
24515             
24516             options.map = this.gMapContext.map;
24517             options.radius = radius;
24518             options.center = center;
24519             this.gMapContext.circle = new google.maps.Circle(options);
24520             return this.gMapContext.circle;
24521         }
24522         
24523         return null;
24524     },
24525     
24526     setPosition: function(location) 
24527     {
24528         this.gMapContext.location = location;
24529         this.gMapContext.marker.setPosition(location);
24530         this.gMapContext.map.panTo(location);
24531         this.drawCircle(location, this.gMapContext.radius, {});
24532         
24533         var _this = this;
24534         
24535         if (this.gMapContext.settings.enableReverseGeocode) {
24536             this.gMapContext.geodecoder.geocode({
24537                 latLng: this.gMapContext.location
24538             }, function(results, status) {
24539                 
24540                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24541                     _this.gMapContext.locationName = results[0].formatted_address;
24542                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24543                     
24544                     _this.fireEvent('positionchanged', this, location);
24545                 }
24546             });
24547             
24548             return;
24549         }
24550         
24551         this.fireEvent('positionchanged', this, location);
24552     },
24553     
24554     resize: function()
24555     {
24556         google.maps.event.trigger(this.gMapContext.map, "resize");
24557         
24558         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24559         
24560         this.fireEvent('resize', this);
24561     },
24562     
24563     setPositionByLatLng: function(latitude, longitude)
24564     {
24565         this.setPosition(new google.maps.LatLng(latitude, longitude));
24566     },
24567     
24568     getCurrentPosition: function() 
24569     {
24570         return {
24571             latitude: this.gMapContext.location.lat(),
24572             longitude: this.gMapContext.location.lng()
24573         };
24574     },
24575     
24576     getAddressName: function() 
24577     {
24578         return this.gMapContext.locationName;
24579     },
24580     
24581     getAddressComponents: function() 
24582     {
24583         return this.gMapContext.addressComponents;
24584     },
24585     
24586     address_component_from_google_geocode: function(address_components) 
24587     {
24588         var result = {};
24589         
24590         for (var i = 0; i < address_components.length; i++) {
24591             var component = address_components[i];
24592             if (component.types.indexOf("postal_code") >= 0) {
24593                 result.postalCode = component.short_name;
24594             } else if (component.types.indexOf("street_number") >= 0) {
24595                 result.streetNumber = component.short_name;
24596             } else if (component.types.indexOf("route") >= 0) {
24597                 result.streetName = component.short_name;
24598             } else if (component.types.indexOf("neighborhood") >= 0) {
24599                 result.city = component.short_name;
24600             } else if (component.types.indexOf("locality") >= 0) {
24601                 result.city = component.short_name;
24602             } else if (component.types.indexOf("sublocality") >= 0) {
24603                 result.district = component.short_name;
24604             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24605                 result.stateOrProvince = component.short_name;
24606             } else if (component.types.indexOf("country") >= 0) {
24607                 result.country = component.short_name;
24608             }
24609         }
24610         
24611         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24612         result.addressLine2 = "";
24613         return result;
24614     },
24615     
24616     setZoomLevel: function(zoom)
24617     {
24618         this.gMapContext.map.setZoom(zoom);
24619     },
24620     
24621     show: function()
24622     {
24623         if(!this.el){
24624             return;
24625         }
24626         
24627         this.el.show();
24628         
24629         this.resize();
24630         
24631         this.fireEvent('show', this);
24632     },
24633     
24634     hide: function()
24635     {
24636         if(!this.el){
24637             return;
24638         }
24639         
24640         this.el.hide();
24641         
24642         this.fireEvent('hide', this);
24643     }
24644     
24645 });
24646
24647 Roo.apply(Roo.bootstrap.LocationPicker, {
24648     
24649     OverlayView : function(map, options)
24650     {
24651         options = options || {};
24652         
24653         this.setMap(map);
24654     }
24655     
24656     
24657 });/*
24658  * - LGPL
24659  *
24660  * Alert
24661  * 
24662  */
24663
24664 /**
24665  * @class Roo.bootstrap.Alert
24666  * @extends Roo.bootstrap.Component
24667  * Bootstrap Alert class
24668  * @cfg {String} title The title of alert
24669  * @cfg {String} html The content of alert
24670  * @cfg {String} weight (  success | info | warning | danger )
24671  * @cfg {String} faicon font-awesomeicon
24672  * 
24673  * @constructor
24674  * Create a new alert
24675  * @param {Object} config The config object
24676  */
24677
24678
24679 Roo.bootstrap.Alert = function(config){
24680     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24681     
24682 };
24683
24684 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24685     
24686     title: '',
24687     html: '',
24688     weight: false,
24689     faicon: false,
24690     
24691     getAutoCreate : function()
24692     {
24693         
24694         var cfg = {
24695             tag : 'div',
24696             cls : 'alert',
24697             cn : [
24698                 {
24699                     tag : 'i',
24700                     cls : 'roo-alert-icon'
24701                     
24702                 },
24703                 {
24704                     tag : 'b',
24705                     cls : 'roo-alert-title',
24706                     html : this.title
24707                 },
24708                 {
24709                     tag : 'span',
24710                     cls : 'roo-alert-text',
24711                     html : this.html
24712                 }
24713             ]
24714         };
24715         
24716         if(this.faicon){
24717             cfg.cn[0].cls += ' fa ' + this.faicon;
24718         }
24719         
24720         if(this.weight){
24721             cfg.cls += ' alert-' + this.weight;
24722         }
24723         
24724         return cfg;
24725     },
24726     
24727     initEvents: function() 
24728     {
24729         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24730     },
24731     
24732     setTitle : function(str)
24733     {
24734         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24735     },
24736     
24737     setText : function(str)
24738     {
24739         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24740     },
24741     
24742     setWeight : function(weight)
24743     {
24744         if(this.weight){
24745             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24746         }
24747         
24748         this.weight = weight;
24749         
24750         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24751     },
24752     
24753     setIcon : function(icon)
24754     {
24755         if(this.faicon){
24756             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24757         }
24758         
24759         this.faicon = icon;
24760         
24761         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24762     },
24763     
24764     hide: function() 
24765     {
24766         this.el.hide();   
24767     },
24768     
24769     show: function() 
24770     {  
24771         this.el.show();   
24772     }
24773     
24774 });
24775
24776  
24777 /*
24778 * Licence: LGPL
24779 */
24780
24781 /**
24782  * @class Roo.bootstrap.UploadCropbox
24783  * @extends Roo.bootstrap.Component
24784  * Bootstrap UploadCropbox class
24785  * @cfg {String} emptyText show when image has been loaded
24786  * @cfg {String} rotateNotify show when image too small to rotate
24787  * @cfg {Number} errorTimeout default 3000
24788  * @cfg {Number} minWidth default 300
24789  * @cfg {Number} minHeight default 300
24790  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24791  * @cfg {Boolean} isDocument (true|false) default false
24792  * @cfg {String} url action url
24793  * @cfg {String} paramName default 'imageUpload'
24794  * @cfg {String} method default POST
24795  * @cfg {Boolean} loadMask (true|false) default true
24796  * @cfg {Boolean} loadingText default 'Loading...'
24797  * 
24798  * @constructor
24799  * Create a new UploadCropbox
24800  * @param {Object} config The config object
24801  */
24802
24803 Roo.bootstrap.UploadCropbox = function(config){
24804     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24805     
24806     this.addEvents({
24807         /**
24808          * @event beforeselectfile
24809          * Fire before select file
24810          * @param {Roo.bootstrap.UploadCropbox} this
24811          */
24812         "beforeselectfile" : true,
24813         /**
24814          * @event initial
24815          * Fire after initEvent
24816          * @param {Roo.bootstrap.UploadCropbox} this
24817          */
24818         "initial" : true,
24819         /**
24820          * @event crop
24821          * Fire after initEvent
24822          * @param {Roo.bootstrap.UploadCropbox} this
24823          * @param {String} data
24824          */
24825         "crop" : true,
24826         /**
24827          * @event prepare
24828          * Fire when preparing the file data
24829          * @param {Roo.bootstrap.UploadCropbox} this
24830          * @param {Object} file
24831          */
24832         "prepare" : true,
24833         /**
24834          * @event exception
24835          * Fire when get exception
24836          * @param {Roo.bootstrap.UploadCropbox} this
24837          * @param {XMLHttpRequest} xhr
24838          */
24839         "exception" : true,
24840         /**
24841          * @event beforeloadcanvas
24842          * Fire before load the canvas
24843          * @param {Roo.bootstrap.UploadCropbox} this
24844          * @param {String} src
24845          */
24846         "beforeloadcanvas" : true,
24847         /**
24848          * @event trash
24849          * Fire when trash image
24850          * @param {Roo.bootstrap.UploadCropbox} this
24851          */
24852         "trash" : true,
24853         /**
24854          * @event download
24855          * Fire when download the image
24856          * @param {Roo.bootstrap.UploadCropbox} this
24857          */
24858         "download" : true,
24859         /**
24860          * @event footerbuttonclick
24861          * Fire when footerbuttonclick
24862          * @param {Roo.bootstrap.UploadCropbox} this
24863          * @param {String} type
24864          */
24865         "footerbuttonclick" : true,
24866         /**
24867          * @event resize
24868          * Fire when resize
24869          * @param {Roo.bootstrap.UploadCropbox} this
24870          */
24871         "resize" : true,
24872         /**
24873          * @event rotate
24874          * Fire when rotate the image
24875          * @param {Roo.bootstrap.UploadCropbox} this
24876          * @param {String} pos
24877          */
24878         "rotate" : true,
24879         /**
24880          * @event inspect
24881          * Fire when inspect the file
24882          * @param {Roo.bootstrap.UploadCropbox} this
24883          * @param {Object} file
24884          */
24885         "inspect" : true,
24886         /**
24887          * @event upload
24888          * Fire when xhr upload the file
24889          * @param {Roo.bootstrap.UploadCropbox} this
24890          * @param {Object} data
24891          */
24892         "upload" : true,
24893         /**
24894          * @event arrange
24895          * Fire when arrange the file data
24896          * @param {Roo.bootstrap.UploadCropbox} this
24897          * @param {Object} formData
24898          */
24899         "arrange" : true
24900     });
24901     
24902     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24903 };
24904
24905 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24906     
24907     emptyText : 'Click to upload image',
24908     rotateNotify : 'Image is too small to rotate',
24909     errorTimeout : 3000,
24910     scale : 0,
24911     baseScale : 1,
24912     rotate : 0,
24913     dragable : false,
24914     pinching : false,
24915     mouseX : 0,
24916     mouseY : 0,
24917     cropData : false,
24918     minWidth : 300,
24919     minHeight : 300,
24920     file : false,
24921     exif : {},
24922     baseRotate : 1,
24923     cropType : 'image/jpeg',
24924     buttons : false,
24925     canvasLoaded : false,
24926     isDocument : false,
24927     method : 'POST',
24928     paramName : 'imageUpload',
24929     loadMask : true,
24930     loadingText : 'Loading...',
24931     maskEl : false,
24932     
24933     getAutoCreate : function()
24934     {
24935         var cfg = {
24936             tag : 'div',
24937             cls : 'roo-upload-cropbox',
24938             cn : [
24939                 {
24940                     tag : 'input',
24941                     cls : 'roo-upload-cropbox-selector',
24942                     type : 'file'
24943                 },
24944                 {
24945                     tag : 'div',
24946                     cls : 'roo-upload-cropbox-body',
24947                     style : 'cursor:pointer',
24948                     cn : [
24949                         {
24950                             tag : 'div',
24951                             cls : 'roo-upload-cropbox-preview'
24952                         },
24953                         {
24954                             tag : 'div',
24955                             cls : 'roo-upload-cropbox-thumb'
24956                         },
24957                         {
24958                             tag : 'div',
24959                             cls : 'roo-upload-cropbox-empty-notify',
24960                             html : this.emptyText
24961                         },
24962                         {
24963                             tag : 'div',
24964                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24965                             html : this.rotateNotify
24966                         }
24967                     ]
24968                 },
24969                 {
24970                     tag : 'div',
24971                     cls : 'roo-upload-cropbox-footer',
24972                     cn : {
24973                         tag : 'div',
24974                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24975                         cn : []
24976                     }
24977                 }
24978             ]
24979         };
24980         
24981         return cfg;
24982     },
24983     
24984     onRender : function(ct, position)
24985     {
24986         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24987         
24988         if (this.buttons.length) {
24989             
24990             Roo.each(this.buttons, function(bb) {
24991                 
24992                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24993                 
24994                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24995                 
24996             }, this);
24997         }
24998         
24999         if(this.loadMask){
25000             this.maskEl = this.el;
25001         }
25002     },
25003     
25004     initEvents : function()
25005     {
25006         this.urlAPI = (window.createObjectURL && window) || 
25007                                 (window.URL && URL.revokeObjectURL && URL) || 
25008                                 (window.webkitURL && webkitURL);
25009                         
25010         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25011         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25012         
25013         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25014         this.selectorEl.hide();
25015         
25016         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25017         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25018         
25019         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25020         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25021         this.thumbEl.hide();
25022         
25023         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25024         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25025         
25026         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25027         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25028         this.errorEl.hide();
25029         
25030         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25031         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25032         this.footerEl.hide();
25033         
25034         this.setThumbBoxSize();
25035         
25036         this.bind();
25037         
25038         this.resize();
25039         
25040         this.fireEvent('initial', this);
25041     },
25042
25043     bind : function()
25044     {
25045         var _this = this;
25046         
25047         window.addEventListener("resize", function() { _this.resize(); } );
25048         
25049         this.bodyEl.on('click', this.beforeSelectFile, this);
25050         
25051         if(Roo.isTouch){
25052             this.bodyEl.on('touchstart', this.onTouchStart, this);
25053             this.bodyEl.on('touchmove', this.onTouchMove, this);
25054             this.bodyEl.on('touchend', this.onTouchEnd, this);
25055         }
25056         
25057         if(!Roo.isTouch){
25058             this.bodyEl.on('mousedown', this.onMouseDown, this);
25059             this.bodyEl.on('mousemove', this.onMouseMove, this);
25060             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25061             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25062             Roo.get(document).on('mouseup', this.onMouseUp, this);
25063         }
25064         
25065         this.selectorEl.on('change', this.onFileSelected, this);
25066     },
25067     
25068     reset : function()
25069     {    
25070         this.scale = 0;
25071         this.baseScale = 1;
25072         this.rotate = 0;
25073         this.baseRotate = 1;
25074         this.dragable = false;
25075         this.pinching = false;
25076         this.mouseX = 0;
25077         this.mouseY = 0;
25078         this.cropData = false;
25079         this.notifyEl.dom.innerHTML = this.emptyText;
25080         
25081         this.selectorEl.dom.value = '';
25082         
25083     },
25084     
25085     resize : function()
25086     {
25087         if(this.fireEvent('resize', this) != false){
25088             this.setThumbBoxPosition();
25089             this.setCanvasPosition();
25090         }
25091     },
25092     
25093     onFooterButtonClick : function(e, el, o, type)
25094     {
25095         switch (type) {
25096             case 'rotate-left' :
25097                 this.onRotateLeft(e);
25098                 break;
25099             case 'rotate-right' :
25100                 this.onRotateRight(e);
25101                 break;
25102             case 'picture' :
25103                 this.beforeSelectFile(e);
25104                 break;
25105             case 'trash' :
25106                 this.trash(e);
25107                 break;
25108             case 'crop' :
25109                 this.crop(e);
25110                 break;
25111             case 'download' :
25112                 this.download(e);
25113                 break;
25114             default :
25115                 break;
25116         }
25117         
25118         this.fireEvent('footerbuttonclick', this, type);
25119     },
25120     
25121     beforeSelectFile : function(e)
25122     {
25123         e.preventDefault();
25124         
25125         if(this.fireEvent('beforeselectfile', this) != false){
25126             this.selectorEl.dom.click();
25127         }
25128     },
25129     
25130     onFileSelected : function(e)
25131     {
25132         e.preventDefault();
25133         
25134         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25135             return;
25136         }
25137         
25138         var file = this.selectorEl.dom.files[0];
25139         
25140         if(this.fireEvent('inspect', this, file) != false){
25141             this.prepare(file);
25142         }
25143         
25144     },
25145     
25146     trash : function(e)
25147     {
25148         this.fireEvent('trash', this);
25149     },
25150     
25151     download : function(e)
25152     {
25153         this.fireEvent('download', this);
25154     },
25155     
25156     loadCanvas : function(src)
25157     {   
25158         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25159             
25160             this.reset();
25161             
25162             this.imageEl = document.createElement('img');
25163             
25164             var _this = this;
25165             
25166             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25167             
25168             this.imageEl.src = src;
25169         }
25170     },
25171     
25172     onLoadCanvas : function()
25173     {   
25174         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25175         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25176         
25177         this.bodyEl.un('click', this.beforeSelectFile, this);
25178         
25179         this.notifyEl.hide();
25180         this.thumbEl.show();
25181         this.footerEl.show();
25182         
25183         this.baseRotateLevel();
25184         
25185         if(this.isDocument){
25186             this.setThumbBoxSize();
25187         }
25188         
25189         this.setThumbBoxPosition();
25190         
25191         this.baseScaleLevel();
25192         
25193         this.draw();
25194         
25195         this.resize();
25196         
25197         this.canvasLoaded = true;
25198         
25199         if(this.loadMask){
25200             this.maskEl.unmask();
25201         }
25202         
25203     },
25204     
25205     setCanvasPosition : function()
25206     {   
25207         if(!this.canvasEl){
25208             return;
25209         }
25210         
25211         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25212         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25213         
25214         this.previewEl.setLeft(pw);
25215         this.previewEl.setTop(ph);
25216         
25217     },
25218     
25219     onMouseDown : function(e)
25220     {   
25221         e.stopEvent();
25222         
25223         this.dragable = true;
25224         this.pinching = false;
25225         
25226         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25227             this.dragable = false;
25228             return;
25229         }
25230         
25231         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25232         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25233         
25234     },
25235     
25236     onMouseMove : function(e)
25237     {   
25238         e.stopEvent();
25239         
25240         if(!this.canvasLoaded){
25241             return;
25242         }
25243         
25244         if (!this.dragable){
25245             return;
25246         }
25247         
25248         var minX = Math.ceil(this.thumbEl.getLeft(true));
25249         var minY = Math.ceil(this.thumbEl.getTop(true));
25250         
25251         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25252         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25253         
25254         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25255         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25256         
25257         x = x - this.mouseX;
25258         y = y - this.mouseY;
25259         
25260         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25261         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25262         
25263         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25264         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25265         
25266         this.previewEl.setLeft(bgX);
25267         this.previewEl.setTop(bgY);
25268         
25269         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25270         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25271     },
25272     
25273     onMouseUp : function(e)
25274     {   
25275         e.stopEvent();
25276         
25277         this.dragable = false;
25278     },
25279     
25280     onMouseWheel : function(e)
25281     {   
25282         e.stopEvent();
25283         
25284         this.startScale = this.scale;
25285         
25286         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25287         
25288         if(!this.zoomable()){
25289             this.scale = this.startScale;
25290             return;
25291         }
25292         
25293         this.draw();
25294         
25295         return;
25296     },
25297     
25298     zoomable : function()
25299     {
25300         var minScale = this.thumbEl.getWidth() / this.minWidth;
25301         
25302         if(this.minWidth < this.minHeight){
25303             minScale = this.thumbEl.getHeight() / this.minHeight;
25304         }
25305         
25306         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25307         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25308         
25309         if(
25310                 this.isDocument &&
25311                 (this.rotate == 0 || this.rotate == 180) && 
25312                 (
25313                     width > this.imageEl.OriginWidth || 
25314                     height > this.imageEl.OriginHeight ||
25315                     (width < this.minWidth && height < this.minHeight)
25316                 )
25317         ){
25318             return false;
25319         }
25320         
25321         if(
25322                 this.isDocument &&
25323                 (this.rotate == 90 || this.rotate == 270) && 
25324                 (
25325                     width > this.imageEl.OriginWidth || 
25326                     height > this.imageEl.OriginHeight ||
25327                     (width < this.minHeight && height < this.minWidth)
25328                 )
25329         ){
25330             return false;
25331         }
25332         
25333         if(
25334                 !this.isDocument &&
25335                 (this.rotate == 0 || this.rotate == 180) && 
25336                 (
25337                     width < this.minWidth || 
25338                     width > this.imageEl.OriginWidth || 
25339                     height < this.minHeight || 
25340                     height > this.imageEl.OriginHeight
25341                 )
25342         ){
25343             return false;
25344         }
25345         
25346         if(
25347                 !this.isDocument &&
25348                 (this.rotate == 90 || this.rotate == 270) && 
25349                 (
25350                     width < this.minHeight || 
25351                     width > this.imageEl.OriginWidth || 
25352                     height < this.minWidth || 
25353                     height > this.imageEl.OriginHeight
25354                 )
25355         ){
25356             return false;
25357         }
25358         
25359         return true;
25360         
25361     },
25362     
25363     onRotateLeft : function(e)
25364     {   
25365         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25366             
25367             var minScale = this.thumbEl.getWidth() / this.minWidth;
25368             
25369             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25370             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25371             
25372             this.startScale = this.scale;
25373             
25374             while (this.getScaleLevel() < minScale){
25375             
25376                 this.scale = this.scale + 1;
25377                 
25378                 if(!this.zoomable()){
25379                     break;
25380                 }
25381                 
25382                 if(
25383                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25384                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25385                 ){
25386                     continue;
25387                 }
25388                 
25389                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25390
25391                 this.draw();
25392                 
25393                 return;
25394             }
25395             
25396             this.scale = this.startScale;
25397             
25398             this.onRotateFail();
25399             
25400             return false;
25401         }
25402         
25403         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25404
25405         if(this.isDocument){
25406             this.setThumbBoxSize();
25407             this.setThumbBoxPosition();
25408             this.setCanvasPosition();
25409         }
25410         
25411         this.draw();
25412         
25413         this.fireEvent('rotate', this, 'left');
25414         
25415     },
25416     
25417     onRotateRight : function(e)
25418     {
25419         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25420             
25421             var minScale = this.thumbEl.getWidth() / this.minWidth;
25422         
25423             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25424             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25425             
25426             this.startScale = this.scale;
25427             
25428             while (this.getScaleLevel() < minScale){
25429             
25430                 this.scale = this.scale + 1;
25431                 
25432                 if(!this.zoomable()){
25433                     break;
25434                 }
25435                 
25436                 if(
25437                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25438                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25439                 ){
25440                     continue;
25441                 }
25442                 
25443                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25444
25445                 this.draw();
25446                 
25447                 return;
25448             }
25449             
25450             this.scale = this.startScale;
25451             
25452             this.onRotateFail();
25453             
25454             return false;
25455         }
25456         
25457         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25458
25459         if(this.isDocument){
25460             this.setThumbBoxSize();
25461             this.setThumbBoxPosition();
25462             this.setCanvasPosition();
25463         }
25464         
25465         this.draw();
25466         
25467         this.fireEvent('rotate', this, 'right');
25468     },
25469     
25470     onRotateFail : function()
25471     {
25472         this.errorEl.show(true);
25473         
25474         var _this = this;
25475         
25476         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25477     },
25478     
25479     draw : function()
25480     {
25481         this.previewEl.dom.innerHTML = '';
25482         
25483         var canvasEl = document.createElement("canvas");
25484         
25485         var contextEl = canvasEl.getContext("2d");
25486         
25487         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25488         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25489         var center = this.imageEl.OriginWidth / 2;
25490         
25491         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25492             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25493             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25494             center = this.imageEl.OriginHeight / 2;
25495         }
25496         
25497         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25498         
25499         contextEl.translate(center, center);
25500         contextEl.rotate(this.rotate * Math.PI / 180);
25501
25502         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25503         
25504         this.canvasEl = document.createElement("canvas");
25505         
25506         this.contextEl = this.canvasEl.getContext("2d");
25507         
25508         switch (this.rotate) {
25509             case 0 :
25510                 
25511                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25512                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25513                 
25514                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25515                 
25516                 break;
25517             case 90 : 
25518                 
25519                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25520                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25521                 
25522                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25523                     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);
25524                     break;
25525                 }
25526                 
25527                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25528                 
25529                 break;
25530             case 180 :
25531                 
25532                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25533                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25534                 
25535                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25536                     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);
25537                     break;
25538                 }
25539                 
25540                 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);
25541                 
25542                 break;
25543             case 270 :
25544                 
25545                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25546                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25547         
25548                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25549                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25550                     break;
25551                 }
25552                 
25553                 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);
25554                 
25555                 break;
25556             default : 
25557                 break;
25558         }
25559         
25560         this.previewEl.appendChild(this.canvasEl);
25561         
25562         this.setCanvasPosition();
25563     },
25564     
25565     crop : function()
25566     {
25567         if(!this.canvasLoaded){
25568             return;
25569         }
25570         
25571         var imageCanvas = document.createElement("canvas");
25572         
25573         var imageContext = imageCanvas.getContext("2d");
25574         
25575         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25576         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25577         
25578         var center = imageCanvas.width / 2;
25579         
25580         imageContext.translate(center, center);
25581         
25582         imageContext.rotate(this.rotate * Math.PI / 180);
25583         
25584         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25585         
25586         var canvas = document.createElement("canvas");
25587         
25588         var context = canvas.getContext("2d");
25589                 
25590         canvas.width = this.minWidth;
25591         canvas.height = this.minHeight;
25592
25593         switch (this.rotate) {
25594             case 0 :
25595                 
25596                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25597                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25598                 
25599                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25600                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25601                 
25602                 var targetWidth = this.minWidth - 2 * x;
25603                 var targetHeight = this.minHeight - 2 * y;
25604                 
25605                 var scale = 1;
25606                 
25607                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25608                     scale = targetWidth / width;
25609                 }
25610                 
25611                 if(x > 0 && y == 0){
25612                     scale = targetHeight / height;
25613                 }
25614                 
25615                 if(x > 0 && y > 0){
25616                     scale = targetWidth / width;
25617                     
25618                     if(width < height){
25619                         scale = targetHeight / height;
25620                     }
25621                 }
25622                 
25623                 context.scale(scale, scale);
25624                 
25625                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25626                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25627
25628                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25629                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25630
25631                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25632                 
25633                 break;
25634             case 90 : 
25635                 
25636                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25637                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25638                 
25639                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25640                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25641                 
25642                 var targetWidth = this.minWidth - 2 * x;
25643                 var targetHeight = this.minHeight - 2 * y;
25644                 
25645                 var scale = 1;
25646                 
25647                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25648                     scale = targetWidth / width;
25649                 }
25650                 
25651                 if(x > 0 && y == 0){
25652                     scale = targetHeight / height;
25653                 }
25654                 
25655                 if(x > 0 && y > 0){
25656                     scale = targetWidth / width;
25657                     
25658                     if(width < height){
25659                         scale = targetHeight / height;
25660                     }
25661                 }
25662                 
25663                 context.scale(scale, scale);
25664                 
25665                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25666                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25667
25668                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25669                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25670                 
25671                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25672                 
25673                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25674                 
25675                 break;
25676             case 180 :
25677                 
25678                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25679                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25680                 
25681                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25682                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25683                 
25684                 var targetWidth = this.minWidth - 2 * x;
25685                 var targetHeight = this.minHeight - 2 * y;
25686                 
25687                 var scale = 1;
25688                 
25689                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25690                     scale = targetWidth / width;
25691                 }
25692                 
25693                 if(x > 0 && y == 0){
25694                     scale = targetHeight / height;
25695                 }
25696                 
25697                 if(x > 0 && y > 0){
25698                     scale = targetWidth / width;
25699                     
25700                     if(width < height){
25701                         scale = targetHeight / height;
25702                     }
25703                 }
25704                 
25705                 context.scale(scale, scale);
25706                 
25707                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25708                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25709
25710                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25711                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25712
25713                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25714                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25715                 
25716                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25717                 
25718                 break;
25719             case 270 :
25720                 
25721                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25722                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25723                 
25724                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25725                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25726                 
25727                 var targetWidth = this.minWidth - 2 * x;
25728                 var targetHeight = this.minHeight - 2 * y;
25729                 
25730                 var scale = 1;
25731                 
25732                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25733                     scale = targetWidth / width;
25734                 }
25735                 
25736                 if(x > 0 && y == 0){
25737                     scale = targetHeight / height;
25738                 }
25739                 
25740                 if(x > 0 && y > 0){
25741                     scale = targetWidth / width;
25742                     
25743                     if(width < height){
25744                         scale = targetHeight / height;
25745                     }
25746                 }
25747                 
25748                 context.scale(scale, scale);
25749                 
25750                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25751                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25752
25753                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25754                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25755                 
25756                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25757                 
25758                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25759                 
25760                 break;
25761             default : 
25762                 break;
25763         }
25764         
25765         this.cropData = canvas.toDataURL(this.cropType);
25766         
25767         if(this.fireEvent('crop', this, this.cropData) !== false){
25768             this.process(this.file, this.cropData);
25769         }
25770         
25771         return;
25772         
25773     },
25774     
25775     setThumbBoxSize : function()
25776     {
25777         var width, height;
25778         
25779         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25780             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25781             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25782             
25783             this.minWidth = width;
25784             this.minHeight = height;
25785             
25786             if(this.rotate == 90 || this.rotate == 270){
25787                 this.minWidth = height;
25788                 this.minHeight = width;
25789             }
25790         }
25791         
25792         height = 300;
25793         width = Math.ceil(this.minWidth * height / this.minHeight);
25794         
25795         if(this.minWidth > this.minHeight){
25796             width = 300;
25797             height = Math.ceil(this.minHeight * width / this.minWidth);
25798         }
25799         
25800         this.thumbEl.setStyle({
25801             width : width + 'px',
25802             height : height + 'px'
25803         });
25804
25805         return;
25806             
25807     },
25808     
25809     setThumbBoxPosition : function()
25810     {
25811         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25812         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25813         
25814         this.thumbEl.setLeft(x);
25815         this.thumbEl.setTop(y);
25816         
25817     },
25818     
25819     baseRotateLevel : function()
25820     {
25821         this.baseRotate = 1;
25822         
25823         if(
25824                 typeof(this.exif) != 'undefined' &&
25825                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25826                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25827         ){
25828             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25829         }
25830         
25831         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25832         
25833     },
25834     
25835     baseScaleLevel : function()
25836     {
25837         var width, height;
25838         
25839         if(this.isDocument){
25840             
25841             if(this.baseRotate == 6 || this.baseRotate == 8){
25842             
25843                 height = this.thumbEl.getHeight();
25844                 this.baseScale = height / this.imageEl.OriginWidth;
25845
25846                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25847                     width = this.thumbEl.getWidth();
25848                     this.baseScale = width / this.imageEl.OriginHeight;
25849                 }
25850
25851                 return;
25852             }
25853
25854             height = this.thumbEl.getHeight();
25855             this.baseScale = height / this.imageEl.OriginHeight;
25856
25857             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25858                 width = this.thumbEl.getWidth();
25859                 this.baseScale = width / this.imageEl.OriginWidth;
25860             }
25861
25862             return;
25863         }
25864         
25865         if(this.baseRotate == 6 || this.baseRotate == 8){
25866             
25867             width = this.thumbEl.getHeight();
25868             this.baseScale = width / this.imageEl.OriginHeight;
25869             
25870             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25871                 height = this.thumbEl.getWidth();
25872                 this.baseScale = height / this.imageEl.OriginHeight;
25873             }
25874             
25875             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25876                 height = this.thumbEl.getWidth();
25877                 this.baseScale = height / this.imageEl.OriginHeight;
25878                 
25879                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25880                     width = this.thumbEl.getHeight();
25881                     this.baseScale = width / this.imageEl.OriginWidth;
25882                 }
25883             }
25884             
25885             return;
25886         }
25887         
25888         width = this.thumbEl.getWidth();
25889         this.baseScale = width / this.imageEl.OriginWidth;
25890         
25891         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25892             height = this.thumbEl.getHeight();
25893             this.baseScale = height / this.imageEl.OriginHeight;
25894         }
25895         
25896         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25897             
25898             height = this.thumbEl.getHeight();
25899             this.baseScale = height / this.imageEl.OriginHeight;
25900             
25901             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25902                 width = this.thumbEl.getWidth();
25903                 this.baseScale = width / this.imageEl.OriginWidth;
25904             }
25905             
25906         }
25907         
25908         return;
25909     },
25910     
25911     getScaleLevel : function()
25912     {
25913         return this.baseScale * Math.pow(1.1, this.scale);
25914     },
25915     
25916     onTouchStart : function(e)
25917     {
25918         if(!this.canvasLoaded){
25919             this.beforeSelectFile(e);
25920             return;
25921         }
25922         
25923         var touches = e.browserEvent.touches;
25924         
25925         if(!touches){
25926             return;
25927         }
25928         
25929         if(touches.length == 1){
25930             this.onMouseDown(e);
25931             return;
25932         }
25933         
25934         if(touches.length != 2){
25935             return;
25936         }
25937         
25938         var coords = [];
25939         
25940         for(var i = 0, finger; finger = touches[i]; i++){
25941             coords.push(finger.pageX, finger.pageY);
25942         }
25943         
25944         var x = Math.pow(coords[0] - coords[2], 2);
25945         var y = Math.pow(coords[1] - coords[3], 2);
25946         
25947         this.startDistance = Math.sqrt(x + y);
25948         
25949         this.startScale = this.scale;
25950         
25951         this.pinching = true;
25952         this.dragable = false;
25953         
25954     },
25955     
25956     onTouchMove : function(e)
25957     {
25958         if(!this.pinching && !this.dragable){
25959             return;
25960         }
25961         
25962         var touches = e.browserEvent.touches;
25963         
25964         if(!touches){
25965             return;
25966         }
25967         
25968         if(this.dragable){
25969             this.onMouseMove(e);
25970             return;
25971         }
25972         
25973         var coords = [];
25974         
25975         for(var i = 0, finger; finger = touches[i]; i++){
25976             coords.push(finger.pageX, finger.pageY);
25977         }
25978         
25979         var x = Math.pow(coords[0] - coords[2], 2);
25980         var y = Math.pow(coords[1] - coords[3], 2);
25981         
25982         this.endDistance = Math.sqrt(x + y);
25983         
25984         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25985         
25986         if(!this.zoomable()){
25987             this.scale = this.startScale;
25988             return;
25989         }
25990         
25991         this.draw();
25992         
25993     },
25994     
25995     onTouchEnd : function(e)
25996     {
25997         this.pinching = false;
25998         this.dragable = false;
25999         
26000     },
26001     
26002     process : function(file, crop)
26003     {
26004         if(this.loadMask){
26005             this.maskEl.mask(this.loadingText);
26006         }
26007         
26008         this.xhr = new XMLHttpRequest();
26009         
26010         file.xhr = this.xhr;
26011
26012         this.xhr.open(this.method, this.url, true);
26013         
26014         var headers = {
26015             "Accept": "application/json",
26016             "Cache-Control": "no-cache",
26017             "X-Requested-With": "XMLHttpRequest"
26018         };
26019         
26020         for (var headerName in headers) {
26021             var headerValue = headers[headerName];
26022             if (headerValue) {
26023                 this.xhr.setRequestHeader(headerName, headerValue);
26024             }
26025         }
26026         
26027         var _this = this;
26028         
26029         this.xhr.onload = function()
26030         {
26031             _this.xhrOnLoad(_this.xhr);
26032         }
26033         
26034         this.xhr.onerror = function()
26035         {
26036             _this.xhrOnError(_this.xhr);
26037         }
26038         
26039         var formData = new FormData();
26040
26041         formData.append('returnHTML', 'NO');
26042         
26043         if(crop){
26044             formData.append('crop', crop);
26045         }
26046         
26047         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26048             formData.append(this.paramName, file, file.name);
26049         }
26050         
26051         if(typeof(file.filename) != 'undefined'){
26052             formData.append('filename', file.filename);
26053         }
26054         
26055         if(typeof(file.mimetype) != 'undefined'){
26056             formData.append('mimetype', file.mimetype);
26057         }
26058         
26059         if(this.fireEvent('arrange', this, formData) != false){
26060             this.xhr.send(formData);
26061         };
26062     },
26063     
26064     xhrOnLoad : function(xhr)
26065     {
26066         if(this.loadMask){
26067             this.maskEl.unmask();
26068         }
26069         
26070         if (xhr.readyState !== 4) {
26071             this.fireEvent('exception', this, xhr);
26072             return;
26073         }
26074
26075         var response = Roo.decode(xhr.responseText);
26076         
26077         if(!response.success){
26078             this.fireEvent('exception', this, xhr);
26079             return;
26080         }
26081         
26082         var response = Roo.decode(xhr.responseText);
26083         
26084         this.fireEvent('upload', this, response);
26085         
26086     },
26087     
26088     xhrOnError : function()
26089     {
26090         if(this.loadMask){
26091             this.maskEl.unmask();
26092         }
26093         
26094         Roo.log('xhr on error');
26095         
26096         var response = Roo.decode(xhr.responseText);
26097           
26098         Roo.log(response);
26099         
26100     },
26101     
26102     prepare : function(file)
26103     {   
26104         if(this.loadMask){
26105             this.maskEl.mask(this.loadingText);
26106         }
26107         
26108         this.file = false;
26109         this.exif = {};
26110         
26111         if(typeof(file) === 'string'){
26112             this.loadCanvas(file);
26113             return;
26114         }
26115         
26116         if(!file || !this.urlAPI){
26117             return;
26118         }
26119         
26120         this.file = file;
26121         this.cropType = file.type;
26122         
26123         var _this = this;
26124         
26125         if(this.fireEvent('prepare', this, this.file) != false){
26126             
26127             var reader = new FileReader();
26128             
26129             reader.onload = function (e) {
26130                 if (e.target.error) {
26131                     Roo.log(e.target.error);
26132                     return;
26133                 }
26134                 
26135                 var buffer = e.target.result,
26136                     dataView = new DataView(buffer),
26137                     offset = 2,
26138                     maxOffset = dataView.byteLength - 4,
26139                     markerBytes,
26140                     markerLength;
26141                 
26142                 if (dataView.getUint16(0) === 0xffd8) {
26143                     while (offset < maxOffset) {
26144                         markerBytes = dataView.getUint16(offset);
26145                         
26146                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26147                             markerLength = dataView.getUint16(offset + 2) + 2;
26148                             if (offset + markerLength > dataView.byteLength) {
26149                                 Roo.log('Invalid meta data: Invalid segment size.');
26150                                 break;
26151                             }
26152                             
26153                             if(markerBytes == 0xffe1){
26154                                 _this.parseExifData(
26155                                     dataView,
26156                                     offset,
26157                                     markerLength
26158                                 );
26159                             }
26160                             
26161                             offset += markerLength;
26162                             
26163                             continue;
26164                         }
26165                         
26166                         break;
26167                     }
26168                     
26169                 }
26170                 
26171                 var url = _this.urlAPI.createObjectURL(_this.file);
26172                 
26173                 _this.loadCanvas(url);
26174                 
26175                 return;
26176             }
26177             
26178             reader.readAsArrayBuffer(this.file);
26179             
26180         }
26181         
26182     },
26183     
26184     parseExifData : function(dataView, offset, length)
26185     {
26186         var tiffOffset = offset + 10,
26187             littleEndian,
26188             dirOffset;
26189     
26190         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26191             // No Exif data, might be XMP data instead
26192             return;
26193         }
26194         
26195         // Check for the ASCII code for "Exif" (0x45786966):
26196         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26197             // No Exif data, might be XMP data instead
26198             return;
26199         }
26200         if (tiffOffset + 8 > dataView.byteLength) {
26201             Roo.log('Invalid Exif data: Invalid segment size.');
26202             return;
26203         }
26204         // Check for the two null bytes:
26205         if (dataView.getUint16(offset + 8) !== 0x0000) {
26206             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26207             return;
26208         }
26209         // Check the byte alignment:
26210         switch (dataView.getUint16(tiffOffset)) {
26211         case 0x4949:
26212             littleEndian = true;
26213             break;
26214         case 0x4D4D:
26215             littleEndian = false;
26216             break;
26217         default:
26218             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26219             return;
26220         }
26221         // Check for the TIFF tag marker (0x002A):
26222         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26223             Roo.log('Invalid Exif data: Missing TIFF marker.');
26224             return;
26225         }
26226         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26227         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26228         
26229         this.parseExifTags(
26230             dataView,
26231             tiffOffset,
26232             tiffOffset + dirOffset,
26233             littleEndian
26234         );
26235     },
26236     
26237     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26238     {
26239         var tagsNumber,
26240             dirEndOffset,
26241             i;
26242         if (dirOffset + 6 > dataView.byteLength) {
26243             Roo.log('Invalid Exif data: Invalid directory offset.');
26244             return;
26245         }
26246         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26247         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26248         if (dirEndOffset + 4 > dataView.byteLength) {
26249             Roo.log('Invalid Exif data: Invalid directory size.');
26250             return;
26251         }
26252         for (i = 0; i < tagsNumber; i += 1) {
26253             this.parseExifTag(
26254                 dataView,
26255                 tiffOffset,
26256                 dirOffset + 2 + 12 * i, // tag offset
26257                 littleEndian
26258             );
26259         }
26260         // Return the offset to the next directory:
26261         return dataView.getUint32(dirEndOffset, littleEndian);
26262     },
26263     
26264     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26265     {
26266         var tag = dataView.getUint16(offset, littleEndian);
26267         
26268         this.exif[tag] = this.getExifValue(
26269             dataView,
26270             tiffOffset,
26271             offset,
26272             dataView.getUint16(offset + 2, littleEndian), // tag type
26273             dataView.getUint32(offset + 4, littleEndian), // tag length
26274             littleEndian
26275         );
26276     },
26277     
26278     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26279     {
26280         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26281             tagSize,
26282             dataOffset,
26283             values,
26284             i,
26285             str,
26286             c;
26287     
26288         if (!tagType) {
26289             Roo.log('Invalid Exif data: Invalid tag type.');
26290             return;
26291         }
26292         
26293         tagSize = tagType.size * length;
26294         // Determine if the value is contained in the dataOffset bytes,
26295         // or if the value at the dataOffset is a pointer to the actual data:
26296         dataOffset = tagSize > 4 ?
26297                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26298         if (dataOffset + tagSize > dataView.byteLength) {
26299             Roo.log('Invalid Exif data: Invalid data offset.');
26300             return;
26301         }
26302         if (length === 1) {
26303             return tagType.getValue(dataView, dataOffset, littleEndian);
26304         }
26305         values = [];
26306         for (i = 0; i < length; i += 1) {
26307             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26308         }
26309         
26310         if (tagType.ascii) {
26311             str = '';
26312             // Concatenate the chars:
26313             for (i = 0; i < values.length; i += 1) {
26314                 c = values[i];
26315                 // Ignore the terminating NULL byte(s):
26316                 if (c === '\u0000') {
26317                     break;
26318                 }
26319                 str += c;
26320             }
26321             return str;
26322         }
26323         return values;
26324     }
26325     
26326 });
26327
26328 Roo.apply(Roo.bootstrap.UploadCropbox, {
26329     tags : {
26330         'Orientation': 0x0112
26331     },
26332     
26333     Orientation: {
26334             1: 0, //'top-left',
26335 //            2: 'top-right',
26336             3: 180, //'bottom-right',
26337 //            4: 'bottom-left',
26338 //            5: 'left-top',
26339             6: 90, //'right-top',
26340 //            7: 'right-bottom',
26341             8: 270 //'left-bottom'
26342     },
26343     
26344     exifTagTypes : {
26345         // byte, 8-bit unsigned int:
26346         1: {
26347             getValue: function (dataView, dataOffset) {
26348                 return dataView.getUint8(dataOffset);
26349             },
26350             size: 1
26351         },
26352         // ascii, 8-bit byte:
26353         2: {
26354             getValue: function (dataView, dataOffset) {
26355                 return String.fromCharCode(dataView.getUint8(dataOffset));
26356             },
26357             size: 1,
26358             ascii: true
26359         },
26360         // short, 16 bit int:
26361         3: {
26362             getValue: function (dataView, dataOffset, littleEndian) {
26363                 return dataView.getUint16(dataOffset, littleEndian);
26364             },
26365             size: 2
26366         },
26367         // long, 32 bit int:
26368         4: {
26369             getValue: function (dataView, dataOffset, littleEndian) {
26370                 return dataView.getUint32(dataOffset, littleEndian);
26371             },
26372             size: 4
26373         },
26374         // rational = two long values, first is numerator, second is denominator:
26375         5: {
26376             getValue: function (dataView, dataOffset, littleEndian) {
26377                 return dataView.getUint32(dataOffset, littleEndian) /
26378                     dataView.getUint32(dataOffset + 4, littleEndian);
26379             },
26380             size: 8
26381         },
26382         // slong, 32 bit signed int:
26383         9: {
26384             getValue: function (dataView, dataOffset, littleEndian) {
26385                 return dataView.getInt32(dataOffset, littleEndian);
26386             },
26387             size: 4
26388         },
26389         // srational, two slongs, first is numerator, second is denominator:
26390         10: {
26391             getValue: function (dataView, dataOffset, littleEndian) {
26392                 return dataView.getInt32(dataOffset, littleEndian) /
26393                     dataView.getInt32(dataOffset + 4, littleEndian);
26394             },
26395             size: 8
26396         }
26397     },
26398     
26399     footer : {
26400         STANDARD : [
26401             {
26402                 tag : 'div',
26403                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26404                 action : 'rotate-left',
26405                 cn : [
26406                     {
26407                         tag : 'button',
26408                         cls : 'btn btn-default',
26409                         html : '<i class="fa fa-undo"></i>'
26410                     }
26411                 ]
26412             },
26413             {
26414                 tag : 'div',
26415                 cls : 'btn-group roo-upload-cropbox-picture',
26416                 action : 'picture',
26417                 cn : [
26418                     {
26419                         tag : 'button',
26420                         cls : 'btn btn-default',
26421                         html : '<i class="fa fa-picture-o"></i>'
26422                     }
26423                 ]
26424             },
26425             {
26426                 tag : 'div',
26427                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26428                 action : 'rotate-right',
26429                 cn : [
26430                     {
26431                         tag : 'button',
26432                         cls : 'btn btn-default',
26433                         html : '<i class="fa fa-repeat"></i>'
26434                     }
26435                 ]
26436             }
26437         ],
26438         DOCUMENT : [
26439             {
26440                 tag : 'div',
26441                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26442                 action : 'rotate-left',
26443                 cn : [
26444                     {
26445                         tag : 'button',
26446                         cls : 'btn btn-default',
26447                         html : '<i class="fa fa-undo"></i>'
26448                     }
26449                 ]
26450             },
26451             {
26452                 tag : 'div',
26453                 cls : 'btn-group roo-upload-cropbox-download',
26454                 action : 'download',
26455                 cn : [
26456                     {
26457                         tag : 'button',
26458                         cls : 'btn btn-default',
26459                         html : '<i class="fa fa-download"></i>'
26460                     }
26461                 ]
26462             },
26463             {
26464                 tag : 'div',
26465                 cls : 'btn-group roo-upload-cropbox-crop',
26466                 action : 'crop',
26467                 cn : [
26468                     {
26469                         tag : 'button',
26470                         cls : 'btn btn-default',
26471                         html : '<i class="fa fa-crop"></i>'
26472                     }
26473                 ]
26474             },
26475             {
26476                 tag : 'div',
26477                 cls : 'btn-group roo-upload-cropbox-trash',
26478                 action : 'trash',
26479                 cn : [
26480                     {
26481                         tag : 'button',
26482                         cls : 'btn btn-default',
26483                         html : '<i class="fa fa-trash"></i>'
26484                     }
26485                 ]
26486             },
26487             {
26488                 tag : 'div',
26489                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26490                 action : 'rotate-right',
26491                 cn : [
26492                     {
26493                         tag : 'button',
26494                         cls : 'btn btn-default',
26495                         html : '<i class="fa fa-repeat"></i>'
26496                     }
26497                 ]
26498             }
26499         ],
26500         ROTATOR : [
26501             {
26502                 tag : 'div',
26503                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26504                 action : 'rotate-left',
26505                 cn : [
26506                     {
26507                         tag : 'button',
26508                         cls : 'btn btn-default',
26509                         html : '<i class="fa fa-undo"></i>'
26510                     }
26511                 ]
26512             },
26513             {
26514                 tag : 'div',
26515                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26516                 action : 'rotate-right',
26517                 cn : [
26518                     {
26519                         tag : 'button',
26520                         cls : 'btn btn-default',
26521                         html : '<i class="fa fa-repeat"></i>'
26522                     }
26523                 ]
26524             }
26525         ]
26526     }
26527 });
26528
26529 /*
26530 * Licence: LGPL
26531 */
26532
26533 /**
26534  * @class Roo.bootstrap.DocumentManager
26535  * @extends Roo.bootstrap.Component
26536  * Bootstrap DocumentManager class
26537  * @cfg {String} paramName default 'imageUpload'
26538  * @cfg {String} method default POST
26539  * @cfg {String} url action url
26540  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26541  * @cfg {Boolean} multiple multiple upload default true
26542  * @cfg {Number} thumbSize default 300
26543  * @cfg {String} fieldLabel
26544  * @cfg {Number} labelWidth default 4
26545  * @cfg {String} labelAlign (left|top) default left
26546  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26547  * 
26548  * @constructor
26549  * Create a new DocumentManager
26550  * @param {Object} config The config object
26551  */
26552
26553 Roo.bootstrap.DocumentManager = function(config){
26554     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26555     
26556     this.addEvents({
26557         /**
26558          * @event initial
26559          * Fire when initial the DocumentManager
26560          * @param {Roo.bootstrap.DocumentManager} this
26561          */
26562         "initial" : true,
26563         /**
26564          * @event inspect
26565          * inspect selected file
26566          * @param {Roo.bootstrap.DocumentManager} this
26567          * @param {File} file
26568          */
26569         "inspect" : true,
26570         /**
26571          * @event exception
26572          * Fire when xhr load exception
26573          * @param {Roo.bootstrap.DocumentManager} this
26574          * @param {XMLHttpRequest} xhr
26575          */
26576         "exception" : true,
26577         /**
26578          * @event prepare
26579          * prepare the form data
26580          * @param {Roo.bootstrap.DocumentManager} this
26581          * @param {Object} formData
26582          */
26583         "prepare" : true,
26584         /**
26585          * @event remove
26586          * Fire when remove the file
26587          * @param {Roo.bootstrap.DocumentManager} this
26588          * @param {Object} file
26589          */
26590         "remove" : true,
26591         /**
26592          * @event refresh
26593          * Fire after refresh the file
26594          * @param {Roo.bootstrap.DocumentManager} this
26595          */
26596         "refresh" : true,
26597         /**
26598          * @event click
26599          * Fire after click the image
26600          * @param {Roo.bootstrap.DocumentManager} this
26601          * @param {Object} file
26602          */
26603         "click" : true,
26604         /**
26605          * @event edit
26606          * Fire when upload a image and editable set to true
26607          * @param {Roo.bootstrap.DocumentManager} this
26608          * @param {Object} file
26609          */
26610         "edit" : true,
26611         /**
26612          * @event beforeselectfile
26613          * Fire before select file
26614          * @param {Roo.bootstrap.DocumentManager} this
26615          */
26616         "beforeselectfile" : true,
26617         /**
26618          * @event process
26619          * Fire before process file
26620          * @param {Roo.bootstrap.DocumentManager} this
26621          * @param {Object} file
26622          */
26623         "process" : true
26624         
26625     });
26626 };
26627
26628 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26629     
26630     boxes : 0,
26631     inputName : '',
26632     thumbSize : 300,
26633     multiple : true,
26634     files : [],
26635     method : 'POST',
26636     url : '',
26637     paramName : 'imageUpload',
26638     fieldLabel : '',
26639     labelWidth : 4,
26640     labelAlign : 'left',
26641     editable : true,
26642     delegates : [],
26643     
26644     
26645     xhr : false, 
26646     
26647     getAutoCreate : function()
26648     {   
26649         var managerWidget = {
26650             tag : 'div',
26651             cls : 'roo-document-manager',
26652             cn : [
26653                 {
26654                     tag : 'input',
26655                     cls : 'roo-document-manager-selector',
26656                     type : 'file'
26657                 },
26658                 {
26659                     tag : 'div',
26660                     cls : 'roo-document-manager-uploader',
26661                     cn : [
26662                         {
26663                             tag : 'div',
26664                             cls : 'roo-document-manager-upload-btn',
26665                             html : '<i class="fa fa-plus"></i>'
26666                         }
26667                     ]
26668                     
26669                 }
26670             ]
26671         };
26672         
26673         var content = [
26674             {
26675                 tag : 'div',
26676                 cls : 'column col-md-12',
26677                 cn : managerWidget
26678             }
26679         ];
26680         
26681         if(this.fieldLabel.length){
26682             
26683             content = [
26684                 {
26685                     tag : 'div',
26686                     cls : 'column col-md-12',
26687                     html : this.fieldLabel
26688                 },
26689                 {
26690                     tag : 'div',
26691                     cls : 'column col-md-12',
26692                     cn : managerWidget
26693                 }
26694             ];
26695
26696             if(this.labelAlign == 'left'){
26697                 content = [
26698                     {
26699                         tag : 'div',
26700                         cls : 'column col-md-' + this.labelWidth,
26701                         html : this.fieldLabel
26702                     },
26703                     {
26704                         tag : 'div',
26705                         cls : 'column col-md-' + (12 - this.labelWidth),
26706                         cn : managerWidget
26707                     }
26708                 ];
26709                 
26710             }
26711         }
26712         
26713         var cfg = {
26714             tag : 'div',
26715             cls : 'row clearfix',
26716             cn : content
26717         };
26718         
26719         return cfg;
26720         
26721     },
26722     
26723     initEvents : function()
26724     {
26725         this.managerEl = this.el.select('.roo-document-manager', true).first();
26726         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26727         
26728         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26729         this.selectorEl.hide();
26730         
26731         if(this.multiple){
26732             this.selectorEl.attr('multiple', 'multiple');
26733         }
26734         
26735         this.selectorEl.on('change', this.onFileSelected, this);
26736         
26737         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26738         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26739         
26740         this.uploader.on('click', this.onUploaderClick, this);
26741         
26742         this.renderProgressDialog();
26743         
26744         var _this = this;
26745         
26746         window.addEventListener("resize", function() { _this.refresh(); } );
26747         
26748         this.fireEvent('initial', this);
26749     },
26750     
26751     renderProgressDialog : function()
26752     {
26753         var _this = this;
26754         
26755         this.progressDialog = new Roo.bootstrap.Modal({
26756             cls : 'roo-document-manager-progress-dialog',
26757             allow_close : false,
26758             title : '',
26759             buttons : [
26760                 {
26761                     name  :'cancel',
26762                     weight : 'danger',
26763                     html : 'Cancel'
26764                 }
26765             ], 
26766             listeners : { 
26767                 btnclick : function() {
26768                     _this.uploadCancel();
26769                     this.hide();
26770                 }
26771             }
26772         });
26773          
26774         this.progressDialog.render(Roo.get(document.body));
26775          
26776         this.progress = new Roo.bootstrap.Progress({
26777             cls : 'roo-document-manager-progress',
26778             active : true,
26779             striped : true
26780         });
26781         
26782         this.progress.render(this.progressDialog.getChildContainer());
26783         
26784         this.progressBar = new Roo.bootstrap.ProgressBar({
26785             cls : 'roo-document-manager-progress-bar',
26786             aria_valuenow : 0,
26787             aria_valuemin : 0,
26788             aria_valuemax : 12,
26789             panel : 'success'
26790         });
26791         
26792         this.progressBar.render(this.progress.getChildContainer());
26793     },
26794     
26795     onUploaderClick : function(e)
26796     {
26797         e.preventDefault();
26798      
26799         if(this.fireEvent('beforeselectfile', this) != false){
26800             this.selectorEl.dom.click();
26801         }
26802         
26803     },
26804     
26805     onFileSelected : function(e)
26806     {
26807         e.preventDefault();
26808         
26809         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26810             return;
26811         }
26812         
26813         Roo.each(this.selectorEl.dom.files, function(file){
26814             if(this.fireEvent('inspect', this, file) != false){
26815                 this.files.push(file);
26816             }
26817         }, this);
26818         
26819         this.queue();
26820         
26821     },
26822     
26823     queue : function()
26824     {
26825         this.selectorEl.dom.value = '';
26826         
26827         if(!this.files.length){
26828             return;
26829         }
26830         
26831         if(this.boxes > 0 && this.files.length > this.boxes){
26832             this.files = this.files.slice(0, this.boxes);
26833         }
26834         
26835         this.uploader.show();
26836         
26837         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26838             this.uploader.hide();
26839         }
26840         
26841         var _this = this;
26842         
26843         var files = [];
26844         
26845         var docs = [];
26846         
26847         Roo.each(this.files, function(file){
26848             
26849             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26850                 var f = this.renderPreview(file);
26851                 files.push(f);
26852                 return;
26853             }
26854             
26855             if(file.type.indexOf('image') != -1){
26856                 this.delegates.push(
26857                     (function(){
26858                         _this.process(file);
26859                     }).createDelegate(this)
26860                 );
26861         
26862                 return;
26863             }
26864             
26865             docs.push(
26866                 (function(){
26867                     _this.process(file);
26868                 }).createDelegate(this)
26869             );
26870             
26871         }, this);
26872         
26873         this.files = files;
26874         
26875         this.delegates = this.delegates.concat(docs);
26876         
26877         if(!this.delegates.length){
26878             this.refresh();
26879             return;
26880         }
26881         
26882         this.progressBar.aria_valuemax = this.delegates.length;
26883         
26884         this.arrange();
26885         
26886         return;
26887     },
26888     
26889     arrange : function()
26890     {
26891         if(!this.delegates.length){
26892             this.progressDialog.hide();
26893             this.refresh();
26894             return;
26895         }
26896         
26897         var delegate = this.delegates.shift();
26898         
26899         this.progressDialog.show();
26900         
26901         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26902         
26903         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26904         
26905         delegate();
26906     },
26907     
26908     refresh : function()
26909     {
26910         this.uploader.show();
26911         
26912         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26913             this.uploader.hide();
26914         }
26915         
26916         Roo.isTouch ? this.closable(false) : this.closable(true);
26917         
26918         this.fireEvent('refresh', this);
26919     },
26920     
26921     onRemove : function(e, el, o)
26922     {
26923         e.preventDefault();
26924         
26925         this.fireEvent('remove', this, o);
26926         
26927     },
26928     
26929     remove : function(o)
26930     {
26931         var files = [];
26932         
26933         Roo.each(this.files, function(file){
26934             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26935                 files.push(file);
26936                 return;
26937             }
26938
26939             o.target.remove();
26940
26941         }, this);
26942         
26943         this.files = files;
26944         
26945         this.refresh();
26946     },
26947     
26948     clear : function()
26949     {
26950         Roo.each(this.files, function(file){
26951             if(!file.target){
26952                 return;
26953             }
26954             
26955             file.target.remove();
26956
26957         }, this);
26958         
26959         this.files = [];
26960         
26961         this.refresh();
26962     },
26963     
26964     onClick : function(e, el, o)
26965     {
26966         e.preventDefault();
26967         
26968         this.fireEvent('click', this, o);
26969         
26970     },
26971     
26972     closable : function(closable)
26973     {
26974         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26975             
26976             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26977             
26978             if(closable){
26979                 el.show();
26980                 return;
26981             }
26982             
26983             el.hide();
26984             
26985         }, this);
26986     },
26987     
26988     xhrOnLoad : function(xhr)
26989     {
26990         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26991             el.remove();
26992         }, this);
26993         
26994         if (xhr.readyState !== 4) {
26995             this.arrange();
26996             this.fireEvent('exception', this, xhr);
26997             return;
26998         }
26999
27000         var response = Roo.decode(xhr.responseText);
27001         
27002         if(!response.success){
27003             this.arrange();
27004             this.fireEvent('exception', this, xhr);
27005             return;
27006         }
27007         
27008         var file = this.renderPreview(response.data);
27009         
27010         this.files.push(file);
27011         
27012         this.arrange();
27013         
27014     },
27015     
27016     xhrOnError : function(xhr)
27017     {
27018         Roo.log('xhr on error');
27019         
27020         var response = Roo.decode(xhr.responseText);
27021           
27022         Roo.log(response);
27023         
27024         this.arrange();
27025     },
27026     
27027     process : function(file)
27028     {
27029         if(this.fireEvent('process', this, file) !== false){
27030             if(this.editable && file.type.indexOf('image') != -1){
27031                 this.fireEvent('edit', this, file);
27032                 return;
27033             }
27034
27035             this.uploadStart(file, false);
27036
27037             return;
27038         }
27039         
27040     },
27041     
27042     uploadStart : function(file, crop)
27043     {
27044         this.xhr = new XMLHttpRequest();
27045         
27046         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27047             this.arrange();
27048             return;
27049         }
27050         
27051         file.xhr = this.xhr;
27052             
27053         this.managerEl.createChild({
27054             tag : 'div',
27055             cls : 'roo-document-manager-loading',
27056             cn : [
27057                 {
27058                     tag : 'div',
27059                     tooltip : file.name,
27060                     cls : 'roo-document-manager-thumb',
27061                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27062                 }
27063             ]
27064
27065         });
27066
27067         this.xhr.open(this.method, this.url, true);
27068         
27069         var headers = {
27070             "Accept": "application/json",
27071             "Cache-Control": "no-cache",
27072             "X-Requested-With": "XMLHttpRequest"
27073         };
27074         
27075         for (var headerName in headers) {
27076             var headerValue = headers[headerName];
27077             if (headerValue) {
27078                 this.xhr.setRequestHeader(headerName, headerValue);
27079             }
27080         }
27081         
27082         var _this = this;
27083         
27084         this.xhr.onload = function()
27085         {
27086             _this.xhrOnLoad(_this.xhr);
27087         }
27088         
27089         this.xhr.onerror = function()
27090         {
27091             _this.xhrOnError(_this.xhr);
27092         }
27093         
27094         var formData = new FormData();
27095
27096         formData.append('returnHTML', 'NO');
27097         
27098         if(crop){
27099             formData.append('crop', crop);
27100         }
27101         
27102         formData.append(this.paramName, file, file.name);
27103         
27104         if(this.fireEvent('prepare', this, formData) != false){
27105             this.xhr.send(formData);
27106         };
27107     },
27108     
27109     uploadCancel : function()
27110     {
27111         if (this.xhr) {
27112             this.xhr.abort();
27113         }
27114         
27115         
27116         this.delegates = [];
27117         
27118         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27119             el.remove();
27120         }, this);
27121         
27122         this.arrange();
27123     },
27124     
27125     renderPreview : function(file)
27126     {
27127         if(typeof(file.target) != 'undefined' && file.target){
27128             return file;
27129         }
27130         
27131         var previewEl = this.managerEl.createChild({
27132             tag : 'div',
27133             cls : 'roo-document-manager-preview',
27134             cn : [
27135                 {
27136                     tag : 'div',
27137                     tooltip : file.filename,
27138                     cls : 'roo-document-manager-thumb',
27139                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27140                 },
27141                 {
27142                     tag : 'button',
27143                     cls : 'close',
27144                     html : '<i class="fa fa-times-circle"></i>'
27145                 }
27146             ]
27147         });
27148
27149         var close = previewEl.select('button.close', true).first();
27150
27151         close.on('click', this.onRemove, this, file);
27152
27153         file.target = previewEl;
27154
27155         var image = previewEl.select('img', true).first();
27156         
27157         var _this = this;
27158         
27159         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27160         
27161         image.on('click', this.onClick, this, file);
27162         
27163         return file;
27164         
27165     },
27166     
27167     onPreviewLoad : function(file, image)
27168     {
27169         if(typeof(file.target) == 'undefined' || !file.target){
27170             return;
27171         }
27172         
27173         var width = image.dom.naturalWidth || image.dom.width;
27174         var height = image.dom.naturalHeight || image.dom.height;
27175         
27176         if(width > height){
27177             file.target.addClass('wide');
27178             return;
27179         }
27180         
27181         file.target.addClass('tall');
27182         return;
27183         
27184     },
27185     
27186     uploadFromSource : function(file, crop)
27187     {
27188         this.xhr = new XMLHttpRequest();
27189         
27190         this.managerEl.createChild({
27191             tag : 'div',
27192             cls : 'roo-document-manager-loading',
27193             cn : [
27194                 {
27195                     tag : 'div',
27196                     tooltip : file.name,
27197                     cls : 'roo-document-manager-thumb',
27198                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27199                 }
27200             ]
27201
27202         });
27203
27204         this.xhr.open(this.method, this.url, true);
27205         
27206         var headers = {
27207             "Accept": "application/json",
27208             "Cache-Control": "no-cache",
27209             "X-Requested-With": "XMLHttpRequest"
27210         };
27211         
27212         for (var headerName in headers) {
27213             var headerValue = headers[headerName];
27214             if (headerValue) {
27215                 this.xhr.setRequestHeader(headerName, headerValue);
27216             }
27217         }
27218         
27219         var _this = this;
27220         
27221         this.xhr.onload = function()
27222         {
27223             _this.xhrOnLoad(_this.xhr);
27224         }
27225         
27226         this.xhr.onerror = function()
27227         {
27228             _this.xhrOnError(_this.xhr);
27229         }
27230         
27231         var formData = new FormData();
27232
27233         formData.append('returnHTML', 'NO');
27234         
27235         formData.append('crop', crop);
27236         
27237         if(typeof(file.filename) != 'undefined'){
27238             formData.append('filename', file.filename);
27239         }
27240         
27241         if(typeof(file.mimetype) != 'undefined'){
27242             formData.append('mimetype', file.mimetype);
27243         }
27244         
27245         if(this.fireEvent('prepare', this, formData) != false){
27246             this.xhr.send(formData);
27247         };
27248     }
27249 });
27250
27251 /*
27252 * Licence: LGPL
27253 */
27254
27255 /**
27256  * @class Roo.bootstrap.DocumentViewer
27257  * @extends Roo.bootstrap.Component
27258  * Bootstrap DocumentViewer class
27259  * 
27260  * @constructor
27261  * Create a new DocumentViewer
27262  * @param {Object} config The config object
27263  */
27264
27265 Roo.bootstrap.DocumentViewer = function(config){
27266     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27267     
27268     this.addEvents({
27269         /**
27270          * @event initial
27271          * Fire after initEvent
27272          * @param {Roo.bootstrap.DocumentViewer} this
27273          */
27274         "initial" : true,
27275         /**
27276          * @event click
27277          * Fire after click
27278          * @param {Roo.bootstrap.DocumentViewer} this
27279          */
27280         "click" : true,
27281         /**
27282          * @event trash
27283          * Fire after trash button
27284          * @param {Roo.bootstrap.DocumentViewer} this
27285          */
27286         "trash" : true
27287         
27288     });
27289 };
27290
27291 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27292     
27293     getAutoCreate : function()
27294     {
27295         var cfg = {
27296             tag : 'div',
27297             cls : 'roo-document-viewer',
27298             cn : [
27299                 {
27300                     tag : 'div',
27301                     cls : 'roo-document-viewer-body',
27302                     cn : [
27303                         {
27304                             tag : 'div',
27305                             cls : 'roo-document-viewer-thumb',
27306                             cn : [
27307                                 {
27308                                     tag : 'img',
27309                                     cls : 'roo-document-viewer-image'
27310                                 }
27311                             ]
27312                         }
27313                     ]
27314                 },
27315                 {
27316                     tag : 'div',
27317                     cls : 'roo-document-viewer-footer',
27318                     cn : {
27319                         tag : 'div',
27320                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27321                         cn : [
27322                             {
27323                                 tag : 'div',
27324                                 cls : 'btn-group',
27325                                 cn : [
27326                                     {
27327                                         tag : 'button',
27328                                         cls : 'btn btn-default roo-document-viewer-trash',
27329                                         html : '<i class="fa fa-trash"></i>'
27330                                     }
27331                                 ]
27332                             }
27333                         ]
27334                     }
27335                 }
27336             ]
27337         };
27338         
27339         return cfg;
27340     },
27341     
27342     initEvents : function()
27343     {
27344         
27345         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27346         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27347         
27348         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27349         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27350         
27351         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27352         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27353         
27354         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27355         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27356         
27357         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27358         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27359         
27360         this.bodyEl.on('click', this.onClick, this);
27361         
27362         this.trashBtn.on('click', this.onTrash, this);
27363         
27364     },
27365     
27366     initial : function()
27367     {
27368 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27369         
27370         
27371         this.fireEvent('initial', this);
27372         
27373     },
27374     
27375     onClick : function(e)
27376     {
27377         e.preventDefault();
27378         
27379         this.fireEvent('click', this);
27380     },
27381     
27382     onTrash : function(e)
27383     {
27384         e.preventDefault();
27385         
27386         this.fireEvent('trash', this);
27387     }
27388     
27389 });
27390 /*
27391  * - LGPL
27392  *
27393  * nav progress bar
27394  * 
27395  */
27396
27397 /**
27398  * @class Roo.bootstrap.NavProgressBar
27399  * @extends Roo.bootstrap.Component
27400  * Bootstrap NavProgressBar class
27401  * 
27402  * @constructor
27403  * Create a new nav progress bar
27404  * @param {Object} config The config object
27405  */
27406
27407 Roo.bootstrap.NavProgressBar = function(config){
27408     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27409
27410     this.bullets = this.bullets || [];
27411    
27412 //    Roo.bootstrap.NavProgressBar.register(this);
27413      this.addEvents({
27414         /**
27415              * @event changed
27416              * Fires when the active item changes
27417              * @param {Roo.bootstrap.NavProgressBar} this
27418              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27419              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27420          */
27421         'changed': true
27422      });
27423     
27424 };
27425
27426 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27427     
27428     bullets : [],
27429     barItems : [],
27430     
27431     getAutoCreate : function()
27432     {
27433         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27434         
27435         cfg = {
27436             tag : 'div',
27437             cls : 'roo-navigation-bar-group',
27438             cn : [
27439                 {
27440                     tag : 'div',
27441                     cls : 'roo-navigation-top-bar'
27442                 },
27443                 {
27444                     tag : 'div',
27445                     cls : 'roo-navigation-bullets-bar',
27446                     cn : [
27447                         {
27448                             tag : 'ul',
27449                             cls : 'roo-navigation-bar'
27450                         }
27451                     ]
27452                 },
27453                 
27454                 {
27455                     tag : 'div',
27456                     cls : 'roo-navigation-bottom-bar'
27457                 }
27458             ]
27459             
27460         };
27461         
27462         return cfg;
27463         
27464     },
27465     
27466     initEvents: function() 
27467     {
27468         
27469     },
27470     
27471     onRender : function(ct, position) 
27472     {
27473         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27474         
27475         if(this.bullets.length){
27476             Roo.each(this.bullets, function(b){
27477                this.addItem(b);
27478             }, this);
27479         }
27480         
27481         this.format();
27482         
27483     },
27484     
27485     addItem : function(cfg)
27486     {
27487         var item = new Roo.bootstrap.NavProgressItem(cfg);
27488         
27489         item.parentId = this.id;
27490         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27491         
27492         if(cfg.html){
27493             var top = new Roo.bootstrap.Element({
27494                 tag : 'div',
27495                 cls : 'roo-navigation-bar-text'
27496             });
27497             
27498             var bottom = new Roo.bootstrap.Element({
27499                 tag : 'div',
27500                 cls : 'roo-navigation-bar-text'
27501             });
27502             
27503             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27504             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27505             
27506             var topText = new Roo.bootstrap.Element({
27507                 tag : 'span',
27508                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27509             });
27510             
27511             var bottomText = new Roo.bootstrap.Element({
27512                 tag : 'span',
27513                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27514             });
27515             
27516             topText.onRender(top.el, null);
27517             bottomText.onRender(bottom.el, null);
27518             
27519             item.topEl = top;
27520             item.bottomEl = bottom;
27521         }
27522         
27523         this.barItems.push(item);
27524         
27525         return item;
27526     },
27527     
27528     getActive : function()
27529     {
27530         var active = false;
27531         
27532         Roo.each(this.barItems, function(v){
27533             
27534             if (!v.isActive()) {
27535                 return;
27536             }
27537             
27538             active = v;
27539             return false;
27540             
27541         });
27542         
27543         return active;
27544     },
27545     
27546     setActiveItem : function(item)
27547     {
27548         var prev = false;
27549         
27550         Roo.each(this.barItems, function(v){
27551             if (v.rid == item.rid) {
27552                 return ;
27553             }
27554             
27555             if (v.isActive()) {
27556                 v.setActive(false);
27557                 prev = v;
27558             }
27559         });
27560
27561         item.setActive(true);
27562         
27563         this.fireEvent('changed', this, item, prev);
27564     },
27565     
27566     getBarItem: function(rid)
27567     {
27568         var ret = false;
27569         
27570         Roo.each(this.barItems, function(e) {
27571             if (e.rid != rid) {
27572                 return;
27573             }
27574             
27575             ret =  e;
27576             return false;
27577         });
27578         
27579         return ret;
27580     },
27581     
27582     indexOfItem : function(item)
27583     {
27584         var index = false;
27585         
27586         Roo.each(this.barItems, function(v, i){
27587             
27588             if (v.rid != item.rid) {
27589                 return;
27590             }
27591             
27592             index = i;
27593             return false
27594         });
27595         
27596         return index;
27597     },
27598     
27599     setActiveNext : function()
27600     {
27601         var i = this.indexOfItem(this.getActive());
27602         
27603         if (i > this.barItems.length) {
27604             return;
27605         }
27606         
27607         this.setActiveItem(this.barItems[i+1]);
27608     },
27609     
27610     setActivePrev : function()
27611     {
27612         var i = this.indexOfItem(this.getActive());
27613         
27614         if (i  < 1) {
27615             return;
27616         }
27617         
27618         this.setActiveItem(this.barItems[i-1]);
27619     },
27620     
27621     format : function()
27622     {
27623         if(!this.barItems.length){
27624             return;
27625         }
27626      
27627         var width = 100 / this.barItems.length;
27628         
27629         Roo.each(this.barItems, function(i){
27630             i.el.setStyle('width', width + '%');
27631             i.topEl.el.setStyle('width', width + '%');
27632             i.bottomEl.el.setStyle('width', width + '%');
27633         }, this);
27634         
27635     }
27636     
27637 });
27638 /*
27639  * - LGPL
27640  *
27641  * Nav Progress Item
27642  * 
27643  */
27644
27645 /**
27646  * @class Roo.bootstrap.NavProgressItem
27647  * @extends Roo.bootstrap.Component
27648  * Bootstrap NavProgressItem class
27649  * @cfg {String} rid the reference id
27650  * @cfg {Boolean} active (true|false) Is item active default false
27651  * @cfg {Boolean} disabled (true|false) Is item active default false
27652  * @cfg {String} html
27653  * @cfg {String} position (top|bottom) text position default bottom
27654  * @cfg {String} icon show icon instead of number
27655  * 
27656  * @constructor
27657  * Create a new NavProgressItem
27658  * @param {Object} config The config object
27659  */
27660 Roo.bootstrap.NavProgressItem = function(config){
27661     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27662     this.addEvents({
27663         // raw events
27664         /**
27665          * @event click
27666          * The raw click event for the entire grid.
27667          * @param {Roo.bootstrap.NavProgressItem} this
27668          * @param {Roo.EventObject} e
27669          */
27670         "click" : true
27671     });
27672    
27673 };
27674
27675 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27676     
27677     rid : '',
27678     active : false,
27679     disabled : false,
27680     html : '',
27681     position : 'bottom',
27682     icon : false,
27683     
27684     getAutoCreate : function()
27685     {
27686         var iconCls = 'roo-navigation-bar-item-icon';
27687         
27688         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27689         
27690         var cfg = {
27691             tag: 'li',
27692             cls: 'roo-navigation-bar-item',
27693             cn : [
27694                 {
27695                     tag : 'i',
27696                     cls : iconCls
27697                 }
27698             ]
27699         };
27700         
27701         if(this.active){
27702             cfg.cls += ' active';
27703         }
27704         if(this.disabled){
27705             cfg.cls += ' disabled';
27706         }
27707         
27708         return cfg;
27709     },
27710     
27711     disable : function()
27712     {
27713         this.setDisabled(true);
27714     },
27715     
27716     enable : function()
27717     {
27718         this.setDisabled(false);
27719     },
27720     
27721     initEvents: function() 
27722     {
27723         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27724         
27725         this.iconEl.on('click', this.onClick, this);
27726     },
27727     
27728     onClick : function(e)
27729     {
27730         e.preventDefault();
27731         
27732         if(this.disabled){
27733             return;
27734         }
27735         
27736         if(this.fireEvent('click', this, e) === false){
27737             return;
27738         };
27739         
27740         this.parent().setActiveItem(this);
27741     },
27742     
27743     isActive: function () 
27744     {
27745         return this.active;
27746     },
27747     
27748     setActive : function(state)
27749     {
27750         if(this.active == state){
27751             return;
27752         }
27753         
27754         this.active = state;
27755         
27756         if (state) {
27757             this.el.addClass('active');
27758             return;
27759         }
27760         
27761         this.el.removeClass('active');
27762         
27763         return;
27764     },
27765     
27766     setDisabled : function(state)
27767     {
27768         if(this.disabled == state){
27769             return;
27770         }
27771         
27772         this.disabled = state;
27773         
27774         if (state) {
27775             this.el.addClass('disabled');
27776             return;
27777         }
27778         
27779         this.el.removeClass('disabled');
27780     },
27781     
27782     tooltipEl : function()
27783     {
27784         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27785     }
27786 });
27787  
27788
27789  /*
27790  * - LGPL
27791  *
27792  * FieldLabel
27793  * 
27794  */
27795
27796 /**
27797  * @class Roo.bootstrap.FieldLabel
27798  * @extends Roo.bootstrap.Component
27799  * Bootstrap FieldLabel class
27800  * @cfg {String} html contents of the element
27801  * @cfg {String} tag tag of the element default label
27802  * @cfg {String} cls class of the element
27803  * @cfg {String} target label target 
27804  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27805  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27806  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27807  * @cfg {String} iconTooltip default "This field is required"
27808  * 
27809  * @constructor
27810  * Create a new FieldLabel
27811  * @param {Object} config The config object
27812  */
27813
27814 Roo.bootstrap.FieldLabel = function(config){
27815     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27816     
27817     this.addEvents({
27818             /**
27819              * @event invalid
27820              * Fires after the field has been marked as invalid.
27821              * @param {Roo.form.FieldLabel} this
27822              * @param {String} msg The validation message
27823              */
27824             invalid : true,
27825             /**
27826              * @event valid
27827              * Fires after the field has been validated with no errors.
27828              * @param {Roo.form.FieldLabel} this
27829              */
27830             valid : true
27831         });
27832 };
27833
27834 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27835     
27836     tag: 'label',
27837     cls: '',
27838     html: '',
27839     target: '',
27840     allowBlank : true,
27841     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27842     validClass : 'text-success fa fa-lg fa-check',
27843     iconTooltip : 'This field is required',
27844     
27845     getAutoCreate : function(){
27846         
27847         var cfg = {
27848             tag : this.tag,
27849             cls : 'roo-bootstrap-field-label ' + this.cls,
27850             for : this.target,
27851             cn : [
27852                 {
27853                     tag : 'i',
27854                     cls : '',
27855                     tooltip : this.iconTooltip
27856                 },
27857                 {
27858                     tag : 'span',
27859                     html : this.html
27860                 }
27861             ] 
27862         };
27863         
27864         return cfg;
27865     },
27866     
27867     initEvents: function() 
27868     {
27869         Roo.bootstrap.Element.superclass.initEvents.call(this);
27870         
27871         this.iconEl = this.el.select('i', true).first();
27872         
27873         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27874         
27875         Roo.bootstrap.FieldLabel.register(this);
27876     },
27877     
27878     /**
27879      * Mark this field as valid
27880      */
27881     markValid : function()
27882     {
27883         this.iconEl.show();
27884         
27885         this.iconEl.removeClass(this.invalidClass);
27886         
27887         this.iconEl.addClass(this.validClass);
27888         
27889         this.fireEvent('valid', this);
27890     },
27891     
27892     /**
27893      * Mark this field as invalid
27894      * @param {String} msg The validation message
27895      */
27896     markInvalid : function(msg)
27897     {
27898         this.iconEl.show();
27899         
27900         this.iconEl.removeClass(this.validClass);
27901         
27902         this.iconEl.addClass(this.invalidClass);
27903         
27904         this.fireEvent('invalid', this, msg);
27905     }
27906     
27907    
27908 });
27909
27910 Roo.apply(Roo.bootstrap.FieldLabel, {
27911     
27912     groups: {},
27913     
27914      /**
27915     * register a FieldLabel Group
27916     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27917     */
27918     register : function(label)
27919     {
27920         if(this.groups.hasOwnProperty(label.target)){
27921             return;
27922         }
27923      
27924         this.groups[label.target] = label;
27925         
27926     },
27927     /**
27928     * fetch a FieldLabel Group based on the target
27929     * @param {string} target
27930     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27931     */
27932     get: function(target) {
27933         if (typeof(this.groups[target]) == 'undefined') {
27934             return false;
27935         }
27936         
27937         return this.groups[target] ;
27938     }
27939 });
27940
27941  
27942
27943  /*
27944  * - LGPL
27945  *
27946  * page DateSplitField.
27947  * 
27948  */
27949
27950
27951 /**
27952  * @class Roo.bootstrap.DateSplitField
27953  * @extends Roo.bootstrap.Component
27954  * Bootstrap DateSplitField class
27955  * @cfg {string} fieldLabel - the label associated
27956  * @cfg {Number} labelWidth set the width of label (0-12)
27957  * @cfg {String} labelAlign (top|left)
27958  * @cfg {Boolean} dayAllowBlank (true|false) default false
27959  * @cfg {Boolean} monthAllowBlank (true|false) default false
27960  * @cfg {Boolean} yearAllowBlank (true|false) default false
27961  * @cfg {string} dayPlaceholder 
27962  * @cfg {string} monthPlaceholder
27963  * @cfg {string} yearPlaceholder
27964  * @cfg {string} dayFormat default 'd'
27965  * @cfg {string} monthFormat default 'm'
27966  * @cfg {string} yearFormat default 'Y'
27967
27968  *     
27969  * @constructor
27970  * Create a new DateSplitField
27971  * @param {Object} config The config object
27972  */
27973
27974 Roo.bootstrap.DateSplitField = function(config){
27975     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27976     
27977     this.addEvents({
27978         // raw events
27979          /**
27980          * @event years
27981          * getting the data of years
27982          * @param {Roo.bootstrap.DateSplitField} this
27983          * @param {Object} years
27984          */
27985         "years" : true,
27986         /**
27987          * @event days
27988          * getting the data of days
27989          * @param {Roo.bootstrap.DateSplitField} this
27990          * @param {Object} days
27991          */
27992         "days" : true,
27993         /**
27994          * @event invalid
27995          * Fires after the field has been marked as invalid.
27996          * @param {Roo.form.Field} this
27997          * @param {String} msg The validation message
27998          */
27999         invalid : true,
28000        /**
28001          * @event valid
28002          * Fires after the field has been validated with no errors.
28003          * @param {Roo.form.Field} this
28004          */
28005         valid : true
28006     });
28007 };
28008
28009 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28010     
28011     fieldLabel : '',
28012     labelAlign : 'top',
28013     labelWidth : 3,
28014     dayAllowBlank : false,
28015     monthAllowBlank : false,
28016     yearAllowBlank : false,
28017     dayPlaceholder : '',
28018     monthPlaceholder : '',
28019     yearPlaceholder : '',
28020     dayFormat : 'd',
28021     monthFormat : 'm',
28022     yearFormat : 'Y',
28023     isFormField : true,
28024     
28025     getAutoCreate : function()
28026     {
28027         var cfg = {
28028             tag : 'div',
28029             cls : 'row roo-date-split-field-group',
28030             cn : [
28031                 {
28032                     tag : 'input',
28033                     type : 'hidden',
28034                     cls : 'form-hidden-field roo-date-split-field-group-value',
28035                     name : this.name
28036                 }
28037             ]
28038         };
28039         
28040         if(this.fieldLabel){
28041             cfg.cn.push({
28042                 tag : 'div',
28043                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28044                 cn : [
28045                     {
28046                         tag : 'label',
28047                         html : this.fieldLabel
28048                     }
28049                 ]
28050             });
28051         }
28052         
28053         Roo.each(['day', 'month', 'year'], function(t){
28054             cfg.cn.push({
28055                 tag : 'div',
28056                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28057             });
28058         }, this);
28059         
28060         return cfg;
28061     },
28062     
28063     inputEl: function ()
28064     {
28065         return this.el.select('.roo-date-split-field-group-value', true).first();
28066     },
28067     
28068     onRender : function(ct, position) 
28069     {
28070         var _this = this;
28071         
28072         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28073         
28074         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28075         
28076         this.dayField = new Roo.bootstrap.ComboBox({
28077             allowBlank : this.dayAllowBlank,
28078             alwaysQuery : true,
28079             displayField : 'value',
28080             editable : false,
28081             fieldLabel : '',
28082             forceSelection : true,
28083             mode : 'local',
28084             placeholder : this.dayPlaceholder,
28085             selectOnFocus : true,
28086             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28087             triggerAction : 'all',
28088             typeAhead : true,
28089             valueField : 'value',
28090             store : new Roo.data.SimpleStore({
28091                 data : (function() {    
28092                     var days = [];
28093                     _this.fireEvent('days', _this, days);
28094                     return days;
28095                 })(),
28096                 fields : [ 'value' ]
28097             }),
28098             listeners : {
28099                 select : function (_self, record, index)
28100                 {
28101                     _this.setValue(_this.getValue());
28102                 }
28103             }
28104         });
28105
28106         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28107         
28108         this.monthField = new Roo.bootstrap.MonthField({
28109             after : '<i class=\"fa fa-calendar\"></i>',
28110             allowBlank : this.monthAllowBlank,
28111             placeholder : this.monthPlaceholder,
28112             readOnly : true,
28113             listeners : {
28114                 render : function (_self)
28115                 {
28116                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28117                         e.preventDefault();
28118                         _self.focus();
28119                     });
28120                 },
28121                 select : function (_self, oldvalue, newvalue)
28122                 {
28123                     _this.setValue(_this.getValue());
28124                 }
28125             }
28126         });
28127         
28128         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28129         
28130         this.yearField = new Roo.bootstrap.ComboBox({
28131             allowBlank : this.yearAllowBlank,
28132             alwaysQuery : true,
28133             displayField : 'value',
28134             editable : false,
28135             fieldLabel : '',
28136             forceSelection : true,
28137             mode : 'local',
28138             placeholder : this.yearPlaceholder,
28139             selectOnFocus : true,
28140             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28141             triggerAction : 'all',
28142             typeAhead : true,
28143             valueField : 'value',
28144             store : new Roo.data.SimpleStore({
28145                 data : (function() {
28146                     var years = [];
28147                     _this.fireEvent('years', _this, years);
28148                     return years;
28149                 })(),
28150                 fields : [ 'value' ]
28151             }),
28152             listeners : {
28153                 select : function (_self, record, index)
28154                 {
28155                     _this.setValue(_this.getValue());
28156                 }
28157             }
28158         });
28159
28160         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28161     },
28162     
28163     setValue : function(v, format)
28164     {
28165         this.inputEl.dom.value = v;
28166         
28167         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28168         
28169         var d = Date.parseDate(v, f);
28170         
28171         if(!d){
28172             this.validate();
28173             return;
28174         }
28175         
28176         this.setDay(d.format(this.dayFormat));
28177         this.setMonth(d.format(this.monthFormat));
28178         this.setYear(d.format(this.yearFormat));
28179         
28180         this.validate();
28181         
28182         return;
28183     },
28184     
28185     setDay : function(v)
28186     {
28187         this.dayField.setValue(v);
28188         this.inputEl.dom.value = this.getValue();
28189         this.validate();
28190         return;
28191     },
28192     
28193     setMonth : function(v)
28194     {
28195         this.monthField.setValue(v, true);
28196         this.inputEl.dom.value = this.getValue();
28197         this.validate();
28198         return;
28199     },
28200     
28201     setYear : function(v)
28202     {
28203         this.yearField.setValue(v);
28204         this.inputEl.dom.value = this.getValue();
28205         this.validate();
28206         return;
28207     },
28208     
28209     getDay : function()
28210     {
28211         return this.dayField.getValue();
28212     },
28213     
28214     getMonth : function()
28215     {
28216         return this.monthField.getValue();
28217     },
28218     
28219     getYear : function()
28220     {
28221         return this.yearField.getValue();
28222     },
28223     
28224     getValue : function()
28225     {
28226         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28227         
28228         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28229         
28230         return date;
28231     },
28232     
28233     reset : function()
28234     {
28235         this.setDay('');
28236         this.setMonth('');
28237         this.setYear('');
28238         this.inputEl.dom.value = '';
28239         this.validate();
28240         return;
28241     },
28242     
28243     validate : function()
28244     {
28245         var d = this.dayField.validate();
28246         var m = this.monthField.validate();
28247         var y = this.yearField.validate();
28248         
28249         var valid = true;
28250         
28251         if(
28252                 (!this.dayAllowBlank && !d) ||
28253                 (!this.monthAllowBlank && !m) ||
28254                 (!this.yearAllowBlank && !y)
28255         ){
28256             valid = false;
28257         }
28258         
28259         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28260             return valid;
28261         }
28262         
28263         if(valid){
28264             this.markValid();
28265             return valid;
28266         }
28267         
28268         this.markInvalid();
28269         
28270         return valid;
28271     },
28272     
28273     markValid : function()
28274     {
28275         
28276         var label = this.el.select('label', true).first();
28277         var icon = this.el.select('i.fa-star', true).first();
28278
28279         if(label && icon){
28280             icon.remove();
28281         }
28282         
28283         this.fireEvent('valid', this);
28284     },
28285     
28286      /**
28287      * Mark this field as invalid
28288      * @param {String} msg The validation message
28289      */
28290     markInvalid : function(msg)
28291     {
28292         
28293         var label = this.el.select('label', true).first();
28294         var icon = this.el.select('i.fa-star', true).first();
28295
28296         if(label && !icon){
28297             this.el.select('.roo-date-split-field-label', true).createChild({
28298                 tag : 'i',
28299                 cls : 'text-danger fa fa-lg fa-star',
28300                 tooltip : 'This field is required',
28301                 style : 'margin-right:5px;'
28302             }, label, true);
28303         }
28304         
28305         this.fireEvent('invalid', this, msg);
28306     },
28307     
28308     clearInvalid : function()
28309     {
28310         var label = this.el.select('label', true).first();
28311         var icon = this.el.select('i.fa-star', true).first();
28312
28313         if(label && icon){
28314             icon.remove();
28315         }
28316         
28317         this.fireEvent('valid', this);
28318     },
28319     
28320     getName: function()
28321     {
28322         return this.name;
28323     }
28324     
28325 });
28326
28327  /**
28328  *
28329  * This is based on 
28330  * http://masonry.desandro.com
28331  *
28332  * The idea is to render all the bricks based on vertical width...
28333  *
28334  * The original code extends 'outlayer' - we might need to use that....
28335  * 
28336  */
28337
28338
28339 /**
28340  * @class Roo.bootstrap.LayoutMasonry
28341  * @extends Roo.bootstrap.Component
28342  * Bootstrap Layout Masonry class
28343  * 
28344  * @constructor
28345  * Create a new Element
28346  * @param {Object} config The config object
28347  */
28348
28349 Roo.bootstrap.LayoutMasonry = function(config){
28350     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28351     
28352     this.bricks = [];
28353     
28354 };
28355
28356 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28357     
28358     /**
28359      * @cfg {Boolean} isLayoutInstant = no animation?
28360      */   
28361     isLayoutInstant : false, // needed?
28362    
28363     /**
28364      * @cfg {Number} boxWidth  width of the columns
28365      */   
28366     boxWidth : 450,
28367     
28368       /**
28369      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28370      */   
28371     boxHeight : 0,
28372     
28373     /**
28374      * @cfg {Number} padWidth padding below box..
28375      */   
28376     padWidth : 10, 
28377     
28378     /**
28379      * @cfg {Number} gutter gutter width..
28380      */   
28381     gutter : 10, 
28382     
28383     /**
28384      * @cfg {Boolean} isAutoInitial defalut true
28385      */   
28386     isAutoInitial : true, 
28387     
28388     containerWidth: 0,
28389     
28390     /**
28391      * @cfg {Boolean} isHorizontal defalut false
28392      */   
28393     isHorizontal : false, 
28394
28395     currentSize : null,
28396     
28397     tag: 'div',
28398     
28399     cls: '',
28400     
28401     bricks: null, //CompositeElement
28402     
28403     cols : 1,
28404     
28405     _isLayoutInited : false,
28406     
28407 //    isAlternative : false, // only use for vertical layout...
28408     
28409     /**
28410      * @cfg {Number} alternativePadWidth padding below box..
28411      */   
28412     alternativePadWidth : 50, 
28413     
28414     getAutoCreate : function(){
28415         
28416         var cfg = {
28417             tag: this.tag,
28418             cls: 'blog-masonary-wrapper ' + this.cls,
28419             cn : {
28420                 cls : 'mas-boxes masonary'
28421             }
28422         };
28423         
28424         return cfg;
28425     },
28426     
28427     getChildContainer: function( )
28428     {
28429         if (this.boxesEl) {
28430             return this.boxesEl;
28431         }
28432         
28433         this.boxesEl = this.el.select('.mas-boxes').first();
28434         
28435         return this.boxesEl;
28436     },
28437     
28438     
28439     initEvents : function()
28440     {
28441         var _this = this;
28442         
28443         if(this.isAutoInitial){
28444             Roo.log('hook children rendered');
28445             this.on('childrenrendered', function() {
28446                 Roo.log('children rendered');
28447                 _this.initial();
28448             } ,this);
28449         }
28450     },
28451     
28452     initial : function()
28453     {
28454         this.currentSize = this.el.getBox(true);
28455         
28456         Roo.EventManager.onWindowResize(this.resize, this); 
28457
28458         if(!this.isAutoInitial){
28459             this.layout();
28460             return;
28461         }
28462         
28463         this.layout();
28464         
28465         return;
28466         //this.layout.defer(500,this);
28467         
28468     },
28469     
28470     resize : function()
28471     {
28472         Roo.log('resize');
28473         
28474         var cs = this.el.getBox(true);
28475         
28476         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28477             Roo.log("no change in with or X");
28478             return;
28479         }
28480         
28481         this.currentSize = cs;
28482         
28483         this.layout();
28484         
28485     },
28486     
28487     layout : function()
28488     {   
28489         this._resetLayout();
28490         
28491         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28492         
28493         this.layoutItems( isInstant );
28494       
28495         this._isLayoutInited = true;
28496         
28497     },
28498     
28499     _resetLayout : function()
28500     {
28501         if(this.isHorizontal){
28502             this.horizontalMeasureColumns();
28503             return;
28504         }
28505         
28506         this.verticalMeasureColumns();
28507         
28508     },
28509     
28510     verticalMeasureColumns : function()
28511     {
28512         this.getContainerWidth();
28513         
28514 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28515 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28516 //            return;
28517 //        }
28518         
28519         var boxWidth = this.boxWidth + this.padWidth;
28520         
28521         if(this.containerWidth < this.boxWidth){
28522             boxWidth = this.containerWidth
28523         }
28524         
28525         var containerWidth = this.containerWidth;
28526         
28527         var cols = Math.floor(containerWidth / boxWidth);
28528         
28529         this.cols = Math.max( cols, 1 );
28530         
28531         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28532         
28533         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28534         
28535         this.colWidth = boxWidth + avail - this.padWidth;
28536         
28537         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28538         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28539     },
28540     
28541     horizontalMeasureColumns : function()
28542     {
28543         this.getContainerWidth();
28544         
28545         var boxWidth = this.boxWidth;
28546         
28547         if(this.containerWidth < boxWidth){
28548             boxWidth = this.containerWidth;
28549         }
28550         
28551         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28552         
28553         this.el.setHeight(boxWidth);
28554         
28555     },
28556     
28557     getContainerWidth : function()
28558     {
28559         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28560     },
28561     
28562     layoutItems : function( isInstant )
28563     {
28564         var items = Roo.apply([], this.bricks);
28565         
28566         if(this.isHorizontal){
28567             this._horizontalLayoutItems( items , isInstant );
28568             return;
28569         }
28570         
28571 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28572 //            this._verticalAlternativeLayoutItems( items , isInstant );
28573 //            return;
28574 //        }
28575         
28576         this._verticalLayoutItems( items , isInstant );
28577         
28578     },
28579     
28580     _verticalLayoutItems : function ( items , isInstant)
28581     {
28582         if ( !items || !items.length ) {
28583             return;
28584         }
28585         
28586         var standard = [
28587             ['xs', 'xs', 'xs', 'tall'],
28588             ['xs', 'xs', 'tall'],
28589             ['xs', 'xs', 'sm'],
28590             ['xs', 'xs', 'xs'],
28591             ['xs', 'tall'],
28592             ['xs', 'sm'],
28593             ['xs', 'xs'],
28594             ['xs'],
28595             
28596             ['sm', 'xs', 'xs'],
28597             ['sm', 'xs'],
28598             ['sm'],
28599             
28600             ['tall', 'xs', 'xs', 'xs'],
28601             ['tall', 'xs', 'xs'],
28602             ['tall', 'xs'],
28603             ['tall']
28604             
28605         ];
28606         
28607         var queue = [];
28608         
28609         var boxes = [];
28610         
28611         var box = [];
28612         
28613         Roo.each(items, function(item, k){
28614             
28615             switch (item.size) {
28616                 // these layouts take up a full box,
28617                 case 'md' :
28618                 case 'md-left' :
28619                 case 'md-right' :
28620                 case 'wide' :
28621                     
28622                     if(box.length){
28623                         boxes.push(box);
28624                         box = [];
28625                     }
28626                     
28627                     boxes.push([item]);
28628                     
28629                     break;
28630                     
28631                 case 'xs' :
28632                 case 'sm' :
28633                 case 'tall' :
28634                     
28635                     box.push(item);
28636                     
28637                     break;
28638                 default :
28639                     break;
28640                     
28641             }
28642             
28643         }, this);
28644         
28645         if(box.length){
28646             boxes.push(box);
28647             box = [];
28648         }
28649         
28650         var filterPattern = function(box, length)
28651         {
28652             if(!box.length){
28653                 return;
28654             }
28655             
28656             var match = false;
28657             
28658             var pattern = box.slice(0, length);
28659             
28660             var format = [];
28661             
28662             Roo.each(pattern, function(i){
28663                 format.push(i.size);
28664             }, this);
28665             
28666             Roo.each(standard, function(s){
28667                 
28668                 if(String(s) != String(format)){
28669                     return;
28670                 }
28671                 
28672                 match = true;
28673                 return false;
28674                 
28675             }, this);
28676             
28677             if(!match && length == 1){
28678                 return;
28679             }
28680             
28681             if(!match){
28682                 filterPattern(box, length - 1);
28683                 return;
28684             }
28685                 
28686             queue.push(pattern);
28687
28688             box = box.slice(length, box.length);
28689
28690             filterPattern(box, 4);
28691
28692             return;
28693             
28694         }
28695         
28696         Roo.each(boxes, function(box, k){
28697             
28698             if(!box.length){
28699                 return;
28700             }
28701             
28702             if(box.length == 1){
28703                 queue.push(box);
28704                 return;
28705             }
28706             
28707             filterPattern(box, 4);
28708             
28709         }, this);
28710         
28711         this._processVerticalLayoutQueue( queue, isInstant );
28712         
28713     },
28714     
28715 //    _verticalAlternativeLayoutItems : function( items , isInstant )
28716 //    {
28717 //        if ( !items || !items.length ) {
28718 //            return;
28719 //        }
28720 //
28721 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
28722 //        
28723 //    },
28724     
28725     _horizontalLayoutItems : function ( items , isInstant)
28726     {
28727         if ( !items || !items.length || items.length < 3) {
28728             return;
28729         }
28730         
28731         items.reverse();
28732         
28733         var eItems = items.slice(0, 3);
28734         
28735         items = items.slice(3, items.length);
28736         
28737         var standard = [
28738             ['xs', 'xs', 'xs', 'wide'],
28739             ['xs', 'xs', 'wide'],
28740             ['xs', 'xs', 'sm'],
28741             ['xs', 'xs', 'xs'],
28742             ['xs', 'wide'],
28743             ['xs', 'sm'],
28744             ['xs', 'xs'],
28745             ['xs'],
28746             
28747             ['sm', 'xs', 'xs'],
28748             ['sm', 'xs'],
28749             ['sm'],
28750             
28751             ['wide', 'xs', 'xs', 'xs'],
28752             ['wide', 'xs', 'xs'],
28753             ['wide', 'xs'],
28754             ['wide'],
28755             
28756             ['wide-thin']
28757         ];
28758         
28759         var queue = [];
28760         
28761         var boxes = [];
28762         
28763         var box = [];
28764         
28765         Roo.each(items, function(item, k){
28766             
28767             switch (item.size) {
28768                 case 'md' :
28769                 case 'md-left' :
28770                 case 'md-right' :
28771                 case 'tall' :
28772                     
28773                     if(box.length){
28774                         boxes.push(box);
28775                         box = [];
28776                     }
28777                     
28778                     boxes.push([item]);
28779                     
28780                     break;
28781                     
28782                 case 'xs' :
28783                 case 'sm' :
28784                 case 'wide' :
28785                 case 'wide-thin' :
28786                     
28787                     box.push(item);
28788                     
28789                     break;
28790                 default :
28791                     break;
28792                     
28793             }
28794             
28795         }, this);
28796         
28797         if(box.length){
28798             boxes.push(box);
28799             box = [];
28800         }
28801         
28802         var filterPattern = function(box, length)
28803         {
28804             if(!box.length){
28805                 return;
28806             }
28807             
28808             var match = false;
28809             
28810             var pattern = box.slice(0, length);
28811             
28812             var format = [];
28813             
28814             Roo.each(pattern, function(i){
28815                 format.push(i.size);
28816             }, this);
28817             
28818             Roo.each(standard, function(s){
28819                 
28820                 if(String(s) != String(format)){
28821                     return;
28822                 }
28823                 
28824                 match = true;
28825                 return false;
28826                 
28827             }, this);
28828             
28829             if(!match && length == 1){
28830                 return;
28831             }
28832             
28833             if(!match){
28834                 filterPattern(box, length - 1);
28835                 return;
28836             }
28837                 
28838             queue.push(pattern);
28839
28840             box = box.slice(length, box.length);
28841
28842             filterPattern(box, 4);
28843
28844             return;
28845             
28846         }
28847         
28848         Roo.each(boxes, function(box, k){
28849             
28850             if(!box.length){
28851                 return;
28852             }
28853             
28854             if(box.length == 1){
28855                 queue.push(box);
28856                 return;
28857             }
28858             
28859             filterPattern(box, 4);
28860             
28861         }, this);
28862         
28863         
28864         var prune = [];
28865         
28866         var pos = this.el.getBox(true);
28867         
28868         var minX = pos.x;
28869         
28870         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
28871         
28872         var hit_end = false;
28873         
28874         Roo.each(queue, function(box){
28875             
28876             if(hit_end){
28877                 
28878                 Roo.each(box, function(b){
28879                 
28880                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
28881                     b.el.hide();
28882
28883                 }, this);
28884
28885                 return;
28886             }
28887             
28888             var mx = 0;
28889             
28890             Roo.each(box, function(b){
28891                 
28892                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
28893                 b.el.show();
28894
28895                 mx = Math.max(mx, b.x);
28896                 
28897             }, this);
28898             
28899             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
28900             
28901             if(maxX < minX){
28902                 
28903                 Roo.each(box, function(b){
28904                 
28905                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
28906                     b.el.hide();
28907                     
28908                 }, this);
28909                 
28910                 hit_end = true;
28911                 
28912                 return;
28913             }
28914             
28915             prune.push(box);
28916             
28917         }, this);
28918         
28919         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
28920     },
28921     
28922     /** Sets position of item in DOM
28923     * @param {Element} item
28924     * @param {Number} x - horizontal position
28925     * @param {Number} y - vertical position
28926     * @param {Boolean} isInstant - disables transitions
28927     */
28928     _processVerticalLayoutQueue : function( queue, isInstant )
28929     {
28930         var pos = this.el.getBox(true);
28931         var x = pos.x;
28932         var y = pos.y;
28933         var maxY = [];
28934         
28935         for (var i = 0; i < this.cols; i++){
28936             maxY[i] = pos.y;
28937         }
28938         
28939         Roo.each(queue, function(box, k){
28940             
28941             var col = k % this.cols;
28942             
28943             Roo.each(box, function(b,kk){
28944                 
28945                 b.el.position('absolute');
28946                 
28947                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
28948                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
28949                 
28950                 if(b.size == 'md-left' || b.size == 'md-right'){
28951                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
28952                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
28953                 }
28954                 
28955                 b.el.setWidth(width);
28956                 b.el.setHeight(height);
28957                 
28958             }, this);
28959             
28960             for (var i = 0; i < this.cols; i++){
28961                 
28962                 if(maxY[i] < maxY[col]){
28963                     col = i;
28964                     continue;
28965                 }
28966                 
28967                 col = Math.min(col, i);
28968                 
28969             }
28970             
28971             x = pos.x + col * (this.colWidth + this.padWidth);
28972             
28973             y = maxY[col];
28974             
28975             var positions = [];
28976             
28977             switch (box.length){
28978                 case 1 :
28979                     positions = this.getVerticalOneBoxColPositions(x, y, box);
28980                     break;
28981                 case 2 :
28982                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
28983                     break;
28984                 case 3 :
28985                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
28986                     break;
28987                 case 4 :
28988                     positions = this.getVerticalFourBoxColPositions(x, y, box);
28989                     break;
28990                 default :
28991                     break;
28992             }
28993             
28994             Roo.each(box, function(b,kk){
28995                 
28996                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
28997                 
28998                 var sz = b.el.getSize();
28999                 
29000                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29001                 
29002             }, this);
29003             
29004         }, this);
29005         
29006         var mY = 0;
29007         
29008         for (var i = 0; i < this.cols; i++){
29009             mY = Math.max(mY, maxY[i]);
29010         }
29011         
29012         this.el.setHeight(mY - pos.y);
29013         
29014     },
29015     
29016 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29017 //    {
29018 //        var pos = this.el.getBox(true);
29019 //        var x = pos.x;
29020 //        var y = pos.y;
29021 //        var maxX = pos.right;
29022 //        
29023 //        var maxHeight = 0;
29024 //        
29025 //        Roo.each(items, function(item, k){
29026 //            
29027 //            var c = k % 2;
29028 //            
29029 //            item.el.position('absolute');
29030 //                
29031 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29032 //
29033 //            item.el.setWidth(width);
29034 //
29035 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29036 //
29037 //            item.el.setHeight(height);
29038 //            
29039 //            if(c == 0){
29040 //                item.el.setXY([x, y], isInstant ? false : true);
29041 //            } else {
29042 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29043 //            }
29044 //            
29045 //            y = y + height + this.alternativePadWidth;
29046 //            
29047 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29048 //            
29049 //        }, this);
29050 //        
29051 //        this.el.setHeight(maxHeight);
29052 //        
29053 //    },
29054     
29055     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29056     {
29057         var pos = this.el.getBox(true);
29058         
29059         var minX = pos.x;
29060         var minY = pos.y;
29061         
29062         var maxX = pos.right;
29063         
29064         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29065         
29066         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29067         
29068         Roo.each(queue, function(box, k){
29069             
29070             Roo.each(box, function(b, kk){
29071                 
29072                 b.el.position('absolute');
29073                 
29074                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29075                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29076                 
29077                 if(b.size == 'md-left' || b.size == 'md-right'){
29078                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29079                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29080                 }
29081                 
29082                 b.el.setWidth(width);
29083                 b.el.setHeight(height);
29084                 
29085             }, this);
29086             
29087             if(!box.length){
29088                 return;
29089             }
29090             
29091             var positions = [];
29092             
29093             switch (box.length){
29094                 case 1 :
29095                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29096                     break;
29097                 case 2 :
29098                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29099                     break;
29100                 case 3 :
29101                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29102                     break;
29103                 case 4 :
29104                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29105                     break;
29106                 default :
29107                     break;
29108             }
29109             
29110             Roo.each(box, function(b,kk){
29111                 
29112                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29113                 
29114                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29115                 
29116             }, this);
29117             
29118         }, this);
29119         
29120     },
29121     
29122     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29123     {
29124         Roo.each(eItems, function(b,k){
29125             
29126             b.size = (k == 0) ? 'sm' : 'xs';
29127             b.x = (k == 0) ? 2 : 1;
29128             b.y = (k == 0) ? 2 : 1;
29129             
29130             b.el.position('absolute');
29131             
29132             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29133                 
29134             b.el.setWidth(width);
29135             
29136             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29137             
29138             b.el.setHeight(height);
29139             
29140         }, this);
29141
29142         var positions = [];
29143         
29144         positions.push({
29145             x : maxX - this.unitWidth * 2 - this.gutter,
29146             y : minY
29147         });
29148         
29149         positions.push({
29150             x : maxX - this.unitWidth,
29151             y : minY + (this.unitWidth + this.gutter) * 2
29152         });
29153         
29154         positions.push({
29155             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29156             y : minY
29157         });
29158         
29159         Roo.each(eItems, function(b,k){
29160             
29161             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29162
29163         }, this);
29164         
29165     },
29166     
29167     getVerticalOneBoxColPositions : function(x, y, box)
29168     {
29169         var pos = [];
29170         
29171         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29172         
29173         if(box[0].size == 'md-left'){
29174             rand = 0;
29175         }
29176         
29177         if(box[0].size == 'md-right'){
29178             rand = 1;
29179         }
29180         
29181         pos.push({
29182             x : x + (this.unitWidth + this.gutter) * rand,
29183             y : y
29184         });
29185         
29186         return pos;
29187     },
29188     
29189     getVerticalTwoBoxColPositions : function(x, y, box)
29190     {
29191         var pos = [];
29192         
29193         if(box[0].size == 'xs'){
29194             
29195             pos.push({
29196                 x : x,
29197                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29198             });
29199
29200             pos.push({
29201                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29202                 y : y
29203             });
29204             
29205             return pos;
29206             
29207         }
29208         
29209         pos.push({
29210             x : x,
29211             y : y
29212         });
29213
29214         pos.push({
29215             x : x + (this.unitWidth + this.gutter) * 2,
29216             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29217         });
29218         
29219         return pos;
29220         
29221     },
29222     
29223     getVerticalThreeBoxColPositions : function(x, y, box)
29224     {
29225         var pos = [];
29226         
29227         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29228             
29229             pos.push({
29230                 x : x,
29231                 y : y
29232             });
29233
29234             pos.push({
29235                 x : x + (this.unitWidth + this.gutter) * 1,
29236                 y : y
29237             });
29238             
29239             pos.push({
29240                 x : x + (this.unitWidth + this.gutter) * 2,
29241                 y : y
29242             });
29243             
29244             return pos;
29245             
29246         }
29247         
29248         if(box[0].size == 'xs' && box[1].size == 'xs'){
29249             
29250             pos.push({
29251                 x : x,
29252                 y : y
29253             });
29254
29255             pos.push({
29256                 x : x,
29257                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29258             });
29259             
29260             pos.push({
29261                 x : x + (this.unitWidth + this.gutter) * 1,
29262                 y : y
29263             });
29264             
29265             return pos;
29266             
29267         }
29268         
29269         pos.push({
29270             x : x,
29271             y : y
29272         });
29273
29274         pos.push({
29275             x : x + (this.unitWidth + this.gutter) * 2,
29276             y : y
29277         });
29278
29279         pos.push({
29280             x : x + (this.unitWidth + this.gutter) * 2,
29281             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29282         });
29283             
29284         return pos;
29285         
29286     },
29287     
29288     getVerticalFourBoxColPositions : function(x, y, box)
29289     {
29290         var pos = [];
29291         
29292         if(box[0].size == 'xs'){
29293             
29294             pos.push({
29295                 x : x,
29296                 y : y
29297             });
29298
29299             pos.push({
29300                 x : x,
29301                 y : y + (this.unitHeight + this.gutter) * 1
29302             });
29303             
29304             pos.push({
29305                 x : x,
29306                 y : y + (this.unitHeight + this.gutter) * 2
29307             });
29308             
29309             pos.push({
29310                 x : x + (this.unitWidth + this.gutter) * 1,
29311                 y : y
29312             });
29313             
29314             return pos;
29315             
29316         }
29317         
29318         pos.push({
29319             x : x,
29320             y : y
29321         });
29322
29323         pos.push({
29324             x : x + (this.unitWidth + this.gutter) * 2,
29325             y : y
29326         });
29327
29328         pos.push({
29329             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29330             y : y + (this.unitHeight + this.gutter) * 1
29331         });
29332
29333         pos.push({
29334             x : x + (this.unitWidth + this.gutter) * 2,
29335             y : y + (this.unitWidth + this.gutter) * 2
29336         });
29337
29338         return pos;
29339         
29340     },
29341     
29342     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29343     {
29344         var pos = [];
29345         
29346         if(box[0].size == 'md-left'){
29347             pos.push({
29348                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29349                 y : minY
29350             });
29351             
29352             return pos;
29353         }
29354         
29355         if(box[0].size == 'md-right'){
29356             pos.push({
29357                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29358                 y : minY + (this.unitWidth + this.gutter) * 1
29359             });
29360             
29361             return pos;
29362         }
29363         
29364         var rand = Math.floor(Math.random() * (4 - box[0].y));
29365         
29366         pos.push({
29367             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29368             y : minY + (this.unitWidth + this.gutter) * rand
29369         });
29370         
29371         return pos;
29372         
29373     },
29374     
29375     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29376     {
29377         var pos = [];
29378         
29379         if(box[0].size == 'xs'){
29380             
29381             pos.push({
29382                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29383                 y : minY
29384             });
29385
29386             pos.push({
29387                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29388                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29389             });
29390             
29391             return pos;
29392             
29393         }
29394         
29395         pos.push({
29396             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29397             y : minY
29398         });
29399
29400         pos.push({
29401             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29402             y : minY + (this.unitWidth + this.gutter) * 2
29403         });
29404         
29405         return pos;
29406         
29407     },
29408     
29409     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29410     {
29411         var pos = [];
29412         
29413         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29414             
29415             pos.push({
29416                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29417                 y : minY
29418             });
29419
29420             pos.push({
29421                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29422                 y : minY + (this.unitWidth + this.gutter) * 1
29423             });
29424             
29425             pos.push({
29426                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29427                 y : minY + (this.unitWidth + this.gutter) * 2
29428             });
29429             
29430             return pos;
29431             
29432         }
29433         
29434         if(box[0].size == 'xs' && box[1].size == 'xs'){
29435             
29436             pos.push({
29437                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29438                 y : minY
29439             });
29440
29441             pos.push({
29442                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29443                 y : minY
29444             });
29445             
29446             pos.push({
29447                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29448                 y : minY + (this.unitWidth + this.gutter) * 1
29449             });
29450             
29451             return pos;
29452             
29453         }
29454         
29455         pos.push({
29456             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29457             y : minY
29458         });
29459
29460         pos.push({
29461             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29462             y : minY + (this.unitWidth + this.gutter) * 2
29463         });
29464
29465         pos.push({
29466             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29467             y : minY + (this.unitWidth + this.gutter) * 2
29468         });
29469             
29470         return pos;
29471         
29472     },
29473     
29474     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29475     {
29476         var pos = [];
29477         
29478         if(box[0].size == 'xs'){
29479             
29480             pos.push({
29481                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29482                 y : minY
29483             });
29484
29485             pos.push({
29486                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29487                 y : minY
29488             });
29489             
29490             pos.push({
29491                 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),
29492                 y : minY
29493             });
29494             
29495             pos.push({
29496                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29497                 y : minY + (this.unitWidth + this.gutter) * 1
29498             });
29499             
29500             return pos;
29501             
29502         }
29503         
29504         pos.push({
29505             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29506             y : minY
29507         });
29508         
29509         pos.push({
29510             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29511             y : minY + (this.unitWidth + this.gutter) * 2
29512         });
29513         
29514         pos.push({
29515             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29516             y : minY + (this.unitWidth + this.gutter) * 2
29517         });
29518         
29519         pos.push({
29520             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),
29521             y : minY + (this.unitWidth + this.gutter) * 2
29522         });
29523
29524         return pos;
29525         
29526     }
29527     
29528 });
29529
29530  
29531
29532  /**
29533  *
29534  * This is based on 
29535  * http://masonry.desandro.com
29536  *
29537  * The idea is to render all the bricks based on vertical width...
29538  *
29539  * The original code extends 'outlayer' - we might need to use that....
29540  * 
29541  */
29542
29543
29544 /**
29545  * @class Roo.bootstrap.LayoutMasonryAuto
29546  * @extends Roo.bootstrap.Component
29547  * Bootstrap Layout Masonry class
29548  * 
29549  * @constructor
29550  * Create a new Element
29551  * @param {Object} config The config object
29552  */
29553
29554 Roo.bootstrap.LayoutMasonryAuto = function(config){
29555     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29556 };
29557
29558 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
29559     
29560       /**
29561      * @cfg {Boolean} isFitWidth  - resize the width..
29562      */   
29563     isFitWidth : false,  // options..
29564     /**
29565      * @cfg {Boolean} isOriginLeft = left align?
29566      */   
29567     isOriginLeft : true,
29568     /**
29569      * @cfg {Boolean} isOriginTop = top align?
29570      */   
29571     isOriginTop : false,
29572     /**
29573      * @cfg {Boolean} isLayoutInstant = no animation?
29574      */   
29575     isLayoutInstant : false, // needed?
29576     /**
29577      * @cfg {Boolean} isResizingContainer = not sure if this is used..
29578      */   
29579     isResizingContainer : true,
29580     /**
29581      * @cfg {Number} columnWidth  width of the columns 
29582      */   
29583     
29584     columnWidth : 0,
29585     /**
29586      * @cfg {Number} padHeight padding below box..
29587      */   
29588     
29589     padHeight : 10, 
29590     
29591     /**
29592      * @cfg {Boolean} isAutoInitial defalut true
29593      */   
29594     
29595     isAutoInitial : true, 
29596     
29597     // private?
29598     gutter : 0,
29599     
29600     containerWidth: 0,
29601     initialColumnWidth : 0,
29602     currentSize : null,
29603     
29604     colYs : null, // array.
29605     maxY : 0,
29606     padWidth: 10,
29607     
29608     
29609     tag: 'div',
29610     cls: '',
29611     bricks: null, //CompositeElement
29612     cols : 0, // array?
29613     // element : null, // wrapped now this.el
29614     _isLayoutInited : null, 
29615     
29616     
29617     getAutoCreate : function(){
29618         
29619         var cfg = {
29620             tag: this.tag,
29621             cls: 'blog-masonary-wrapper ' + this.cls,
29622             cn : {
29623                 cls : 'mas-boxes masonary'
29624             }
29625         };
29626         
29627         return cfg;
29628     },
29629     
29630     getChildContainer: function( )
29631     {
29632         if (this.boxesEl) {
29633             return this.boxesEl;
29634         }
29635         
29636         this.boxesEl = this.el.select('.mas-boxes').first();
29637         
29638         return this.boxesEl;
29639     },
29640     
29641     
29642     initEvents : function()
29643     {
29644         var _this = this;
29645         
29646         if(this.isAutoInitial){
29647             Roo.log('hook children rendered');
29648             this.on('childrenrendered', function() {
29649                 Roo.log('children rendered');
29650                 _this.initial();
29651             } ,this);
29652         }
29653         
29654     },
29655     
29656     initial : function()
29657     {
29658         this.reloadItems();
29659
29660         this.currentSize = this.el.getBox(true);
29661
29662         /// was window resize... - let's see if this works..
29663         Roo.EventManager.onWindowResize(this.resize, this); 
29664
29665         if(!this.isAutoInitial){
29666             this.layout();
29667             return;
29668         }
29669         
29670         this.layout.defer(500,this);
29671     },
29672     
29673     reloadItems: function()
29674     {
29675         this.bricks = this.el.select('.masonry-brick', true);
29676         
29677         this.bricks.each(function(b) {
29678             //Roo.log(b.getSize());
29679             if (!b.attr('originalwidth')) {
29680                 b.attr('originalwidth',  b.getSize().width);
29681             }
29682             
29683         });
29684         
29685         Roo.log(this.bricks.elements.length);
29686     },
29687     
29688     resize : function()
29689     {
29690         Roo.log('resize');
29691         var cs = this.el.getBox(true);
29692         
29693         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29694             Roo.log("no change in with or X");
29695             return;
29696         }
29697         this.currentSize = cs;
29698         this.layout();
29699     },
29700     
29701     layout : function()
29702     {
29703          Roo.log('layout');
29704         this._resetLayout();
29705         //this._manageStamps();
29706       
29707         // don't animate first layout
29708         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29709         this.layoutItems( isInstant );
29710       
29711         // flag for initalized
29712         this._isLayoutInited = true;
29713     },
29714     
29715     layoutItems : function( isInstant )
29716     {
29717         //var items = this._getItemsForLayout( this.items );
29718         // original code supports filtering layout items.. we just ignore it..
29719         
29720         this._layoutItems( this.bricks , isInstant );
29721       
29722         this._postLayout();
29723     },
29724     _layoutItems : function ( items , isInstant)
29725     {
29726        //this.fireEvent( 'layout', this, items );
29727     
29728
29729         if ( !items || !items.elements.length ) {
29730           // no items, emit event with empty array
29731             return;
29732         }
29733
29734         var queue = [];
29735         items.each(function(item) {
29736             Roo.log("layout item");
29737             Roo.log(item);
29738             // get x/y object from method
29739             var position = this._getItemLayoutPosition( item );
29740             // enqueue
29741             position.item = item;
29742             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29743             queue.push( position );
29744         }, this);
29745       
29746         this._processLayoutQueue( queue );
29747     },
29748     /** Sets position of item in DOM
29749     * @param {Element} item
29750     * @param {Number} x - horizontal position
29751     * @param {Number} y - vertical position
29752     * @param {Boolean} isInstant - disables transitions
29753     */
29754     _processLayoutQueue : function( queue )
29755     {
29756         for ( var i=0, len = queue.length; i < len; i++ ) {
29757             var obj = queue[i];
29758             obj.item.position('absolute');
29759             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29760         }
29761     },
29762       
29763     
29764     /**
29765     * Any logic you want to do after each layout,
29766     * i.e. size the container
29767     */
29768     _postLayout : function()
29769     {
29770         this.resizeContainer();
29771     },
29772     
29773     resizeContainer : function()
29774     {
29775         if ( !this.isResizingContainer ) {
29776             return;
29777         }
29778         var size = this._getContainerSize();
29779         if ( size ) {
29780             this.el.setSize(size.width,size.height);
29781             this.boxesEl.setSize(size.width,size.height);
29782         }
29783     },
29784     
29785     
29786     
29787     _resetLayout : function()
29788     {
29789         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
29790         this.colWidth = this.el.getWidth();
29791         //this.gutter = this.el.getWidth(); 
29792         
29793         this.measureColumns();
29794
29795         // reset column Y
29796         var i = this.cols;
29797         this.colYs = [];
29798         while (i--) {
29799             this.colYs.push( 0 );
29800         }
29801     
29802         this.maxY = 0;
29803     },
29804
29805     measureColumns : function()
29806     {
29807         this.getContainerWidth();
29808       // if columnWidth is 0, default to outerWidth of first item
29809         if ( !this.columnWidth ) {
29810             var firstItem = this.bricks.first();
29811             Roo.log(firstItem);
29812             this.columnWidth  = this.containerWidth;
29813             if (firstItem && firstItem.attr('originalwidth') ) {
29814                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
29815             }
29816             // columnWidth fall back to item of first element
29817             Roo.log("set column width?");
29818                         this.initialColumnWidth = this.columnWidth  ;
29819
29820             // if first elem has no width, default to size of container
29821             
29822         }
29823         
29824         
29825         if (this.initialColumnWidth) {
29826             this.columnWidth = this.initialColumnWidth;
29827         }
29828         
29829         
29830             
29831         // column width is fixed at the top - however if container width get's smaller we should
29832         // reduce it...
29833         
29834         // this bit calcs how man columns..
29835             
29836         var columnWidth = this.columnWidth += this.gutter;
29837       
29838         // calculate columns
29839         var containerWidth = this.containerWidth + this.gutter;
29840         
29841         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
29842         // fix rounding errors, typically with gutters
29843         var excess = columnWidth - containerWidth % columnWidth;
29844         
29845         
29846         // if overshoot is less than a pixel, round up, otherwise floor it
29847         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
29848         cols = Math[ mathMethod ]( cols );
29849         this.cols = Math.max( cols, 1 );
29850         
29851         
29852          // padding positioning..
29853         var totalColWidth = this.cols * this.columnWidth;
29854         var padavail = this.containerWidth - totalColWidth;
29855         // so for 2 columns - we need 3 'pads'
29856         
29857         var padNeeded = (1+this.cols) * this.padWidth;
29858         
29859         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
29860         
29861         this.columnWidth += padExtra
29862         //this.padWidth = Math.floor(padavail /  ( this.cols));
29863         
29864         // adjust colum width so that padding is fixed??
29865         
29866         // we have 3 columns ... total = width * 3
29867         // we have X left over... that should be used by 
29868         
29869         //if (this.expandC) {
29870             
29871         //}
29872         
29873         
29874         
29875     },
29876     
29877     getContainerWidth : function()
29878     {
29879        /* // container is parent if fit width
29880         var container = this.isFitWidth ? this.element.parentNode : this.element;
29881         // check that this.size and size are there
29882         // IE8 triggers resize on body size change, so they might not be
29883         
29884         var size = getSize( container );  //FIXME
29885         this.containerWidth = size && size.innerWidth; //FIXME
29886         */
29887          
29888         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29889         
29890     },
29891     
29892     _getItemLayoutPosition : function( item )  // what is item?
29893     {
29894         // we resize the item to our columnWidth..
29895       
29896         item.setWidth(this.columnWidth);
29897         item.autoBoxAdjust  = false;
29898         
29899         var sz = item.getSize();
29900  
29901         // how many columns does this brick span
29902         var remainder = this.containerWidth % this.columnWidth;
29903         
29904         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
29905         // round if off by 1 pixel, otherwise use ceil
29906         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
29907         colSpan = Math.min( colSpan, this.cols );
29908         
29909         // normally this should be '1' as we dont' currently allow multi width columns..
29910         
29911         var colGroup = this._getColGroup( colSpan );
29912         // get the minimum Y value from the columns
29913         var minimumY = Math.min.apply( Math, colGroup );
29914         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
29915         
29916         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
29917          
29918         // position the brick
29919         var position = {
29920             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
29921             y: this.currentSize.y + minimumY + this.padHeight
29922         };
29923         
29924         Roo.log(position);
29925         // apply setHeight to necessary columns
29926         var setHeight = minimumY + sz.height + this.padHeight;
29927         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
29928         
29929         var setSpan = this.cols + 1 - colGroup.length;
29930         for ( var i = 0; i < setSpan; i++ ) {
29931           this.colYs[ shortColIndex + i ] = setHeight ;
29932         }
29933       
29934         return position;
29935     },
29936     
29937     /**
29938      * @param {Number} colSpan - number of columns the element spans
29939      * @returns {Array} colGroup
29940      */
29941     _getColGroup : function( colSpan )
29942     {
29943         if ( colSpan < 2 ) {
29944           // if brick spans only one column, use all the column Ys
29945           return this.colYs;
29946         }
29947       
29948         var colGroup = [];
29949         // how many different places could this brick fit horizontally
29950         var groupCount = this.cols + 1 - colSpan;
29951         // for each group potential horizontal position
29952         for ( var i = 0; i < groupCount; i++ ) {
29953           // make an array of colY values for that one group
29954           var groupColYs = this.colYs.slice( i, i + colSpan );
29955           // and get the max value of the array
29956           colGroup[i] = Math.max.apply( Math, groupColYs );
29957         }
29958         return colGroup;
29959     },
29960     /*
29961     _manageStamp : function( stamp )
29962     {
29963         var stampSize =  stamp.getSize();
29964         var offset = stamp.getBox();
29965         // get the columns that this stamp affects
29966         var firstX = this.isOriginLeft ? offset.x : offset.right;
29967         var lastX = firstX + stampSize.width;
29968         var firstCol = Math.floor( firstX / this.columnWidth );
29969         firstCol = Math.max( 0, firstCol );
29970         
29971         var lastCol = Math.floor( lastX / this.columnWidth );
29972         // lastCol should not go over if multiple of columnWidth #425
29973         lastCol -= lastX % this.columnWidth ? 0 : 1;
29974         lastCol = Math.min( this.cols - 1, lastCol );
29975         
29976         // set colYs to bottom of the stamp
29977         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
29978             stampSize.height;
29979             
29980         for ( var i = firstCol; i <= lastCol; i++ ) {
29981           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
29982         }
29983     },
29984     */
29985     
29986     _getContainerSize : function()
29987     {
29988         this.maxY = Math.max.apply( Math, this.colYs );
29989         var size = {
29990             height: this.maxY
29991         };
29992       
29993         if ( this.isFitWidth ) {
29994             size.width = this._getContainerFitWidth();
29995         }
29996       
29997         return size;
29998     },
29999     
30000     _getContainerFitWidth : function()
30001     {
30002         var unusedCols = 0;
30003         // count unused columns
30004         var i = this.cols;
30005         while ( --i ) {
30006           if ( this.colYs[i] !== 0 ) {
30007             break;
30008           }
30009           unusedCols++;
30010         }
30011         // fit container to columns that have been used
30012         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30013     },
30014     
30015     needsResizeLayout : function()
30016     {
30017         var previousWidth = this.containerWidth;
30018         this.getContainerWidth();
30019         return previousWidth !== this.containerWidth;
30020     }
30021  
30022 });
30023
30024  
30025
30026  /*
30027  * - LGPL
30028  *
30029  * element
30030  * 
30031  */
30032
30033 /**
30034  * @class Roo.bootstrap.MasonryBrick
30035  * @extends Roo.bootstrap.Component
30036  * Bootstrap MasonryBrick class
30037  * 
30038  * @constructor
30039  * Create a new MasonryBrick
30040  * @param {Object} config The config object
30041  */
30042
30043 Roo.bootstrap.MasonryBrick = function(config){
30044     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30045     
30046     this.addEvents({
30047         // raw events
30048         /**
30049          * @event click
30050          * When a MasonryBrick is clcik
30051          * @param {Roo.bootstrap.MasonryBrick} this
30052          * @param {Roo.EventObject} e
30053          */
30054         "click" : true
30055     });
30056 };
30057
30058 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30059     
30060     /**
30061      * @cfg {String} title
30062      */   
30063     title : '',
30064     /**
30065      * @cfg {String} html
30066      */   
30067     html : '',
30068     /**
30069      * @cfg {String} bgimage
30070      */   
30071     bgimage : '',
30072     /**
30073      * @cfg {String} cls
30074      */   
30075     cls : '',
30076     /**
30077      * @cfg {String} href
30078      */   
30079     href : '',
30080     /**
30081      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30082      */   
30083     size : 'xs',
30084     
30085     /**
30086      * @cfg {String} (center|bottom) placetitle
30087      */   
30088     placetitle : '',
30089     
30090     getAutoCreate : function()
30091     {
30092         var cls = 'masonry-brick';
30093         
30094         if(this.href.length){
30095             cls += ' masonry-brick-link';
30096         }
30097         
30098         if(this.bgimage.length){
30099             cls += ' masonry-brick-image';
30100         }
30101         
30102         if(this.size){
30103             cls += ' masonry-' + this.size + '-brick';
30104         }
30105         
30106         if(this.placetitle.length){
30107             
30108             switch (this.placetitle) {
30109                 case 'center' :
30110                     cls += ' masonry-center-title';
30111                     break;
30112                 case 'bottom' :
30113                     cls += ' masonry-bottom-title';
30114                     break;
30115                 default:
30116                     break;
30117             }
30118             
30119         } else {
30120             if(!this.html.length && !this.bgimage.length){
30121                 cls += ' masonry-center-title';
30122             }
30123
30124             if(!this.html.length && this.bgimage.length){
30125                 cls += ' masonry-bottom-title';
30126             }
30127         }
30128         
30129         if(this.cls){
30130             cls += ' ' + this.cls;
30131         }
30132         
30133         var cfg = {
30134             tag: (this.href.length) ? 'a' : 'div',
30135             cls: cls,
30136             cn: [
30137                 {
30138                     tag: 'div',
30139                     cls: 'masonry-brick-paragraph',
30140                     cn: []
30141                 }
30142             ]
30143         };
30144         
30145         if(this.href.length){
30146             cfg.href = this.href;
30147         }
30148         
30149         var cn = cfg.cn[0].cn;
30150         
30151         if(this.title.length){
30152             cn.push({
30153                 tag: 'h4',
30154                 cls: 'masonry-brick-title',
30155                 html: this.title
30156             });
30157         }
30158         
30159         if(this.html.length){
30160             cn.push({
30161                 tag: 'p',
30162                 cls: 'masonry-brick-text',
30163                 html: this.html
30164             });
30165         }
30166         
30167         if(this.bgimage.length){
30168             cfg.cn.push({
30169                 tag: 'img',
30170                 cls: 'masonry-brick-image-view',
30171                 src: this.bgimage
30172             });
30173         }
30174         
30175         return cfg;
30176         
30177     },
30178     
30179     initEvents: function() 
30180     {
30181         switch (this.size) {
30182             case 'xs' :
30183 //                this.intSize = 1;
30184                 this.x = 1;
30185                 this.y = 1;
30186                 break;
30187             case 'sm' :
30188 //                this.intSize = 2;
30189                 this.x = 2;
30190                 this.y = 2;
30191                 break;
30192             case 'md' :
30193             case 'md-left' :
30194             case 'md-right' :
30195 //                this.intSize = 3;
30196                 this.x = 3;
30197                 this.y = 3;
30198                 break;
30199             case 'tall' :
30200 //                this.intSize = 3;
30201                 this.x = 2;
30202                 this.y = 3;
30203                 break;
30204             case 'wide' :
30205 //                this.intSize = 3;
30206                 this.x = 3;
30207                 this.y = 2;
30208                 break;
30209             case 'wide-thin' :
30210 //                this.intSize = 3;
30211                 this.x = 3;
30212                 this.y = 1;
30213                 break;
30214                         
30215             default :
30216                 break;
30217         }
30218         
30219         
30220         
30221         if(Roo.isTouch){
30222             this.el.on('touchstart', this.onTouchStart, this);
30223             this.el.on('touchmove', this.onTouchMove, this);
30224             this.el.on('touchend', this.onTouchEnd, this);
30225         } else {
30226             this.el.on('mouseenter'  ,this.enter, this);
30227             this.el.on('mouseleave', this.leave, this);
30228         }
30229         
30230         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30231             this.parent().bricks.push(this);   
30232         }
30233         
30234     },
30235     
30236     onClick: function(e, el)
30237     {
30238         alert('click');
30239         
30240         if(!Roo.isTouch){
30241             return;
30242         }
30243         
30244         var time = this.endTimer - this.startTimer;
30245         
30246         alert(time);
30247         
30248         if(time < 1000){
30249             return;
30250         }
30251         
30252         e.preventDefault();
30253     },
30254     
30255     enter: function(e, el)
30256     {
30257         e.preventDefault();
30258         
30259         if(this.bgimage.length && this.html.length){
30260             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30261         }
30262     },
30263     
30264     leave: function(e, el)
30265     {
30266         e.preventDefault();
30267         
30268         if(this.bgimage.length && this.html.length){
30269             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30270         }
30271     },
30272     
30273     onTouchStart: function(e, el)
30274     {
30275 //        e.preventDefault();
30276         
30277         if(!this.bgimage.length || !this.html.length){
30278             return;
30279         }
30280         
30281         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30282         
30283         this.timer = new Date().getTime();
30284         
30285         this.touchmoved = false;
30286     },
30287     
30288     onTouchMove: function(e, el)
30289     {
30290         this.touchmoved = true;
30291     },
30292     
30293     onTouchEnd: function(e, el)
30294     {
30295 //        e.preventDefault();
30296         
30297         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30298             return;
30299         }
30300         
30301         if(!this.bgimage.length || !this.html.length){
30302             
30303             if(this.href.length){
30304                 window.location.href = this.href;
30305             }
30306             
30307             return;
30308         }
30309         
30310         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30311         
30312         window.location.href = this.href;
30313     }
30314     
30315 });
30316
30317  
30318
30319  /*
30320  * - LGPL
30321  *
30322  * element
30323  * 
30324  */
30325
30326 /**
30327  * @class Roo.bootstrap.Brick
30328  * @extends Roo.bootstrap.Component
30329  * Bootstrap Brick class
30330  * 
30331  * @constructor
30332  * Create a new Brick
30333  * @param {Object} config The config object
30334  */
30335
30336 Roo.bootstrap.Brick = function(config){
30337     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30338     
30339     this.addEvents({
30340         // raw events
30341         /**
30342          * @event click
30343          * When a Brick is click
30344          * @param {Roo.bootstrap.Brick} this
30345          * @param {Roo.EventObject} e
30346          */
30347         "click" : true
30348     });
30349 };
30350
30351 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30352     
30353     /**
30354      * @cfg {String} title
30355      */   
30356     title : '',
30357     /**
30358      * @cfg {String} html
30359      */   
30360     html : '',
30361     /**
30362      * @cfg {String} bgimage
30363      */   
30364     bgimage : '',
30365     /**
30366      * @cfg {String} cls
30367      */   
30368     cls : '',
30369     /**
30370      * @cfg {String} href
30371      */   
30372     href : '',
30373     /**
30374      * @cfg {String} video
30375      */   
30376     video : '',
30377     /**
30378      * @cfg {Boolean} square
30379      */   
30380     square : true,
30381     
30382     getAutoCreate : function()
30383     {
30384         var cls = 'roo-brick';
30385         
30386         if(this.href.length){
30387             cls += ' roo-brick-link';
30388         }
30389         
30390         if(this.bgimage.length){
30391             cls += ' roo-brick-image';
30392         }
30393         
30394         if(!this.html.length && !this.bgimage.length){
30395             cls += ' roo-brick-center-title';
30396         }
30397         
30398         if(!this.html.length && this.bgimage.length){
30399             cls += ' roo-brick-bottom-title';
30400         }
30401         
30402         if(this.cls){
30403             cls += ' ' + this.cls;
30404         }
30405         
30406         var cfg = {
30407             tag: (this.href.length) ? 'a' : 'div',
30408             cls: cls,
30409             cn: [
30410                 {
30411                     tag: 'div',
30412                     cls: 'roo-brick-paragraph',
30413                     cn: []
30414                 }
30415             ]
30416         };
30417         
30418         if(this.href.length){
30419             cfg.href = this.href;
30420         }
30421         
30422         var cn = cfg.cn[0].cn;
30423         
30424         if(this.title.length){
30425             cn.push({
30426                 tag: 'h4',
30427                 cls: 'roo-brick-title',
30428                 html: this.title
30429             });
30430         }
30431         
30432         if(this.html.length){
30433             cn.push({
30434                 tag: 'p',
30435                 cls: 'roo-brick-text',
30436                 html: this.html
30437             });
30438         }
30439         
30440         if(this.bgimage.length){
30441             cfg.cn.push({
30442                 tag: 'img',
30443                 cls: 'roo-brick-image-view',
30444                 src: this.bgimage
30445             });
30446         }
30447         
30448         return cfg;
30449     },
30450     
30451     initEvents: function() 
30452     {
30453         if(this.title.length || this.html.length){
30454             this.el.on('mouseenter'  ,this.enter, this);
30455             this.el.on('mouseleave', this.leave, this);
30456         }
30457         
30458         
30459         Roo.EventManager.onWindowResize(this.resize, this); 
30460         
30461         this.resize();
30462     },
30463     
30464     resize : function()
30465     {
30466         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30467         
30468         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30469 //        paragraph.setHeight(paragraph.getWidth());
30470         
30471         if(this.bgimage.length){
30472             var image = this.el.select('.roo-brick-image-view', true).first();
30473             image.setWidth(paragraph.getWidth());
30474             image.setHeight(paragraph.getWidth());
30475         }
30476         
30477     },
30478     
30479     enter: function(e, el)
30480     {
30481         e.preventDefault();
30482         
30483         if(this.bgimage.length){
30484             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30485             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30486         }
30487     },
30488     
30489     leave: function(e, el)
30490     {
30491         e.preventDefault();
30492         
30493         if(this.bgimage.length){
30494             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30495             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30496         }
30497     }
30498     
30499 });
30500
30501  
30502
30503  /*
30504  * Based on:
30505  * Ext JS Library 1.1.1
30506  * Copyright(c) 2006-2007, Ext JS, LLC.
30507  *
30508  * Originally Released Under LGPL - original licence link has changed is not relivant.
30509  *
30510  * Fork - LGPL
30511  * <script type="text/javascript">
30512  */
30513
30514
30515 /**
30516  * @class Roo.bootstrap.SplitBar
30517  * @extends Roo.util.Observable
30518  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30519  * <br><br>
30520  * Usage:
30521  * <pre><code>
30522 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30523                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30524 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30525 split.minSize = 100;
30526 split.maxSize = 600;
30527 split.animate = true;
30528 split.on('moved', splitterMoved);
30529 </code></pre>
30530  * @constructor
30531  * Create a new SplitBar
30532  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
30533  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
30534  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30535  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
30536                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30537                         position of the SplitBar).
30538  */
30539 Roo.bootstrap.SplitBar = function(cfg){
30540     
30541     /** @private */
30542     
30543     //{
30544     //  dragElement : elm
30545     //  resizingElement: el,
30546         // optional..
30547     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30548     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
30549         // existingProxy ???
30550     //}
30551     
30552     this.el = Roo.get(cfg.dragElement, true);
30553     this.el.dom.unselectable = "on";
30554     /** @private */
30555     this.resizingEl = Roo.get(cfg.resizingElement, true);
30556
30557     /**
30558      * @private
30559      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30560      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30561      * @type Number
30562      */
30563     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30564     
30565     /**
30566      * The minimum size of the resizing element. (Defaults to 0)
30567      * @type Number
30568      */
30569     this.minSize = 0;
30570     
30571     /**
30572      * The maximum size of the resizing element. (Defaults to 2000)
30573      * @type Number
30574      */
30575     this.maxSize = 2000;
30576     
30577     /**
30578      * Whether to animate the transition to the new size
30579      * @type Boolean
30580      */
30581     this.animate = false;
30582     
30583     /**
30584      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30585      * @type Boolean
30586      */
30587     this.useShim = false;
30588     
30589     /** @private */
30590     this.shim = null;
30591     
30592     if(!cfg.existingProxy){
30593         /** @private */
30594         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30595     }else{
30596         this.proxy = Roo.get(cfg.existingProxy).dom;
30597     }
30598     /** @private */
30599     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30600     
30601     /** @private */
30602     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30603     
30604     /** @private */
30605     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30606     
30607     /** @private */
30608     this.dragSpecs = {};
30609     
30610     /**
30611      * @private The adapter to use to positon and resize elements
30612      */
30613     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30614     this.adapter.init(this);
30615     
30616     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30617         /** @private */
30618         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30619         this.el.addClass("roo-splitbar-h");
30620     }else{
30621         /** @private */
30622         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30623         this.el.addClass("roo-splitbar-v");
30624     }
30625     
30626     this.addEvents({
30627         /**
30628          * @event resize
30629          * Fires when the splitter is moved (alias for {@link #event-moved})
30630          * @param {Roo.bootstrap.SplitBar} this
30631          * @param {Number} newSize the new width or height
30632          */
30633         "resize" : true,
30634         /**
30635          * @event moved
30636          * Fires when the splitter is moved
30637          * @param {Roo.bootstrap.SplitBar} this
30638          * @param {Number} newSize the new width or height
30639          */
30640         "moved" : true,
30641         /**
30642          * @event beforeresize
30643          * Fires before the splitter is dragged
30644          * @param {Roo.bootstrap.SplitBar} this
30645          */
30646         "beforeresize" : true,
30647
30648         "beforeapply" : true
30649     });
30650
30651     Roo.util.Observable.call(this);
30652 };
30653
30654 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30655     onStartProxyDrag : function(x, y){
30656         this.fireEvent("beforeresize", this);
30657         if(!this.overlay){
30658             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
30659             o.unselectable();
30660             o.enableDisplayMode("block");
30661             // all splitbars share the same overlay
30662             Roo.bootstrap.SplitBar.prototype.overlay = o;
30663         }
30664         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30665         this.overlay.show();
30666         Roo.get(this.proxy).setDisplayed("block");
30667         var size = this.adapter.getElementSize(this);
30668         this.activeMinSize = this.getMinimumSize();;
30669         this.activeMaxSize = this.getMaximumSize();;
30670         var c1 = size - this.activeMinSize;
30671         var c2 = Math.max(this.activeMaxSize - size, 0);
30672         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30673             this.dd.resetConstraints();
30674             this.dd.setXConstraint(
30675                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
30676                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30677             );
30678             this.dd.setYConstraint(0, 0);
30679         }else{
30680             this.dd.resetConstraints();
30681             this.dd.setXConstraint(0, 0);
30682             this.dd.setYConstraint(
30683                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
30684                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30685             );
30686          }
30687         this.dragSpecs.startSize = size;
30688         this.dragSpecs.startPoint = [x, y];
30689         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30690     },
30691     
30692     /** 
30693      * @private Called after the drag operation by the DDProxy
30694      */
30695     onEndProxyDrag : function(e){
30696         Roo.get(this.proxy).setDisplayed(false);
30697         var endPoint = Roo.lib.Event.getXY(e);
30698         if(this.overlay){
30699             this.overlay.hide();
30700         }
30701         var newSize;
30702         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30703             newSize = this.dragSpecs.startSize + 
30704                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30705                     endPoint[0] - this.dragSpecs.startPoint[0] :
30706                     this.dragSpecs.startPoint[0] - endPoint[0]
30707                 );
30708         }else{
30709             newSize = this.dragSpecs.startSize + 
30710                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30711                     endPoint[1] - this.dragSpecs.startPoint[1] :
30712                     this.dragSpecs.startPoint[1] - endPoint[1]
30713                 );
30714         }
30715         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30716         if(newSize != this.dragSpecs.startSize){
30717             if(this.fireEvent('beforeapply', this, newSize) !== false){
30718                 this.adapter.setElementSize(this, newSize);
30719                 this.fireEvent("moved", this, newSize);
30720                 this.fireEvent("resize", this, newSize);
30721             }
30722         }
30723     },
30724     
30725     /**
30726      * Get the adapter this SplitBar uses
30727      * @return The adapter object
30728      */
30729     getAdapter : function(){
30730         return this.adapter;
30731     },
30732     
30733     /**
30734      * Set the adapter this SplitBar uses
30735      * @param {Object} adapter A SplitBar adapter object
30736      */
30737     setAdapter : function(adapter){
30738         this.adapter = adapter;
30739         this.adapter.init(this);
30740     },
30741     
30742     /**
30743      * Gets the minimum size for the resizing element
30744      * @return {Number} The minimum size
30745      */
30746     getMinimumSize : function(){
30747         return this.minSize;
30748     },
30749     
30750     /**
30751      * Sets the minimum size for the resizing element
30752      * @param {Number} minSize The minimum size
30753      */
30754     setMinimumSize : function(minSize){
30755         this.minSize = minSize;
30756     },
30757     
30758     /**
30759      * Gets the maximum size for the resizing element
30760      * @return {Number} The maximum size
30761      */
30762     getMaximumSize : function(){
30763         return this.maxSize;
30764     },
30765     
30766     /**
30767      * Sets the maximum size for the resizing element
30768      * @param {Number} maxSize The maximum size
30769      */
30770     setMaximumSize : function(maxSize){
30771         this.maxSize = maxSize;
30772     },
30773     
30774     /**
30775      * Sets the initialize size for the resizing element
30776      * @param {Number} size The initial size
30777      */
30778     setCurrentSize : function(size){
30779         var oldAnimate = this.animate;
30780         this.animate = false;
30781         this.adapter.setElementSize(this, size);
30782         this.animate = oldAnimate;
30783     },
30784     
30785     /**
30786      * Destroy this splitbar. 
30787      * @param {Boolean} removeEl True to remove the element
30788      */
30789     destroy : function(removeEl){
30790         if(this.shim){
30791             this.shim.remove();
30792         }
30793         this.dd.unreg();
30794         this.proxy.parentNode.removeChild(this.proxy);
30795         if(removeEl){
30796             this.el.remove();
30797         }
30798     }
30799 });
30800
30801 /**
30802  * @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.
30803  */
30804 Roo.bootstrap.SplitBar.createProxy = function(dir){
30805     var proxy = new Roo.Element(document.createElement("div"));
30806     proxy.unselectable();
30807     var cls = 'roo-splitbar-proxy';
30808     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
30809     document.body.appendChild(proxy.dom);
30810     return proxy.dom;
30811 };
30812
30813 /** 
30814  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
30815  * Default Adapter. It assumes the splitter and resizing element are not positioned
30816  * elements and only gets/sets the width of the element. Generally used for table based layouts.
30817  */
30818 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
30819 };
30820
30821 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
30822     // do nothing for now
30823     init : function(s){
30824     
30825     },
30826     /**
30827      * Called before drag operations to get the current size of the resizing element. 
30828      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30829      */
30830      getElementSize : function(s){
30831         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30832             return s.resizingEl.getWidth();
30833         }else{
30834             return s.resizingEl.getHeight();
30835         }
30836     },
30837     
30838     /**
30839      * Called after drag operations to set the size of the resizing element.
30840      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30841      * @param {Number} newSize The new size to set
30842      * @param {Function} onComplete A function to be invoked when resizing is complete
30843      */
30844     setElementSize : function(s, newSize, onComplete){
30845         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30846             if(!s.animate){
30847                 s.resizingEl.setWidth(newSize);
30848                 if(onComplete){
30849                     onComplete(s, newSize);
30850                 }
30851             }else{
30852                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
30853             }
30854         }else{
30855             
30856             if(!s.animate){
30857                 s.resizingEl.setHeight(newSize);
30858                 if(onComplete){
30859                     onComplete(s, newSize);
30860                 }
30861             }else{
30862                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
30863             }
30864         }
30865     }
30866 };
30867
30868 /** 
30869  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
30870  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
30871  * Adapter that  moves the splitter element to align with the resized sizing element. 
30872  * Used with an absolute positioned SplitBar.
30873  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
30874  * document.body, make sure you assign an id to the body element.
30875  */
30876 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
30877     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30878     this.container = Roo.get(container);
30879 };
30880
30881 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
30882     init : function(s){
30883         this.basic.init(s);
30884     },
30885     
30886     getElementSize : function(s){
30887         return this.basic.getElementSize(s);
30888     },
30889     
30890     setElementSize : function(s, newSize, onComplete){
30891         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
30892     },
30893     
30894     moveSplitter : function(s){
30895         var yes = Roo.bootstrap.SplitBar;
30896         switch(s.placement){
30897             case yes.LEFT:
30898                 s.el.setX(s.resizingEl.getRight());
30899                 break;
30900             case yes.RIGHT:
30901                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
30902                 break;
30903             case yes.TOP:
30904                 s.el.setY(s.resizingEl.getBottom());
30905                 break;
30906             case yes.BOTTOM:
30907                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
30908                 break;
30909         }
30910     }
30911 };
30912
30913 /**
30914  * Orientation constant - Create a vertical SplitBar
30915  * @static
30916  * @type Number
30917  */
30918 Roo.bootstrap.SplitBar.VERTICAL = 1;
30919
30920 /**
30921  * Orientation constant - Create a horizontal SplitBar
30922  * @static
30923  * @type Number
30924  */
30925 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
30926
30927 /**
30928  * Placement constant - The resizing element is to the left of the splitter element
30929  * @static
30930  * @type Number
30931  */
30932 Roo.bootstrap.SplitBar.LEFT = 1;
30933
30934 /**
30935  * Placement constant - The resizing element is to the right of the splitter element
30936  * @static
30937  * @type Number
30938  */
30939 Roo.bootstrap.SplitBar.RIGHT = 2;
30940
30941 /**
30942  * Placement constant - The resizing element is positioned above the splitter element
30943  * @static
30944  * @type Number
30945  */
30946 Roo.bootstrap.SplitBar.TOP = 3;
30947
30948 /**
30949  * Placement constant - The resizing element is positioned under splitter element
30950  * @static
30951  * @type Number
30952  */
30953 Roo.bootstrap.SplitBar.BOTTOM = 4;
30954 Roo.namespace("Roo.bootstrap.layout");/*
30955  * Based on:
30956  * Ext JS Library 1.1.1
30957  * Copyright(c) 2006-2007, Ext JS, LLC.
30958  *
30959  * Originally Released Under LGPL - original licence link has changed is not relivant.
30960  *
30961  * Fork - LGPL
30962  * <script type="text/javascript">
30963  */
30964  
30965 /**
30966  * @class Roo.bootstrap.layout.Manager
30967  * @extends Roo.bootstrap.Component
30968  * Base class for layout managers.
30969  */
30970 Roo.bootstrap.layout.Manager = function(config)
30971 {
30972     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
30973     this.el = Roo.get(config.el);
30974     // ie scrollbar fix
30975     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30976         document.body.scroll = "no";
30977     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30978         this.el.position('relative');
30979     }
30980     
30981     this.id = this.el.id;
30982     this.el.addClass("roo-layout-container");
30983     /** false to disable window resize monitoring @type Boolean */
30984     this.monitorWindowResize = true;
30985     this.regions = {};
30986     this.addEvents({
30987         /**
30988          * @event layout
30989          * Fires when a layout is performed. 
30990          * @param {Roo.LayoutManager} this
30991          */
30992         "layout" : true,
30993         /**
30994          * @event regionresized
30995          * Fires when the user resizes a region. 
30996          * @param {Roo.LayoutRegion} region The resized region
30997          * @param {Number} newSize The new size (width for east/west, height for north/south)
30998          */
30999         "regionresized" : true,
31000         /**
31001          * @event regioncollapsed
31002          * Fires when a region is collapsed. 
31003          * @param {Roo.LayoutRegion} region The collapsed region
31004          */
31005         "regioncollapsed" : true,
31006         /**
31007          * @event regionexpanded
31008          * Fires when a region is expanded.  
31009          * @param {Roo.LayoutRegion} region The expanded region
31010          */
31011         "regionexpanded" : true
31012     });
31013     this.updating = false;
31014     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31015 };
31016
31017 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31018     
31019     
31020     regions : null,
31021     
31022     monitorWindowResize : true,
31023     
31024     
31025     updating : false,
31026     
31027     /**
31028      * Returns true if this layout is currently being updated
31029      * @return {Boolean}
31030      */
31031     isUpdating : function(){
31032         return this.updating; 
31033     },
31034     
31035     /**
31036      * Suspend the LayoutManager from doing auto-layouts while
31037      * making multiple add or remove calls
31038      */
31039     beginUpdate : function(){
31040         this.updating = true;    
31041     },
31042     
31043     /**
31044      * Restore auto-layouts and optionally disable the manager from performing a layout
31045      * @param {Boolean} noLayout true to disable a layout update 
31046      */
31047     endUpdate : function(noLayout){
31048         this.updating = false;
31049         if(!noLayout){
31050             this.layout();
31051         }    
31052     },
31053     
31054     layout: function(){
31055         // abstract...
31056     },
31057     
31058     onRegionResized : function(region, newSize){
31059         this.fireEvent("regionresized", region, newSize);
31060         this.layout();
31061     },
31062     
31063     onRegionCollapsed : function(region){
31064         this.fireEvent("regioncollapsed", region);
31065     },
31066     
31067     onRegionExpanded : function(region){
31068         this.fireEvent("regionexpanded", region);
31069     },
31070         
31071     /**
31072      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31073      * performs box-model adjustments.
31074      * @return {Object} The size as an object {width: (the width), height: (the height)}
31075      */
31076     getViewSize : function()
31077     {
31078         var size;
31079         if(this.el.dom != document.body){
31080             size = this.el.getSize();
31081         }else{
31082             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31083         }
31084         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31085         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31086         return size;
31087     },
31088     
31089     /**
31090      * Returns the Element this layout is bound to.
31091      * @return {Roo.Element}
31092      */
31093     getEl : function(){
31094         return this.el;
31095     },
31096     
31097     /**
31098      * Returns the specified region.
31099      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31100      * @return {Roo.LayoutRegion}
31101      */
31102     getRegion : function(target){
31103         return this.regions[target.toLowerCase()];
31104     },
31105     
31106     onWindowResize : function(){
31107         if(this.monitorWindowResize){
31108             this.layout();
31109         }
31110     }
31111 });/*
31112  * Based on:
31113  * Ext JS Library 1.1.1
31114  * Copyright(c) 2006-2007, Ext JS, LLC.
31115  *
31116  * Originally Released Under LGPL - original licence link has changed is not relivant.
31117  *
31118  * Fork - LGPL
31119  * <script type="text/javascript">
31120  */
31121 /**
31122  * @class Roo.bootstrap.layout.Border
31123  * @extends Roo.bootstrap.layout.Manager
31124  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31125  * please see: examples/bootstrap/nested.html<br><br>
31126  
31127 <b>The container the layout is rendered into can be either the body element or any other element.
31128 If it is not the body element, the container needs to either be an absolute positioned element,
31129 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31130 the container size if it is not the body element.</b>
31131
31132 * @constructor
31133 * Create a new Border
31134 * @param {Object} config Configuration options
31135  */
31136 Roo.bootstrap.layout.Border = function(config){
31137     config = config || {};
31138     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31139     
31140     
31141     
31142     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31143         if(config[region]){
31144             config[region].region = region;
31145             this.addRegion(config[region]);
31146         }
31147     },this);
31148     
31149 };
31150
31151 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31152
31153 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31154     /**
31155      * Creates and adds a new region if it doesn't already exist.
31156      * @param {String} target The target region key (north, south, east, west or center).
31157      * @param {Object} config The regions config object
31158      * @return {BorderLayoutRegion} The new region
31159      */
31160     addRegion : function(config)
31161     {
31162         if(!this.regions[config.region]){
31163             var r = this.factory(config);
31164             this.bindRegion(r);
31165         }
31166         return this.regions[config.region];
31167     },
31168
31169     // private (kinda)
31170     bindRegion : function(r){
31171         this.regions[r.config.region] = r;
31172         
31173         r.on("visibilitychange",    this.layout, this);
31174         r.on("paneladded",          this.layout, this);
31175         r.on("panelremoved",        this.layout, this);
31176         r.on("invalidated",         this.layout, this);
31177         r.on("resized",             this.onRegionResized, this);
31178         r.on("collapsed",           this.onRegionCollapsed, this);
31179         r.on("expanded",            this.onRegionExpanded, this);
31180     },
31181
31182     /**
31183      * Performs a layout update.
31184      */
31185     layout : function()
31186     {
31187         if(this.updating) {
31188             return;
31189         }
31190         var size = this.getViewSize();
31191         var w = size.width;
31192         var h = size.height;
31193         var centerW = w;
31194         var centerH = h;
31195         var centerY = 0;
31196         var centerX = 0;
31197         //var x = 0, y = 0;
31198
31199         var rs = this.regions;
31200         var north = rs["north"];
31201         var south = rs["south"]; 
31202         var west = rs["west"];
31203         var east = rs["east"];
31204         var center = rs["center"];
31205         //if(this.hideOnLayout){ // not supported anymore
31206             //c.el.setStyle("display", "none");
31207         //}
31208         if(north && north.isVisible()){
31209             var b = north.getBox();
31210             var m = north.getMargins();
31211             b.width = w - (m.left+m.right);
31212             b.x = m.left;
31213             b.y = m.top;
31214             centerY = b.height + b.y + m.bottom;
31215             centerH -= centerY;
31216             north.updateBox(this.safeBox(b));
31217         }
31218         if(south && south.isVisible()){
31219             var b = south.getBox();
31220             var m = south.getMargins();
31221             b.width = w - (m.left+m.right);
31222             b.x = m.left;
31223             var totalHeight = (b.height + m.top + m.bottom);
31224             b.y = h - totalHeight + m.top;
31225             centerH -= totalHeight;
31226             south.updateBox(this.safeBox(b));
31227         }
31228         if(west && west.isVisible()){
31229             var b = west.getBox();
31230             var m = west.getMargins();
31231             b.height = centerH - (m.top+m.bottom);
31232             b.x = m.left;
31233             b.y = centerY + m.top;
31234             var totalWidth = (b.width + m.left + m.right);
31235             centerX += totalWidth;
31236             centerW -= totalWidth;
31237             west.updateBox(this.safeBox(b));
31238         }
31239         if(east && east.isVisible()){
31240             var b = east.getBox();
31241             var m = east.getMargins();
31242             b.height = centerH - (m.top+m.bottom);
31243             var totalWidth = (b.width + m.left + m.right);
31244             b.x = w - totalWidth + m.left;
31245             b.y = centerY + m.top;
31246             centerW -= totalWidth;
31247             east.updateBox(this.safeBox(b));
31248         }
31249         if(center){
31250             var m = center.getMargins();
31251             var centerBox = {
31252                 x: centerX + m.left,
31253                 y: centerY + m.top,
31254                 width: centerW - (m.left+m.right),
31255                 height: centerH - (m.top+m.bottom)
31256             };
31257             //if(this.hideOnLayout){
31258                 //center.el.setStyle("display", "block");
31259             //}
31260             center.updateBox(this.safeBox(centerBox));
31261         }
31262         this.el.repaint();
31263         this.fireEvent("layout", this);
31264     },
31265
31266     // private
31267     safeBox : function(box){
31268         box.width = Math.max(0, box.width);
31269         box.height = Math.max(0, box.height);
31270         return box;
31271     },
31272
31273     /**
31274      * Adds a ContentPanel (or subclass) to this layout.
31275      * @param {String} target The target region key (north, south, east, west or center).
31276      * @param {Roo.ContentPanel} panel The panel to add
31277      * @return {Roo.ContentPanel} The added panel
31278      */
31279     add : function(target, panel){
31280          
31281         target = target.toLowerCase();
31282         return this.regions[target].add(panel);
31283     },
31284
31285     /**
31286      * Remove a ContentPanel (or subclass) to this layout.
31287      * @param {String} target The target region key (north, south, east, west or center).
31288      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31289      * @return {Roo.ContentPanel} The removed panel
31290      */
31291     remove : function(target, panel){
31292         target = target.toLowerCase();
31293         return this.regions[target].remove(panel);
31294     },
31295
31296     /**
31297      * Searches all regions for a panel with the specified id
31298      * @param {String} panelId
31299      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31300      */
31301     findPanel : function(panelId){
31302         var rs = this.regions;
31303         for(var target in rs){
31304             if(typeof rs[target] != "function"){
31305                 var p = rs[target].getPanel(panelId);
31306                 if(p){
31307                     return p;
31308                 }
31309             }
31310         }
31311         return null;
31312     },
31313
31314     /**
31315      * Searches all regions for a panel with the specified id and activates (shows) it.
31316      * @param {String/ContentPanel} panelId The panels id or the panel itself
31317      * @return {Roo.ContentPanel} The shown panel or null
31318      */
31319     showPanel : function(panelId) {
31320       var rs = this.regions;
31321       for(var target in rs){
31322          var r = rs[target];
31323          if(typeof r != "function"){
31324             if(r.hasPanel(panelId)){
31325                return r.showPanel(panelId);
31326             }
31327          }
31328       }
31329       return null;
31330    },
31331
31332    /**
31333      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31334      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31335      */
31336    /*
31337     restoreState : function(provider){
31338         if(!provider){
31339             provider = Roo.state.Manager;
31340         }
31341         var sm = new Roo.LayoutStateManager();
31342         sm.init(this, provider);
31343     },
31344 */
31345  
31346  
31347     /**
31348      * Adds a xtype elements to the layout.
31349      * <pre><code>
31350
31351 layout.addxtype({
31352        xtype : 'ContentPanel',
31353        region: 'west',
31354        items: [ .... ]
31355    }
31356 );
31357
31358 layout.addxtype({
31359         xtype : 'NestedLayoutPanel',
31360         region: 'west',
31361         layout: {
31362            center: { },
31363            west: { }   
31364         },
31365         items : [ ... list of content panels or nested layout panels.. ]
31366    }
31367 );
31368 </code></pre>
31369      * @param {Object} cfg Xtype definition of item to add.
31370      */
31371     addxtype : function(cfg)
31372     {
31373         // basically accepts a pannel...
31374         // can accept a layout region..!?!?
31375         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31376         
31377         
31378         // theory?  children can only be panels??
31379         
31380         //if (!cfg.xtype.match(/Panel$/)) {
31381         //    return false;
31382         //}
31383         var ret = false;
31384         
31385         if (typeof(cfg.region) == 'undefined') {
31386             Roo.log("Failed to add Panel, region was not set");
31387             Roo.log(cfg);
31388             return false;
31389         }
31390         var region = cfg.region;
31391         delete cfg.region;
31392         
31393           
31394         var xitems = [];
31395         if (cfg.items) {
31396             xitems = cfg.items;
31397             delete cfg.items;
31398         }
31399         var nb = false;
31400         
31401         switch(cfg.xtype) 
31402         {
31403             case 'Content':  // ContentPanel (el, cfg)
31404             case 'Scroll':  // ContentPanel (el, cfg)
31405             case 'View': 
31406                 cfg.autoCreate = true;
31407                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31408                 //} else {
31409                 //    var el = this.el.createChild();
31410                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31411                 //}
31412                 
31413                 this.add(region, ret);
31414                 break;
31415             
31416             /*
31417             case 'TreePanel': // our new panel!
31418                 cfg.el = this.el.createChild();
31419                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31420                 this.add(region, ret);
31421                 break;
31422             */
31423             
31424             case 'Nest': 
31425                 // create a new Layout (which is  a Border Layout...
31426                 
31427                 var clayout = cfg.layout;
31428                 clayout.el  = this.el.createChild();
31429                 clayout.items   = clayout.items  || [];
31430                 
31431                 delete cfg.layout;
31432                 
31433                 // replace this exitems with the clayout ones..
31434                 xitems = clayout.items;
31435                  
31436                 // force background off if it's in center...
31437                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31438                     cfg.background = false;
31439                 }
31440                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31441                 
31442                 
31443                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31444                 //console.log('adding nested layout panel '  + cfg.toSource());
31445                 this.add(region, ret);
31446                 nb = {}; /// find first...
31447                 break;
31448             
31449             case 'Grid':
31450                 
31451                 // needs grid and region
31452                 
31453                 //var el = this.getRegion(region).el.createChild();
31454                 var el = this.el.createChild();
31455                 // create the grid first...
31456                 cfg.grid.el = el;
31457                 cfg.grid = new cfg.grid.ns[cfg.grid.xtype](el);
31458                 
31459                 
31460                 if (region == 'center' && this.active ) {
31461                     cfg.background = false;
31462                 }
31463                 
31464                 ret = new cfg.ns[cfg.xtype](cfg); // new panel!!!!!
31465                 
31466                 this.add(region, ret);
31467                 
31468                 if (cfg.background) {
31469                     // render grid on panel activation (if panel background)
31470                     ret.on('activate', function(gp) {
31471                         if (!gp.grid.rendered) {
31472                             gp.grid.render();
31473                         }
31474                     });
31475                 } else {
31476                     grid.render();
31477                 }
31478                 break;
31479            
31480            
31481             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31482                 // it was the old xcomponent building that caused this before.
31483                 // espeically if border is the top element in the tree.
31484                 ret = this;
31485                 break; 
31486                 
31487                     
31488                 
31489                 
31490                 
31491             default:
31492                 /*
31493                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31494                     
31495                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31496                     this.add(region, ret);
31497                 } else {
31498                 */
31499                     Roo.log(cfg);
31500                     throw "Can not add '" + cfg.xtype + "' to Border";
31501                     return null;
31502              
31503                                 
31504              
31505         }
31506         this.beginUpdate();
31507         // add children..
31508         var region = '';
31509         var abn = {};
31510         Roo.each(xitems, function(i)  {
31511             region = nb && i.region ? i.region : false;
31512             
31513             var add = ret.addxtype(i);
31514            
31515             if (region) {
31516                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31517                 if (!i.background) {
31518                     abn[region] = nb[region] ;
31519                 }
31520             }
31521             
31522         });
31523         this.endUpdate();
31524
31525         // make the last non-background panel active..
31526         //if (nb) { Roo.log(abn); }
31527         if (nb) {
31528             
31529             for(var r in abn) {
31530                 region = this.getRegion(r);
31531                 if (region) {
31532                     // tried using nb[r], but it does not work..
31533                      
31534                     region.showPanel(abn[r]);
31535                    
31536                 }
31537             }
31538         }
31539         return ret;
31540         
31541     },
31542     
31543     
31544 // private
31545     factory : function(cfg)
31546     {
31547         
31548         var validRegions = Roo.bootstrap.layout.Border.regions;
31549
31550         var target = cfg.region;
31551         cfg.mgr = this;
31552         
31553         var r = Roo.bootstrap.layout;
31554         Roo.log(target);
31555         switch(target){
31556             case "north":
31557                 return new r.North(cfg);
31558             case "south":
31559                 return new r.South(cfg);
31560             case "east":
31561                 return new r.East(cfg);
31562             case "west":
31563                 return new r.West(cfg);
31564             case "center":
31565                 return new r.Center(cfg);
31566         }
31567         throw 'Layout region "'+target+'" not supported.';
31568     }
31569     
31570     
31571 });
31572  /*
31573  * Based on:
31574  * Ext JS Library 1.1.1
31575  * Copyright(c) 2006-2007, Ext JS, LLC.
31576  *
31577  * Originally Released Under LGPL - original licence link has changed is not relivant.
31578  *
31579  * Fork - LGPL
31580  * <script type="text/javascript">
31581  */
31582  
31583 /**
31584  * @class Roo.bootstrap.layout.Basic
31585  * @extends Roo.util.Observable
31586  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31587  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31588  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31589  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31590  * @cfg {string}   region  the region that it inhabits..
31591  * @cfg {bool}   skipConfig skip config?
31592  * 
31593
31594  */
31595 Roo.bootstrap.layout.Basic = function(config){
31596     
31597     this.mgr = config.mgr;
31598     
31599     this.position = config.region;
31600     
31601     var skipConfig = config.skipConfig;
31602     
31603     this.events = {
31604         /**
31605          * @scope Roo.BasicLayoutRegion
31606          */
31607         
31608         /**
31609          * @event beforeremove
31610          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31611          * @param {Roo.LayoutRegion} this
31612          * @param {Roo.ContentPanel} panel The panel
31613          * @param {Object} e The cancel event object
31614          */
31615         "beforeremove" : true,
31616         /**
31617          * @event invalidated
31618          * Fires when the layout for this region is changed.
31619          * @param {Roo.LayoutRegion} this
31620          */
31621         "invalidated" : true,
31622         /**
31623          * @event visibilitychange
31624          * Fires when this region is shown or hidden 
31625          * @param {Roo.LayoutRegion} this
31626          * @param {Boolean} visibility true or false
31627          */
31628         "visibilitychange" : true,
31629         /**
31630          * @event paneladded
31631          * Fires when a panel is added. 
31632          * @param {Roo.LayoutRegion} this
31633          * @param {Roo.ContentPanel} panel The panel
31634          */
31635         "paneladded" : true,
31636         /**
31637          * @event panelremoved
31638          * Fires when a panel is removed. 
31639          * @param {Roo.LayoutRegion} this
31640          * @param {Roo.ContentPanel} panel The panel
31641          */
31642         "panelremoved" : true,
31643         /**
31644          * @event beforecollapse
31645          * Fires when this region before collapse.
31646          * @param {Roo.LayoutRegion} this
31647          */
31648         "beforecollapse" : true,
31649         /**
31650          * @event collapsed
31651          * Fires when this region is collapsed.
31652          * @param {Roo.LayoutRegion} this
31653          */
31654         "collapsed" : true,
31655         /**
31656          * @event expanded
31657          * Fires when this region is expanded.
31658          * @param {Roo.LayoutRegion} this
31659          */
31660         "expanded" : true,
31661         /**
31662          * @event slideshow
31663          * Fires when this region is slid into view.
31664          * @param {Roo.LayoutRegion} this
31665          */
31666         "slideshow" : true,
31667         /**
31668          * @event slidehide
31669          * Fires when this region slides out of view. 
31670          * @param {Roo.LayoutRegion} this
31671          */
31672         "slidehide" : true,
31673         /**
31674          * @event panelactivated
31675          * Fires when a panel is activated. 
31676          * @param {Roo.LayoutRegion} this
31677          * @param {Roo.ContentPanel} panel The activated panel
31678          */
31679         "panelactivated" : true,
31680         /**
31681          * @event resized
31682          * Fires when the user resizes this region. 
31683          * @param {Roo.LayoutRegion} this
31684          * @param {Number} newSize The new size (width for east/west, height for north/south)
31685          */
31686         "resized" : true
31687     };
31688     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31689     this.panels = new Roo.util.MixedCollection();
31690     this.panels.getKey = this.getPanelId.createDelegate(this);
31691     this.box = null;
31692     this.activePanel = null;
31693     // ensure listeners are added...
31694     
31695     if (config.listeners || config.events) {
31696         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31697             listeners : config.listeners || {},
31698             events : config.events || {}
31699         });
31700     }
31701     
31702     if(skipConfig !== true){
31703         this.applyConfig(config);
31704     }
31705 };
31706
31707 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31708 {
31709     getPanelId : function(p){
31710         return p.getId();
31711     },
31712     
31713     applyConfig : function(config){
31714         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31715         this.config = config;
31716         
31717     },
31718     
31719     /**
31720      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31721      * the width, for horizontal (north, south) the height.
31722      * @param {Number} newSize The new width or height
31723      */
31724     resizeTo : function(newSize){
31725         var el = this.el ? this.el :
31726                  (this.activePanel ? this.activePanel.getEl() : null);
31727         if(el){
31728             switch(this.position){
31729                 case "east":
31730                 case "west":
31731                     el.setWidth(newSize);
31732                     this.fireEvent("resized", this, newSize);
31733                 break;
31734                 case "north":
31735                 case "south":
31736                     el.setHeight(newSize);
31737                     this.fireEvent("resized", this, newSize);
31738                 break;                
31739             }
31740         }
31741     },
31742     
31743     getBox : function(){
31744         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31745     },
31746     
31747     getMargins : function(){
31748         return this.margins;
31749     },
31750     
31751     updateBox : function(box){
31752         this.box = box;
31753         var el = this.activePanel.getEl();
31754         el.dom.style.left = box.x + "px";
31755         el.dom.style.top = box.y + "px";
31756         this.activePanel.setSize(box.width, box.height);
31757     },
31758     
31759     /**
31760      * Returns the container element for this region.
31761      * @return {Roo.Element}
31762      */
31763     getEl : function(){
31764         return this.activePanel;
31765     },
31766     
31767     /**
31768      * Returns true if this region is currently visible.
31769      * @return {Boolean}
31770      */
31771     isVisible : function(){
31772         return this.activePanel ? true : false;
31773     },
31774     
31775     setActivePanel : function(panel){
31776         panel = this.getPanel(panel);
31777         if(this.activePanel && this.activePanel != panel){
31778             this.activePanel.setActiveState(false);
31779             this.activePanel.getEl().setLeftTop(-10000,-10000);
31780         }
31781         this.activePanel = panel;
31782         panel.setActiveState(true);
31783         if(this.box){
31784             panel.setSize(this.box.width, this.box.height);
31785         }
31786         this.fireEvent("panelactivated", this, panel);
31787         this.fireEvent("invalidated");
31788     },
31789     
31790     /**
31791      * Show the specified panel.
31792      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31793      * @return {Roo.ContentPanel} The shown panel or null
31794      */
31795     showPanel : function(panel){
31796         panel = this.getPanel(panel);
31797         if(panel){
31798             this.setActivePanel(panel);
31799         }
31800         return panel;
31801     },
31802     
31803     /**
31804      * Get the active panel for this region.
31805      * @return {Roo.ContentPanel} The active panel or null
31806      */
31807     getActivePanel : function(){
31808         return this.activePanel;
31809     },
31810     
31811     /**
31812      * Add the passed ContentPanel(s)
31813      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31814      * @return {Roo.ContentPanel} The panel added (if only one was added)
31815      */
31816     add : function(panel){
31817         if(arguments.length > 1){
31818             for(var i = 0, len = arguments.length; i < len; i++) {
31819                 this.add(arguments[i]);
31820             }
31821             return null;
31822         }
31823         if(this.hasPanel(panel)){
31824             this.showPanel(panel);
31825             return panel;
31826         }
31827         var el = panel.getEl();
31828         if(el.dom.parentNode != this.mgr.el.dom){
31829             this.mgr.el.dom.appendChild(el.dom);
31830         }
31831         if(panel.setRegion){
31832             panel.setRegion(this);
31833         }
31834         this.panels.add(panel);
31835         el.setStyle("position", "absolute");
31836         if(!panel.background){
31837             this.setActivePanel(panel);
31838             if(this.config.initialSize && this.panels.getCount()==1){
31839                 this.resizeTo(this.config.initialSize);
31840             }
31841         }
31842         this.fireEvent("paneladded", this, panel);
31843         return panel;
31844     },
31845     
31846     /**
31847      * Returns true if the panel is in this region.
31848      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31849      * @return {Boolean}
31850      */
31851     hasPanel : function(panel){
31852         if(typeof panel == "object"){ // must be panel obj
31853             panel = panel.getId();
31854         }
31855         return this.getPanel(panel) ? true : false;
31856     },
31857     
31858     /**
31859      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31860      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31861      * @param {Boolean} preservePanel Overrides the config preservePanel option
31862      * @return {Roo.ContentPanel} The panel that was removed
31863      */
31864     remove : function(panel, preservePanel){
31865         panel = this.getPanel(panel);
31866         if(!panel){
31867             return null;
31868         }
31869         var e = {};
31870         this.fireEvent("beforeremove", this, panel, e);
31871         if(e.cancel === true){
31872             return null;
31873         }
31874         var panelId = panel.getId();
31875         this.panels.removeKey(panelId);
31876         return panel;
31877     },
31878     
31879     /**
31880      * Returns the panel specified or null if it's not in this region.
31881      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31882      * @return {Roo.ContentPanel}
31883      */
31884     getPanel : function(id){
31885         if(typeof id == "object"){ // must be panel obj
31886             return id;
31887         }
31888         return this.panels.get(id);
31889     },
31890     
31891     /**
31892      * Returns this regions position (north/south/east/west/center).
31893      * @return {String} 
31894      */
31895     getPosition: function(){
31896         return this.position;    
31897     }
31898 });/*
31899  * Based on:
31900  * Ext JS Library 1.1.1
31901  * Copyright(c) 2006-2007, Ext JS, LLC.
31902  *
31903  * Originally Released Under LGPL - original licence link has changed is not relivant.
31904  *
31905  * Fork - LGPL
31906  * <script type="text/javascript">
31907  */
31908  
31909 /**
31910  * @class Roo.bootstrap.layout.Region
31911  * @extends Roo.bootstrap.layout.Basic
31912  * This class represents a region in a layout manager.
31913  
31914  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31915  * @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})
31916  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
31917  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31918  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31919  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31920  * @cfg {String}    title           The title for the region (overrides panel titles)
31921  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31922  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31923  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31924  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31925  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31926  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31927  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31928  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31929  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31930  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
31931
31932  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31933  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31934  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31935  * @cfg {Number}    width           For East/West panels
31936  * @cfg {Number}    height          For North/South panels
31937  * @cfg {Boolean}   split           To show the splitter
31938  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31939  * 
31940  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31941  * @cfg {string}   region  the region that it inhabits..
31942  *
31943
31944  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
31945  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
31946
31947  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
31948  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
31949  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
31950  */
31951 Roo.bootstrap.layout.Region = function(config)
31952 {
31953     
31954     var mgr = config.mgr;
31955     var pos = config.region;
31956     config.skipConfig = true;
31957     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
31958     var dh = Roo.DomHelper;
31959     /** This region's container element 
31960     * @type Roo.Element */
31961     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "roo-layout-region roo-layout-panel roo-layout-panel-" + this.position}, true);
31962     /** This region's title element 
31963     * @type Roo.Element */
31964
31965     this.titleEl = dh.append(this.el.dom,
31966         {
31967                 tag: "div",
31968                 unselectable: "on",
31969                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
31970                 children:[
31971                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31972                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
31973                 ]}, true);
31974     
31975     this.titleEl.enableDisplayMode();
31976     /** This region's title text element 
31977     * @type HTMLElement */
31978     this.titleTextEl = this.titleEl.dom.firstChild;
31979     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31980     /*
31981     this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
31982     this.closeBtn.enableDisplayMode();
31983     this.closeBtn.on("click", this.closeClicked, this);
31984     this.closeBtn.hide();
31985 */
31986     this.createBody(config);
31987     this.visible = true;
31988     this.collapsed = false;
31989
31990     if(config.hideWhenEmpty){
31991         this.hide();
31992         this.on("paneladded", this.validateVisibility, this);
31993         this.on("panelremoved", this.validateVisibility, this);
31994     }
31995     this.applyConfig(config);
31996 };
31997
31998 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
31999
32000
32001
32002     createBody : function(){
32003         /** This region's body element 
32004         * @type Roo.Element */
32005         this.bodyEl = this.el.createChild({
32006                 tag: "div",
32007                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32008         });
32009     },
32010
32011     applyConfig : function(c)
32012     {
32013         /*
32014          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32015             var dh = Roo.DomHelper;
32016             if(c.titlebar !== false){
32017                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32018                 this.collapseBtn.on("click", this.collapse, this);
32019                 this.collapseBtn.enableDisplayMode();
32020                 /*
32021                 if(c.showPin === true || this.showPin){
32022                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32023                     this.stickBtn.enableDisplayMode();
32024                     this.stickBtn.on("click", this.expand, this);
32025                     this.stickBtn.hide();
32026                 }
32027                 
32028             }
32029             */
32030             /** This region's collapsed element
32031             * @type Roo.Element */
32032             /*
32033              *
32034             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32035                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32036             ]}, true);
32037             
32038             if(c.floatable !== false){
32039                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32040                this.collapsedEl.on("click", this.collapseClick, this);
32041             }
32042
32043             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32044                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32045                    id: "message", unselectable: "on", style:{"float":"left"}});
32046                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32047              }
32048             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32049             this.expandBtn.on("click", this.expand, this);
32050             
32051         }
32052         
32053         if(this.collapseBtn){
32054             this.collapseBtn.setVisible(c.collapsible == true);
32055         }
32056         
32057         this.cmargins = c.cmargins || this.cmargins ||
32058                          (this.position == "west" || this.position == "east" ?
32059                              {top: 0, left: 2, right:2, bottom: 0} :
32060                              {top: 2, left: 0, right:0, bottom: 2});
32061         */
32062         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32063         
32064         
32065         this.bottomTabs = c.tabPosition != "top";
32066         
32067         this.autoScroll = c.autoScroll || false;
32068         
32069         
32070         if(this.autoScroll){
32071             this.bodyEl.setStyle("overflow", "auto");
32072         }else{
32073             this.bodyEl.setStyle("overflow", c.overflow || 'hidden');
32074         }
32075         //if(c.titlebar !== false){
32076             if((!c.titlebar && !c.title) || c.titlebar === false){
32077                 this.titleEl.hide();
32078             }else{
32079                 this.titleEl.show();
32080                 if(c.title){
32081                     this.titleTextEl.innerHTML = c.title;
32082                 }
32083             }
32084         //}
32085         this.duration = c.duration || .30;
32086         this.slideDuration = c.slideDuration || .45;
32087         this.config = c;
32088         if(c.collapsed){
32089             this.collapse(true);
32090         }
32091         if(c.hidden){
32092             this.hide();
32093         }
32094     },
32095     /**
32096      * Returns true if this region is currently visible.
32097      * @return {Boolean}
32098      */
32099     isVisible : function(){
32100         return this.visible;
32101     },
32102
32103     /**
32104      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32105      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32106      */
32107     //setCollapsedTitle : function(title){
32108     //    title = title || "&#160;";
32109      //   if(this.collapsedTitleTextEl){
32110       //      this.collapsedTitleTextEl.innerHTML = title;
32111        // }
32112     //},
32113
32114     getBox : function(){
32115         var b;
32116       //  if(!this.collapsed){
32117             b = this.el.getBox(false, true);
32118        // }else{
32119           //  b = this.collapsedEl.getBox(false, true);
32120         //}
32121         return b;
32122     },
32123
32124     getMargins : function(){
32125         return this.margins;
32126         //return this.collapsed ? this.cmargins : this.margins;
32127     },
32128 /*
32129     highlight : function(){
32130         this.el.addClass("x-layout-panel-dragover");
32131     },
32132
32133     unhighlight : function(){
32134         this.el.removeClass("x-layout-panel-dragover");
32135     },
32136 */
32137     updateBox : function(box)
32138     {
32139         this.box = box;
32140         if(!this.collapsed){
32141             this.el.dom.style.left = box.x + "px";
32142             this.el.dom.style.top = box.y + "px";
32143             this.updateBody(box.width, box.height);
32144         }else{
32145             this.collapsedEl.dom.style.left = box.x + "px";
32146             this.collapsedEl.dom.style.top = box.y + "px";
32147             this.collapsedEl.setSize(box.width, box.height);
32148         }
32149         if(this.tabs){
32150             this.tabs.autoSizeTabs();
32151         }
32152     },
32153
32154     updateBody : function(w, h)
32155     {
32156         if(w !== null){
32157             this.el.setWidth(w);
32158             w -= this.el.getBorderWidth("rl");
32159             if(this.config.adjustments){
32160                 w += this.config.adjustments[0];
32161             }
32162         }
32163         if(h !== null){
32164             this.el.setHeight(h);
32165             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32166             h -= this.el.getBorderWidth("tb");
32167             if(this.config.adjustments){
32168                 h += this.config.adjustments[1];
32169             }
32170             this.bodyEl.setHeight(h);
32171             if(this.tabs){
32172                 h = this.tabs.syncHeight(h);
32173             }
32174         }
32175         if(this.panelSize){
32176             w = w !== null ? w : this.panelSize.width;
32177             h = h !== null ? h : this.panelSize.height;
32178         }
32179         if(this.activePanel){
32180             var el = this.activePanel.getEl();
32181             w = w !== null ? w : el.getWidth();
32182             h = h !== null ? h : el.getHeight();
32183             this.panelSize = {width: w, height: h};
32184             this.activePanel.setSize(w, h);
32185         }
32186         if(Roo.isIE && this.tabs){
32187             this.tabs.el.repaint();
32188         }
32189     },
32190
32191     /**
32192      * Returns the container element for this region.
32193      * @return {Roo.Element}
32194      */
32195     getEl : function(){
32196         return this.el;
32197     },
32198
32199     /**
32200      * Hides this region.
32201      */
32202     hide : function(){
32203         //if(!this.collapsed){
32204             this.el.dom.style.left = "-2000px";
32205             this.el.hide();
32206         //}else{
32207          //   this.collapsedEl.dom.style.left = "-2000px";
32208          //   this.collapsedEl.hide();
32209        // }
32210         this.visible = false;
32211         this.fireEvent("visibilitychange", this, false);
32212     },
32213
32214     /**
32215      * Shows this region if it was previously hidden.
32216      */
32217     show : function(){
32218         //if(!this.collapsed){
32219             this.el.show();
32220         //}else{
32221         //    this.collapsedEl.show();
32222        // }
32223         this.visible = true;
32224         this.fireEvent("visibilitychange", this, true);
32225     },
32226 /*
32227     closeClicked : function(){
32228         if(this.activePanel){
32229             this.remove(this.activePanel);
32230         }
32231     },
32232
32233     collapseClick : function(e){
32234         if(this.isSlid){
32235            e.stopPropagation();
32236            this.slideIn();
32237         }else{
32238            e.stopPropagation();
32239            this.slideOut();
32240         }
32241     },
32242 */
32243     /**
32244      * Collapses this region.
32245      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32246      */
32247     /*
32248     collapse : function(skipAnim, skipCheck = false){
32249         if(this.collapsed) {
32250             return;
32251         }
32252         
32253         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32254             
32255             this.collapsed = true;
32256             if(this.split){
32257                 this.split.el.hide();
32258             }
32259             if(this.config.animate && skipAnim !== true){
32260                 this.fireEvent("invalidated", this);
32261                 this.animateCollapse();
32262             }else{
32263                 this.el.setLocation(-20000,-20000);
32264                 this.el.hide();
32265                 this.collapsedEl.show();
32266                 this.fireEvent("collapsed", this);
32267                 this.fireEvent("invalidated", this);
32268             }
32269         }
32270         
32271     },
32272 */
32273     animateCollapse : function(){
32274         // overridden
32275     },
32276
32277     /**
32278      * Expands this region if it was previously collapsed.
32279      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32280      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32281      */
32282     /*
32283     expand : function(e, skipAnim){
32284         if(e) {
32285             e.stopPropagation();
32286         }
32287         if(!this.collapsed || this.el.hasActiveFx()) {
32288             return;
32289         }
32290         if(this.isSlid){
32291             this.afterSlideIn();
32292             skipAnim = true;
32293         }
32294         this.collapsed = false;
32295         if(this.config.animate && skipAnim !== true){
32296             this.animateExpand();
32297         }else{
32298             this.el.show();
32299             if(this.split){
32300                 this.split.el.show();
32301             }
32302             this.collapsedEl.setLocation(-2000,-2000);
32303             this.collapsedEl.hide();
32304             this.fireEvent("invalidated", this);
32305             this.fireEvent("expanded", this);
32306         }
32307     },
32308 */
32309     animateExpand : function(){
32310         // overridden
32311     },
32312
32313     initTabs : function()
32314     {
32315         this.bodyEl.setStyle("overflow", "hidden");
32316         var ts = new Roo.bootstrap.panel.Tabs({
32317                 el: this.bodyEl.dom,
32318                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32319                 disableTooltips: this.config.disableTabTips,
32320                 toolbar : this.config.toolbar
32321             });
32322         
32323         if(this.config.hideTabs){
32324             ts.stripWrap.setDisplayed(false);
32325         }
32326         this.tabs = ts;
32327         ts.resizeTabs = this.config.resizeTabs === true;
32328         ts.minTabWidth = this.config.minTabWidth || 40;
32329         ts.maxTabWidth = this.config.maxTabWidth || 250;
32330         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32331         ts.monitorResize = false;
32332         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32333         ts.bodyEl.addClass('roo-layout-tabs-body');
32334         this.panels.each(this.initPanelAsTab, this);
32335     },
32336
32337     initPanelAsTab : function(panel){
32338         var ti = this.tabs.addTab(
32339                     panel.getEl().id,
32340                     panel.getTitle(), null,
32341                     this.config.closeOnTab && panel.isClosable()
32342             );
32343         if(panel.tabTip !== undefined){
32344             ti.setTooltip(panel.tabTip);
32345         }
32346         ti.on("activate", function(){
32347               this.setActivePanel(panel);
32348         }, this);
32349         
32350         if(this.config.closeOnTab){
32351             ti.on("beforeclose", function(t, e){
32352                 e.cancel = true;
32353                 this.remove(panel);
32354             }, this);
32355         }
32356         return ti;
32357     },
32358
32359     updatePanelTitle : function(panel, title)
32360     {
32361         if(this.activePanel == panel){
32362             this.updateTitle(title);
32363         }
32364         if(this.tabs){
32365             var ti = this.tabs.getTab(panel.getEl().id);
32366             ti.setText(title);
32367             if(panel.tabTip !== undefined){
32368                 ti.setTooltip(panel.tabTip);
32369             }
32370         }
32371     },
32372
32373     updateTitle : function(title){
32374         if(this.titleTextEl && !this.config.title){
32375             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32376         }
32377     },
32378
32379     setActivePanel : function(panel)
32380     {
32381         panel = this.getPanel(panel);
32382         if(this.activePanel && this.activePanel != panel){
32383             this.activePanel.setActiveState(false);
32384         }
32385         this.activePanel = panel;
32386         panel.setActiveState(true);
32387         if(this.panelSize){
32388             panel.setSize(this.panelSize.width, this.panelSize.height);
32389         }
32390         if(this.closeBtn){
32391             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32392         }
32393         this.updateTitle(panel.getTitle());
32394         if(this.tabs){
32395             this.fireEvent("invalidated", this);
32396         }
32397         this.fireEvent("panelactivated", this, panel);
32398     },
32399
32400     /**
32401      * Shows the specified panel.
32402      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32403      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32404      */
32405     showPanel : function(panel)
32406     {
32407         panel = this.getPanel(panel);
32408         if(panel){
32409             if(this.tabs){
32410                 var tab = this.tabs.getTab(panel.getEl().id);
32411                 if(tab.isHidden()){
32412                     this.tabs.unhideTab(tab.id);
32413                 }
32414                 tab.activate();
32415             }else{
32416                 this.setActivePanel(panel);
32417             }
32418         }
32419         return panel;
32420     },
32421
32422     /**
32423      * Get the active panel for this region.
32424      * @return {Roo.ContentPanel} The active panel or null
32425      */
32426     getActivePanel : function(){
32427         return this.activePanel;
32428     },
32429
32430     validateVisibility : function(){
32431         if(this.panels.getCount() < 1){
32432             this.updateTitle("&#160;");
32433             this.closeBtn.hide();
32434             this.hide();
32435         }else{
32436             if(!this.isVisible()){
32437                 this.show();
32438             }
32439         }
32440     },
32441
32442     /**
32443      * Adds the passed ContentPanel(s) to this region.
32444      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32445      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32446      */
32447     add : function(panel){
32448         if(arguments.length > 1){
32449             for(var i = 0, len = arguments.length; i < len; i++) {
32450                 this.add(arguments[i]);
32451             }
32452             return null;
32453         }
32454         if(this.hasPanel(panel)){
32455             this.showPanel(panel);
32456             return panel;
32457         }
32458         panel.setRegion(this);
32459         this.panels.add(panel);
32460         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32461             this.bodyEl.dom.appendChild(panel.getEl().dom);
32462             if(panel.background !== true){
32463                 this.setActivePanel(panel);
32464             }
32465             this.fireEvent("paneladded", this, panel);
32466             return panel;
32467         }
32468         if(!this.tabs){
32469             this.initTabs();
32470         }else{
32471             this.initPanelAsTab(panel);
32472         }
32473         
32474         
32475         if(panel.background !== true){
32476             this.tabs.activate(panel.getEl().id);
32477         }
32478         this.fireEvent("paneladded", this, panel);
32479         return panel;
32480     },
32481
32482     /**
32483      * Hides the tab for the specified panel.
32484      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32485      */
32486     hidePanel : function(panel){
32487         if(this.tabs && (panel = this.getPanel(panel))){
32488             this.tabs.hideTab(panel.getEl().id);
32489         }
32490     },
32491
32492     /**
32493      * Unhides the tab for a previously hidden panel.
32494      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32495      */
32496     unhidePanel : function(panel){
32497         if(this.tabs && (panel = this.getPanel(panel))){
32498             this.tabs.unhideTab(panel.getEl().id);
32499         }
32500     },
32501
32502     clearPanels : function(){
32503         while(this.panels.getCount() > 0){
32504              this.remove(this.panels.first());
32505         }
32506     },
32507
32508     /**
32509      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32510      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32511      * @param {Boolean} preservePanel Overrides the config preservePanel option
32512      * @return {Roo.ContentPanel} The panel that was removed
32513      */
32514     remove : function(panel, preservePanel)
32515     {
32516         panel = this.getPanel(panel);
32517         if(!panel){
32518             return null;
32519         }
32520         var e = {};
32521         this.fireEvent("beforeremove", this, panel, e);
32522         if(e.cancel === true){
32523             return null;
32524         }
32525         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32526         var panelId = panel.getId();
32527         this.panels.removeKey(panelId);
32528         if(preservePanel){
32529             document.body.appendChild(panel.getEl().dom);
32530         }
32531         if(this.tabs){
32532             this.tabs.removeTab(panel.getEl().id);
32533         }else if (!preservePanel){
32534             this.bodyEl.dom.removeChild(panel.getEl().dom);
32535         }
32536         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32537             var p = this.panels.first();
32538             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32539             tempEl.appendChild(p.getEl().dom);
32540             this.bodyEl.update("");
32541             this.bodyEl.dom.appendChild(p.getEl().dom);
32542             tempEl = null;
32543             this.updateTitle(p.getTitle());
32544             this.tabs = null;
32545             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32546             this.setActivePanel(p);
32547         }
32548         panel.setRegion(null);
32549         if(this.activePanel == panel){
32550             this.activePanel = null;
32551         }
32552         if(this.config.autoDestroy !== false && preservePanel !== true){
32553             try{panel.destroy();}catch(e){}
32554         }
32555         this.fireEvent("panelremoved", this, panel);
32556         return panel;
32557     },
32558
32559     /**
32560      * Returns the TabPanel component used by this region
32561      * @return {Roo.TabPanel}
32562      */
32563     getTabs : function(){
32564         return this.tabs;
32565     },
32566
32567     createTool : function(parentEl, className){
32568         var btn = Roo.DomHelper.append(parentEl, {
32569             tag: "div",
32570             cls: "x-layout-tools-button",
32571             children: [ {
32572                 tag: "div",
32573                 cls: "roo-layout-tools-button-inner " + className,
32574                 html: "&#160;"
32575             }]
32576         }, true);
32577         btn.addClassOnOver("roo-layout-tools-button-over");
32578         return btn;
32579     }
32580 });/*
32581  * Based on:
32582  * Ext JS Library 1.1.1
32583  * Copyright(c) 2006-2007, Ext JS, LLC.
32584  *
32585  * Originally Released Under LGPL - original licence link has changed is not relivant.
32586  *
32587  * Fork - LGPL
32588  * <script type="text/javascript">
32589  */
32590  
32591
32592
32593 /**
32594  * @class Roo.SplitLayoutRegion
32595  * @extends Roo.LayoutRegion
32596  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32597  */
32598 Roo.bootstrap.layout.Split = function(config){
32599     this.cursor = config.cursor;
32600     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32601 };
32602
32603 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32604 {
32605     splitTip : "Drag to resize.",
32606     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32607     useSplitTips : false,
32608
32609     applyConfig : function(config){
32610         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32611         
32612         if(config.split){
32613             if(!this.split){
32614                 
32615                 
32616                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,  {
32617                                 tag: "div",
32618                                 id: this.el.id + "-split",
32619                                 cls: "roo-layout-split roo-layout-split-"+this.position,
32620                                 html: "&#160;"
32621                 });
32622                 /** The SplitBar for this region 
32623                 * @type Roo.SplitBar */
32624                 // does not exist yet...
32625                 Roo.log([this.position, this.orientation]);
32626                 
32627                 this.split = new Roo.bootstrap.SplitBar({
32628                     dragElement : splitEl,
32629                     resizingElement: this.el,
32630                     orientation : this.orientation
32631                 });
32632                 
32633                 this.split.on("moved", this.onSplitMove, this);
32634                 this.split.useShim = config.useShim === true;
32635                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32636                 if(this.useSplitTips){
32637                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32638                 }
32639                 //if(config.collapsible){
32640                 //    this.split.el.on("dblclick", this.collapse,  this);
32641                 //}
32642             }
32643             if(typeof config.minSize != "undefined"){
32644                 this.split.minSize = config.minSize;
32645             }
32646             if(typeof config.maxSize != "undefined"){
32647                 this.split.maxSize = config.maxSize;
32648             }
32649             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32650                 this.hideSplitter();
32651             }
32652         }
32653     },
32654
32655     getHMaxSize : function(){
32656          var cmax = this.config.maxSize || 10000;
32657          var center = this.mgr.getRegion("center");
32658          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32659     },
32660
32661     getVMaxSize : function(){
32662          var cmax = this.config.maxSize || 10000;
32663          var center = this.mgr.getRegion("center");
32664          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32665     },
32666
32667     onSplitMove : function(split, newSize){
32668         this.fireEvent("resized", this, newSize);
32669     },
32670     
32671     /** 
32672      * Returns the {@link Roo.SplitBar} for this region.
32673      * @return {Roo.SplitBar}
32674      */
32675     getSplitBar : function(){
32676         return this.split;
32677     },
32678     
32679     hide : function(){
32680         this.hideSplitter();
32681         Roo.bootstrap.layout.Split.superclass.hide.call(this);
32682     },
32683
32684     hideSplitter : function(){
32685         if(this.split){
32686             this.split.el.setLocation(-2000,-2000);
32687             this.split.el.hide();
32688         }
32689     },
32690
32691     show : function(){
32692         if(this.split){
32693             this.split.el.show();
32694         }
32695         Roo.bootstrap.layout.Split.superclass.show.call(this);
32696     },
32697     
32698     beforeSlide: function(){
32699         if(Roo.isGecko){// firefox overflow auto bug workaround
32700             this.bodyEl.clip();
32701             if(this.tabs) {
32702                 this.tabs.bodyEl.clip();
32703             }
32704             if(this.activePanel){
32705                 this.activePanel.getEl().clip();
32706                 
32707                 if(this.activePanel.beforeSlide){
32708                     this.activePanel.beforeSlide();
32709                 }
32710             }
32711         }
32712     },
32713     
32714     afterSlide : function(){
32715         if(Roo.isGecko){// firefox overflow auto bug workaround
32716             this.bodyEl.unclip();
32717             if(this.tabs) {
32718                 this.tabs.bodyEl.unclip();
32719             }
32720             if(this.activePanel){
32721                 this.activePanel.getEl().unclip();
32722                 if(this.activePanel.afterSlide){
32723                     this.activePanel.afterSlide();
32724                 }
32725             }
32726         }
32727     },
32728
32729     initAutoHide : function(){
32730         if(this.autoHide !== false){
32731             if(!this.autoHideHd){
32732                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32733                 this.autoHideHd = {
32734                     "mouseout": function(e){
32735                         if(!e.within(this.el, true)){
32736                             st.delay(500);
32737                         }
32738                     },
32739                     "mouseover" : function(e){
32740                         st.cancel();
32741                     },
32742                     scope : this
32743                 };
32744             }
32745             this.el.on(this.autoHideHd);
32746         }
32747     },
32748
32749     clearAutoHide : function(){
32750         if(this.autoHide !== false){
32751             this.el.un("mouseout", this.autoHideHd.mouseout);
32752             this.el.un("mouseover", this.autoHideHd.mouseover);
32753         }
32754     },
32755
32756     clearMonitor : function(){
32757         Roo.get(document).un("click", this.slideInIf, this);
32758     },
32759
32760     // these names are backwards but not changed for compat
32761     slideOut : function(){
32762         if(this.isSlid || this.el.hasActiveFx()){
32763             return;
32764         }
32765         this.isSlid = true;
32766         if(this.collapseBtn){
32767             this.collapseBtn.hide();
32768         }
32769         this.closeBtnState = this.closeBtn.getStyle('display');
32770         this.closeBtn.hide();
32771         if(this.stickBtn){
32772             this.stickBtn.show();
32773         }
32774         this.el.show();
32775         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32776         this.beforeSlide();
32777         this.el.setStyle("z-index", 10001);
32778         this.el.slideIn(this.getSlideAnchor(), {
32779             callback: function(){
32780                 this.afterSlide();
32781                 this.initAutoHide();
32782                 Roo.get(document).on("click", this.slideInIf, this);
32783                 this.fireEvent("slideshow", this);
32784             },
32785             scope: this,
32786             block: true
32787         });
32788     },
32789
32790     afterSlideIn : function(){
32791         this.clearAutoHide();
32792         this.isSlid = false;
32793         this.clearMonitor();
32794         this.el.setStyle("z-index", "");
32795         if(this.collapseBtn){
32796             this.collapseBtn.show();
32797         }
32798         this.closeBtn.setStyle('display', this.closeBtnState);
32799         if(this.stickBtn){
32800             this.stickBtn.hide();
32801         }
32802         this.fireEvent("slidehide", this);
32803     },
32804
32805     slideIn : function(cb){
32806         if(!this.isSlid || this.el.hasActiveFx()){
32807             Roo.callback(cb);
32808             return;
32809         }
32810         this.isSlid = false;
32811         this.beforeSlide();
32812         this.el.slideOut(this.getSlideAnchor(), {
32813             callback: function(){
32814                 this.el.setLeftTop(-10000, -10000);
32815                 this.afterSlide();
32816                 this.afterSlideIn();
32817                 Roo.callback(cb);
32818             },
32819             scope: this,
32820             block: true
32821         });
32822     },
32823     
32824     slideInIf : function(e){
32825         if(!e.within(this.el)){
32826             this.slideIn();
32827         }
32828     },
32829
32830     animateCollapse : function(){
32831         this.beforeSlide();
32832         this.el.setStyle("z-index", 20000);
32833         var anchor = this.getSlideAnchor();
32834         this.el.slideOut(anchor, {
32835             callback : function(){
32836                 this.el.setStyle("z-index", "");
32837                 this.collapsedEl.slideIn(anchor, {duration:.3});
32838                 this.afterSlide();
32839                 this.el.setLocation(-10000,-10000);
32840                 this.el.hide();
32841                 this.fireEvent("collapsed", this);
32842             },
32843             scope: this,
32844             block: true
32845         });
32846     },
32847
32848     animateExpand : function(){
32849         this.beforeSlide();
32850         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32851         this.el.setStyle("z-index", 20000);
32852         this.collapsedEl.hide({
32853             duration:.1
32854         });
32855         this.el.slideIn(this.getSlideAnchor(), {
32856             callback : function(){
32857                 this.el.setStyle("z-index", "");
32858                 this.afterSlide();
32859                 if(this.split){
32860                     this.split.el.show();
32861                 }
32862                 this.fireEvent("invalidated", this);
32863                 this.fireEvent("expanded", this);
32864             },
32865             scope: this,
32866             block: true
32867         });
32868     },
32869
32870     anchors : {
32871         "west" : "left",
32872         "east" : "right",
32873         "north" : "top",
32874         "south" : "bottom"
32875     },
32876
32877     sanchors : {
32878         "west" : "l",
32879         "east" : "r",
32880         "north" : "t",
32881         "south" : "b"
32882     },
32883
32884     canchors : {
32885         "west" : "tl-tr",
32886         "east" : "tr-tl",
32887         "north" : "tl-bl",
32888         "south" : "bl-tl"
32889     },
32890
32891     getAnchor : function(){
32892         return this.anchors[this.position];
32893     },
32894
32895     getCollapseAnchor : function(){
32896         return this.canchors[this.position];
32897     },
32898
32899     getSlideAnchor : function(){
32900         return this.sanchors[this.position];
32901     },
32902
32903     getAlignAdj : function(){
32904         var cm = this.cmargins;
32905         switch(this.position){
32906             case "west":
32907                 return [0, 0];
32908             break;
32909             case "east":
32910                 return [0, 0];
32911             break;
32912             case "north":
32913                 return [0, 0];
32914             break;
32915             case "south":
32916                 return [0, 0];
32917             break;
32918         }
32919     },
32920
32921     getExpandAdj : function(){
32922         var c = this.collapsedEl, cm = this.cmargins;
32923         switch(this.position){
32924             case "west":
32925                 return [-(cm.right+c.getWidth()+cm.left), 0];
32926             break;
32927             case "east":
32928                 return [cm.right+c.getWidth()+cm.left, 0];
32929             break;
32930             case "north":
32931                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32932             break;
32933             case "south":
32934                 return [0, cm.top+cm.bottom+c.getHeight()];
32935             break;
32936         }
32937     }
32938 });/*
32939  * Based on:
32940  * Ext JS Library 1.1.1
32941  * Copyright(c) 2006-2007, Ext JS, LLC.
32942  *
32943  * Originally Released Under LGPL - original licence link has changed is not relivant.
32944  *
32945  * Fork - LGPL
32946  * <script type="text/javascript">
32947  */
32948 /*
32949  * These classes are private internal classes
32950  */
32951 Roo.bootstrap.layout.Center = function(config){
32952     config.region = "center";
32953     Roo.bootstrap.layout.Region.call(this, config);
32954     this.visible = true;
32955     this.minWidth = config.minWidth || 20;
32956     this.minHeight = config.minHeight || 20;
32957 };
32958
32959 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
32960     hide : function(){
32961         // center panel can't be hidden
32962     },
32963     
32964     show : function(){
32965         // center panel can't be hidden
32966     },
32967     
32968     getMinWidth: function(){
32969         return this.minWidth;
32970     },
32971     
32972     getMinHeight: function(){
32973         return this.minHeight;
32974     }
32975 });
32976
32977
32978
32979
32980  
32981
32982
32983
32984
32985
32986 Roo.bootstrap.layout.North = function(config)
32987 {
32988     config.region = 'north';
32989     config.cursor = 'n-resize';
32990     
32991     Roo.bootstrap.layout.Split.call(this, config);
32992     if(this.split){
32993         this.split.placement = Roo.bootstrap.SplitBar.TOP;
32994         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
32995         this.split.el.addClass("roo-layout-split-v");
32996     }
32997     var size = config.initialSize || config.height;
32998     if(typeof size != "undefined"){
32999         this.el.setHeight(size);
33000     }
33001 };
33002 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33003 {
33004     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33005     getBox : function(){
33006         if(this.collapsed){
33007             return this.collapsedEl.getBox();
33008         }
33009         var box = this.el.getBox();
33010         if(this.split){
33011             box.height += this.split.el.getHeight();
33012         }
33013         return box;
33014     },
33015     
33016     updateBox : function(box){
33017         if(this.split && !this.collapsed){
33018             box.height -= this.split.el.getHeight();
33019             this.split.el.setLeft(box.x);
33020             this.split.el.setTop(box.y+box.height);
33021             this.split.el.setWidth(box.width);
33022         }
33023         if(this.collapsed){
33024             this.updateBody(box.width, null);
33025         }
33026         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33027     }
33028 });
33029
33030
33031
33032
33033
33034 Roo.bootstrap.layout.South = function(config){
33035     config.region = 'south';
33036     config.cursor = 's-resize';
33037     Roo.bootstrap.layout.Split.call(this, config);
33038     if(this.split){
33039         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33040         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33041         this.split.el.addClass("roo-layout-split-v");
33042     }
33043     var size = config.initialSize || config.height;
33044     if(typeof size != "undefined"){
33045         this.el.setHeight(size);
33046     }
33047 };
33048
33049 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33050     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33051     getBox : function(){
33052         if(this.collapsed){
33053             return this.collapsedEl.getBox();
33054         }
33055         var box = this.el.getBox();
33056         if(this.split){
33057             var sh = this.split.el.getHeight();
33058             box.height += sh;
33059             box.y -= sh;
33060         }
33061         return box;
33062     },
33063     
33064     updateBox : function(box){
33065         if(this.split && !this.collapsed){
33066             var sh = this.split.el.getHeight();
33067             box.height -= sh;
33068             box.y += sh;
33069             this.split.el.setLeft(box.x);
33070             this.split.el.setTop(box.y-sh);
33071             this.split.el.setWidth(box.width);
33072         }
33073         if(this.collapsed){
33074             this.updateBody(box.width, null);
33075         }
33076         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33077     }
33078 });
33079
33080 Roo.bootstrap.layout.East = function(config){
33081     config.region = "east";
33082     config.cursor = "e-resize";
33083     Roo.bootstrap.layout.Split.call(this, config);
33084     if(this.split){
33085         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33086         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33087         this.split.el.addClass("roo-layout-split-h");
33088     }
33089     var size = config.initialSize || config.width;
33090     if(typeof size != "undefined"){
33091         this.el.setWidth(size);
33092     }
33093 };
33094 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33095     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33096     getBox : function(){
33097         if(this.collapsed){
33098             return this.collapsedEl.getBox();
33099         }
33100         var box = this.el.getBox();
33101         if(this.split){
33102             var sw = this.split.el.getWidth();
33103             box.width += sw;
33104             box.x -= sw;
33105         }
33106         return box;
33107     },
33108
33109     updateBox : function(box){
33110         if(this.split && !this.collapsed){
33111             var sw = this.split.el.getWidth();
33112             box.width -= sw;
33113             this.split.el.setLeft(box.x);
33114             this.split.el.setTop(box.y);
33115             this.split.el.setHeight(box.height);
33116             box.x += sw;
33117         }
33118         if(this.collapsed){
33119             this.updateBody(null, box.height);
33120         }
33121         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33122     }
33123 });
33124
33125 Roo.bootstrap.layout.West = function(config){
33126     config.region = "west";
33127     config.cursor = "w-resize";
33128     
33129     Roo.bootstrap.layout.Split.call(this, config);
33130     if(this.split){
33131         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33132         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33133         this.split.el.addClass("roo-layout-split-h");
33134     }
33135     var size = config.initialSize || config.width;
33136     if(typeof size != "undefined"){
33137         this.el.setWidth(size);
33138     }
33139 };
33140 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33141     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33142     getBox : function(){
33143         if(this.collapsed){
33144             return this.collapsedEl.getBox();
33145         }
33146         var box = this.el.getBox();
33147         if(this.split){
33148             box.width += this.split.el.getWidth();
33149         }
33150         return box;
33151     },
33152     
33153     updateBox : function(box){
33154         if(this.split && !this.collapsed){
33155             var sw = this.split.el.getWidth();
33156             box.width -= sw;
33157             this.split.el.setLeft(box.x+box.width);
33158             this.split.el.setTop(box.y);
33159             this.split.el.setHeight(box.height);
33160         }
33161         if(this.collapsed){
33162             this.updateBody(null, box.height);
33163         }
33164         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33165     }
33166 });
33167 Roo.namespace("Roo.bootstrap.panel");/*
33168  * Based on:
33169  * Ext JS Library 1.1.1
33170  * Copyright(c) 2006-2007, Ext JS, LLC.
33171  *
33172  * Originally Released Under LGPL - original licence link has changed is not relivant.
33173  *
33174  * Fork - LGPL
33175  * <script type="text/javascript">
33176  */
33177 /**
33178  * @class Roo.ContentPanel
33179  * @extends Roo.util.Observable
33180  * A basic ContentPanel element.
33181  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33182  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33183  * @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
33184  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33185  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33186  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33187  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33188  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33189  * @cfg {String} title          The title for this panel
33190  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33191  * @cfg {String} url            Calls {@link #setUrl} with this value
33192  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33193  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33194  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33195  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33196
33197  * @constructor
33198  * Create a new ContentPanel.
33199  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33200  * @param {String/Object} config A string to set only the title or a config object
33201  * @param {String} content (optional) Set the HTML content for this panel
33202  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33203  */
33204 Roo.bootstrap.panel.Content = function( config){
33205     
33206     var el = config.el;
33207     var content = config.content;
33208
33209     if(config.autoCreate){ // xtype is available if this is called from factory
33210         el = Roo.id();
33211     }
33212     this.el = Roo.get(el);
33213     if(!this.el && config && config.autoCreate){
33214         if(typeof config.autoCreate == "object"){
33215             if(!config.autoCreate.id){
33216                 config.autoCreate.id = config.id||el;
33217             }
33218             this.el = Roo.DomHelper.append(document.body,
33219                         config.autoCreate, true);
33220         }else{
33221             var elcfg =  {   tag: "div",
33222                             cls: "roo-layout-inactive-content",
33223                             id: config.id||el
33224                             };
33225             if (config.html) {
33226                 elcfg.html = config.html;
33227                 
33228             }
33229                         
33230             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33231         }
33232     } 
33233     this.closable = false;
33234     this.loaded = false;
33235     this.active = false;
33236     if(typeof config == "string"){
33237         this.title = config;
33238     }else{
33239         Roo.apply(this, config);
33240     }
33241     /*
33242     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33243         this.wrapEl = this.el.wrap();
33244         this.toolbar.container = this.el.insertSibling(false, 'before');
33245         this.toolbar = new Roo.Toolbar(this.toolbar);
33246     }
33247     
33248     // xtype created footer. - not sure if will work as we normally have to render first..
33249     if (this.footer && !this.footer.el && this.footer.xtype) {
33250         if (!this.wrapEl) {
33251             this.wrapEl = this.el.wrap();
33252         }
33253     
33254         this.footer.container = this.wrapEl.createChild();
33255          
33256         this.footer = Roo.factory(this.footer, Roo);
33257         
33258     }
33259     */
33260     if(this.resizeEl){
33261         this.resizeEl = Roo.get(this.resizeEl, true);
33262     }else{
33263         this.resizeEl = this.el;
33264     }
33265     // handle view.xtype
33266     
33267  
33268     
33269     
33270     this.addEvents({
33271         /**
33272          * @event activate
33273          * Fires when this panel is activated. 
33274          * @param {Roo.ContentPanel} this
33275          */
33276         "activate" : true,
33277         /**
33278          * @event deactivate
33279          * Fires when this panel is activated. 
33280          * @param {Roo.ContentPanel} this
33281          */
33282         "deactivate" : true,
33283
33284         /**
33285          * @event resize
33286          * Fires when this panel is resized if fitToFrame is true.
33287          * @param {Roo.ContentPanel} this
33288          * @param {Number} width The width after any component adjustments
33289          * @param {Number} height The height after any component adjustments
33290          */
33291         "resize" : true,
33292         
33293          /**
33294          * @event render
33295          * Fires when this tab is created
33296          * @param {Roo.ContentPanel} this
33297          */
33298         "render" : true
33299         
33300         
33301         
33302     });
33303     
33304
33305     
33306     
33307     if(this.autoScroll){
33308         this.resizeEl.setStyle("overflow", "auto");
33309     } else {
33310         // fix randome scrolling
33311         this.el.on('scroll', function() {
33312             Roo.log('fix random scolling');
33313             this.scrollTo('top',0); 
33314         });
33315     }
33316     content = content || this.content;
33317     if(content){
33318         this.setContent(content);
33319     }
33320     if(config && config.url){
33321         this.setUrl(this.url, this.params, this.loadOnce);
33322     }
33323     
33324     
33325     
33326     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33327     
33328     if (this.view && typeof(this.view.xtype) != 'undefined') {
33329         this.view.el = this.el.appendChild(document.createElement("div"));
33330         this.view = Roo.factory(this.view); 
33331         this.view.render  &&  this.view.render(false, '');  
33332     }
33333     
33334     
33335     this.fireEvent('render', this);
33336 };
33337
33338 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33339     tabTip:'',
33340     setRegion : function(region){
33341         this.region = region;
33342         if(region){
33343            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33344         }else{
33345            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33346         } 
33347     },
33348     
33349     /**
33350      * Returns the toolbar for this Panel if one was configured. 
33351      * @return {Roo.Toolbar} 
33352      */
33353     getToolbar : function(){
33354         return this.toolbar;
33355     },
33356     
33357     setActiveState : function(active){
33358         this.active = active;
33359         if(!active){
33360             this.fireEvent("deactivate", this);
33361         }else{
33362             this.fireEvent("activate", this);
33363         }
33364     },
33365     /**
33366      * Updates this panel's element
33367      * @param {String} content The new content
33368      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33369     */
33370     setContent : function(content, loadScripts){
33371         this.el.update(content, loadScripts);
33372     },
33373
33374     ignoreResize : function(w, h){
33375         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33376             return true;
33377         }else{
33378             this.lastSize = {width: w, height: h};
33379             return false;
33380         }
33381     },
33382     /**
33383      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33384      * @return {Roo.UpdateManager} The UpdateManager
33385      */
33386     getUpdateManager : function(){
33387         return this.el.getUpdateManager();
33388     },
33389      /**
33390      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33391      * @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:
33392 <pre><code>
33393 panel.load({
33394     url: "your-url.php",
33395     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33396     callback: yourFunction,
33397     scope: yourObject, //(optional scope)
33398     discardUrl: false,
33399     nocache: false,
33400     text: "Loading...",
33401     timeout: 30,
33402     scripts: false
33403 });
33404 </code></pre>
33405      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33406      * 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.
33407      * @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}
33408      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33409      * @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.
33410      * @return {Roo.ContentPanel} this
33411      */
33412     load : function(){
33413         var um = this.el.getUpdateManager();
33414         um.update.apply(um, arguments);
33415         return this;
33416     },
33417
33418
33419     /**
33420      * 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.
33421      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33422      * @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)
33423      * @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)
33424      * @return {Roo.UpdateManager} The UpdateManager
33425      */
33426     setUrl : function(url, params, loadOnce){
33427         if(this.refreshDelegate){
33428             this.removeListener("activate", this.refreshDelegate);
33429         }
33430         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33431         this.on("activate", this.refreshDelegate);
33432         return this.el.getUpdateManager();
33433     },
33434     
33435     _handleRefresh : function(url, params, loadOnce){
33436         if(!loadOnce || !this.loaded){
33437             var updater = this.el.getUpdateManager();
33438             updater.update(url, params, this._setLoaded.createDelegate(this));
33439         }
33440     },
33441     
33442     _setLoaded : function(){
33443         this.loaded = true;
33444     }, 
33445     
33446     /**
33447      * Returns this panel's id
33448      * @return {String} 
33449      */
33450     getId : function(){
33451         return this.el.id;
33452     },
33453     
33454     /** 
33455      * Returns this panel's element - used by regiosn to add.
33456      * @return {Roo.Element} 
33457      */
33458     getEl : function(){
33459         return this.wrapEl || this.el;
33460     },
33461     
33462    
33463     
33464     adjustForComponents : function(width, height)
33465     {
33466         //Roo.log('adjustForComponents ');
33467         if(this.resizeEl != this.el){
33468             width -= this.el.getFrameWidth('lr');
33469             height -= this.el.getFrameWidth('tb');
33470         }
33471         if(this.toolbar){
33472             var te = this.toolbar.getEl();
33473             height -= te.getHeight();
33474             te.setWidth(width);
33475         }
33476         if(this.footer){
33477             var te = this.footer.getEl();
33478             Roo.log("footer:" + te.getHeight());
33479             
33480             height -= te.getHeight();
33481             te.setWidth(width);
33482         }
33483         
33484         
33485         if(this.adjustments){
33486             width += this.adjustments[0];
33487             height += this.adjustments[1];
33488         }
33489         return {"width": width, "height": height};
33490     },
33491     
33492     setSize : function(width, height){
33493         if(this.fitToFrame && !this.ignoreResize(width, height)){
33494             if(this.fitContainer && this.resizeEl != this.el){
33495                 this.el.setSize(width, height);
33496             }
33497             var size = this.adjustForComponents(width, height);
33498             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33499             this.fireEvent('resize', this, size.width, size.height);
33500         }
33501     },
33502     
33503     /**
33504      * Returns this panel's title
33505      * @return {String} 
33506      */
33507     getTitle : function(){
33508         return this.title;
33509     },
33510     
33511     /**
33512      * Set this panel's title
33513      * @param {String} title
33514      */
33515     setTitle : function(title){
33516         this.title = title;
33517         if(this.region){
33518             this.region.updatePanelTitle(this, title);
33519         }
33520     },
33521     
33522     /**
33523      * Returns true is this panel was configured to be closable
33524      * @return {Boolean} 
33525      */
33526     isClosable : function(){
33527         return this.closable;
33528     },
33529     
33530     beforeSlide : function(){
33531         this.el.clip();
33532         this.resizeEl.clip();
33533     },
33534     
33535     afterSlide : function(){
33536         this.el.unclip();
33537         this.resizeEl.unclip();
33538     },
33539     
33540     /**
33541      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33542      *   Will fail silently if the {@link #setUrl} method has not been called.
33543      *   This does not activate the panel, just updates its content.
33544      */
33545     refresh : function(){
33546         if(this.refreshDelegate){
33547            this.loaded = false;
33548            this.refreshDelegate();
33549         }
33550     },
33551     
33552     /**
33553      * Destroys this panel
33554      */
33555     destroy : function(){
33556         this.el.removeAllListeners();
33557         var tempEl = document.createElement("span");
33558         tempEl.appendChild(this.el.dom);
33559         tempEl.innerHTML = "";
33560         this.el.remove();
33561         this.el = null;
33562     },
33563     
33564     /**
33565      * form - if the content panel contains a form - this is a reference to it.
33566      * @type {Roo.form.Form}
33567      */
33568     form : false,
33569     /**
33570      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33571      *    This contains a reference to it.
33572      * @type {Roo.View}
33573      */
33574     view : false,
33575     
33576       /**
33577      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33578      * <pre><code>
33579
33580 layout.addxtype({
33581        xtype : 'Form',
33582        items: [ .... ]
33583    }
33584 );
33585
33586 </code></pre>
33587      * @param {Object} cfg Xtype definition of item to add.
33588      */
33589     
33590     
33591     getChildContainer: function () {
33592         return this.getEl();
33593     }
33594     
33595     
33596     /*
33597         var  ret = new Roo.factory(cfg);
33598         return ret;
33599         
33600         
33601         // add form..
33602         if (cfg.xtype.match(/^Form$/)) {
33603             
33604             var el;
33605             //if (this.footer) {
33606             //    el = this.footer.container.insertSibling(false, 'before');
33607             //} else {
33608                 el = this.el.createChild();
33609             //}
33610
33611             this.form = new  Roo.form.Form(cfg);
33612             
33613             
33614             if ( this.form.allItems.length) {
33615                 this.form.render(el.dom);
33616             }
33617             return this.form;
33618         }
33619         // should only have one of theses..
33620         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33621             // views.. should not be just added - used named prop 'view''
33622             
33623             cfg.el = this.el.appendChild(document.createElement("div"));
33624             // factory?
33625             
33626             var ret = new Roo.factory(cfg);
33627              
33628              ret.render && ret.render(false, ''); // render blank..
33629             this.view = ret;
33630             return ret;
33631         }
33632         return false;
33633     }
33634     \*/
33635 });
33636  
33637 /**
33638  * @class Roo.bootstrap.panel.Grid
33639  * @extends Roo.bootstrap.panel.Content
33640  * @constructor
33641  * Create a new GridPanel.
33642  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33643  * @param {String/Object} config A string to set only the panel's title, or a config object
33644
33645   new Roo.bootstrap.panel.Grid({
33646                 grid: .....
33647                 ....
33648   }
33649
33650  */
33651
33652
33653
33654 Roo.bootstrap.panel.Grid = function(config){
33655     
33656   
33657     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33658         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33659         
33660     this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
33661     config.el = this.wrapper;
33662     
33663     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
33664     
33665     if(this.toolbar){
33666         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33667     }
33668     // xtype created footer. - not sure if will work as we normally have to render first..
33669     if (this.footer && !this.footer.el && this.footer.xtype) {
33670         
33671         this.footer.container = this.grid.getView().getFooterPanel(true);
33672         this.footer.dataSource = this.grid.dataSource;
33673         this.footer = Roo.factory(this.footer, Roo);
33674         
33675     }
33676     
33677     config.grid.monitorWindowResize = false; // turn off autosizing
33678     config.grid.autoHeight = false;
33679     config.grid.autoWidth = false;
33680     this.grid = config.grid;
33681     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33682 };
33683
33684 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
33685     getId : function(){
33686         return this.grid.id;
33687     },
33688     
33689     /**
33690      * Returns the grid for this panel
33691      * @return {Roo.bootstrap.Table} 
33692      */
33693     getGrid : function(){
33694         return this.grid;    
33695     },
33696     
33697     setSize : function(width, height){
33698         if(!this.ignoreResize(width, height)){
33699             var grid = this.grid;
33700             var size = this.adjustForComponents(width, height);
33701             grid.getGridEl().setSize(size.width, size.height);
33702             grid.autoSize();
33703         }
33704     },
33705     
33706     beforeSlide : function(){
33707         this.grid.getView().scroller.clip();
33708     },
33709     
33710     afterSlide : function(){
33711         this.grid.getView().scroller.unclip();
33712     },
33713     
33714     destroy : function(){
33715         this.grid.destroy();
33716         delete this.grid;
33717         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
33718     }
33719 });
33720
33721 /**
33722  * @class Roo.bootstrap.panel.Nest
33723  * @extends Roo.bootstrap.panel.Content
33724  * @constructor
33725  * Create a new Panel, that can contain a layout.Border.
33726  * 
33727  * 
33728  * @param {Roo.BorderLayout} layout The layout for this panel
33729  * @param {String/Object} config A string to set only the title or a config object
33730  */
33731 Roo.bootstrap.panel.Nest = function(config)
33732 {
33733     // construct with only one argument..
33734     /* FIXME - implement nicer consturctors
33735     if (layout.layout) {
33736         config = layout;
33737         layout = config.layout;
33738         delete config.layout;
33739     }
33740     if (layout.xtype && !layout.getEl) {
33741         // then layout needs constructing..
33742         layout = Roo.factory(layout, Roo);
33743     }
33744     */
33745     
33746     config.el =  config.layout.getEl();
33747     
33748     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
33749     
33750     config.layout.monitorWindowResize = false; // turn off autosizing
33751     this.layout = config.layout;
33752     this.layout.getEl().addClass("roo-layout-nested-layout");
33753     
33754     
33755     
33756     
33757 };
33758
33759 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
33760
33761     setSize : function(width, height){
33762         if(!this.ignoreResize(width, height)){
33763             var size = this.adjustForComponents(width, height);
33764             var el = this.layout.getEl();
33765             el.setSize(size.width, size.height);
33766             var touch = el.dom.offsetWidth;
33767             this.layout.layout();
33768             // ie requires a double layout on the first pass
33769             if(Roo.isIE && !this.initialized){
33770                 this.initialized = true;
33771                 this.layout.layout();
33772             }
33773         }
33774     },
33775     
33776     // activate all subpanels if not currently active..
33777     
33778     setActiveState : function(active){
33779         this.active = active;
33780         if(!active){
33781             this.fireEvent("deactivate", this);
33782             return;
33783         }
33784         
33785         this.fireEvent("activate", this);
33786         // not sure if this should happen before or after..
33787         if (!this.layout) {
33788             return; // should not happen..
33789         }
33790         var reg = false;
33791         for (var r in this.layout.regions) {
33792             reg = this.layout.getRegion(r);
33793             if (reg.getActivePanel()) {
33794                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33795                 reg.setActivePanel(reg.getActivePanel());
33796                 continue;
33797             }
33798             if (!reg.panels.length) {
33799                 continue;
33800             }
33801             reg.showPanel(reg.getPanel(0));
33802         }
33803         
33804         
33805         
33806         
33807     },
33808     
33809     /**
33810      * Returns the nested BorderLayout for this panel
33811      * @return {Roo.BorderLayout} 
33812      */
33813     getLayout : function(){
33814         return this.layout;
33815     },
33816     
33817      /**
33818      * Adds a xtype elements to the layout of the nested panel
33819      * <pre><code>
33820
33821 panel.addxtype({
33822        xtype : 'ContentPanel',
33823        region: 'west',
33824        items: [ .... ]
33825    }
33826 );
33827
33828 panel.addxtype({
33829         xtype : 'NestedLayoutPanel',
33830         region: 'west',
33831         layout: {
33832            center: { },
33833            west: { }   
33834         },
33835         items : [ ... list of content panels or nested layout panels.. ]
33836    }
33837 );
33838 </code></pre>
33839      * @param {Object} cfg Xtype definition of item to add.
33840      */
33841     addxtype : function(cfg) {
33842         return this.layout.addxtype(cfg);
33843     
33844     }
33845 });        /*
33846  * Based on:
33847  * Ext JS Library 1.1.1
33848  * Copyright(c) 2006-2007, Ext JS, LLC.
33849  *
33850  * Originally Released Under LGPL - original licence link has changed is not relivant.
33851  *
33852  * Fork - LGPL
33853  * <script type="text/javascript">
33854  */
33855 /**
33856  * @class Roo.TabPanel
33857  * @extends Roo.util.Observable
33858  * A lightweight tab container.
33859  * <br><br>
33860  * Usage:
33861  * <pre><code>
33862 // basic tabs 1, built from existing content
33863 var tabs = new Roo.TabPanel("tabs1");
33864 tabs.addTab("script", "View Script");
33865 tabs.addTab("markup", "View Markup");
33866 tabs.activate("script");
33867
33868 // more advanced tabs, built from javascript
33869 var jtabs = new Roo.TabPanel("jtabs");
33870 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
33871
33872 // set up the UpdateManager
33873 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
33874 var updater = tab2.getUpdateManager();
33875 updater.setDefaultUrl("ajax1.htm");
33876 tab2.on('activate', updater.refresh, updater, true);
33877
33878 // Use setUrl for Ajax loading
33879 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
33880 tab3.setUrl("ajax2.htm", null, true);
33881
33882 // Disabled tab
33883 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
33884 tab4.disable();
33885
33886 jtabs.activate("jtabs-1");
33887  * </code></pre>
33888  * @constructor
33889  * Create a new TabPanel.
33890  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
33891  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
33892  */
33893 Roo.bootstrap.panel.Tabs = function(config){
33894     /**
33895     * The container element for this TabPanel.
33896     * @type Roo.Element
33897     */
33898     this.el = Roo.get(config.el);
33899     delete config.el;
33900     if(config){
33901         if(typeof config == "boolean"){
33902             this.tabPosition = config ? "bottom" : "top";
33903         }else{
33904             Roo.apply(this, config);
33905         }
33906     }
33907     
33908     if(this.tabPosition == "bottom"){
33909         this.bodyEl = Roo.get(this.createBody(this.el.dom));
33910         this.el.addClass("roo-tabs-bottom");
33911     }
33912     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
33913     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
33914     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
33915     if(Roo.isIE){
33916         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
33917     }
33918     if(this.tabPosition != "bottom"){
33919         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
33920          * @type Roo.Element
33921          */
33922         this.bodyEl = Roo.get(this.createBody(this.el.dom));
33923         this.el.addClass("roo-tabs-top");
33924     }
33925     this.items = [];
33926
33927     this.bodyEl.setStyle("position", "relative");
33928
33929     this.active = null;
33930     this.activateDelegate = this.activate.createDelegate(this);
33931
33932     this.addEvents({
33933         /**
33934          * @event tabchange
33935          * Fires when the active tab changes
33936          * @param {Roo.TabPanel} this
33937          * @param {Roo.TabPanelItem} activePanel The new active tab
33938          */
33939         "tabchange": true,
33940         /**
33941          * @event beforetabchange
33942          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
33943          * @param {Roo.TabPanel} this
33944          * @param {Object} e Set cancel to true on this object to cancel the tab change
33945          * @param {Roo.TabPanelItem} tab The tab being changed to
33946          */
33947         "beforetabchange" : true
33948     });
33949
33950     Roo.EventManager.onWindowResize(this.onResize, this);
33951     this.cpad = this.el.getPadding("lr");
33952     this.hiddenCount = 0;
33953
33954
33955     // toolbar on the tabbar support...
33956     if (this.toolbar) {
33957         alert("no toolbar support yet");
33958         this.toolbar  = false;
33959         /*
33960         var tcfg = this.toolbar;
33961         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
33962         this.toolbar = new Roo.Toolbar(tcfg);
33963         if (Roo.isSafari) {
33964             var tbl = tcfg.container.child('table', true);
33965             tbl.setAttribute('width', '100%');
33966         }
33967         */
33968         
33969     }
33970    
33971
33972
33973     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
33974 };
33975
33976 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
33977     /*
33978      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
33979      */
33980     tabPosition : "top",
33981     /*
33982      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
33983      */
33984     currentTabWidth : 0,
33985     /*
33986      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
33987      */
33988     minTabWidth : 40,
33989     /*
33990      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
33991      */
33992     maxTabWidth : 250,
33993     /*
33994      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
33995      */
33996     preferredTabWidth : 175,
33997     /*
33998      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
33999      */
34000     resizeTabs : false,
34001     /*
34002      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34003      */
34004     monitorResize : true,
34005     /*
34006      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34007      */
34008     toolbar : false,
34009
34010     /**
34011      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34012      * @param {String} id The id of the div to use <b>or create</b>
34013      * @param {String} text The text for the tab
34014      * @param {String} content (optional) Content to put in the TabPanelItem body
34015      * @param {Boolean} closable (optional) True to create a close icon on the tab
34016      * @return {Roo.TabPanelItem} The created TabPanelItem
34017      */
34018     addTab : function(id, text, content, closable)
34019     {
34020         var item = new Roo.bootstrap.panel.TabItem({
34021             panel: this,
34022             id : id,
34023             text : text,
34024             closable : closable
34025         });
34026         this.addTabItem(item);
34027         if(content){
34028             item.setContent(content);
34029         }
34030         return item;
34031     },
34032
34033     /**
34034      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34035      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34036      * @return {Roo.TabPanelItem}
34037      */
34038     getTab : function(id){
34039         return this.items[id];
34040     },
34041
34042     /**
34043      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34044      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34045      */
34046     hideTab : function(id){
34047         var t = this.items[id];
34048         if(!t.isHidden()){
34049            t.setHidden(true);
34050            this.hiddenCount++;
34051            this.autoSizeTabs();
34052         }
34053     },
34054
34055     /**
34056      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34057      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34058      */
34059     unhideTab : function(id){
34060         var t = this.items[id];
34061         if(t.isHidden()){
34062            t.setHidden(false);
34063            this.hiddenCount--;
34064            this.autoSizeTabs();
34065         }
34066     },
34067
34068     /**
34069      * Adds an existing {@link Roo.TabPanelItem}.
34070      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34071      */
34072     addTabItem : function(item){
34073         this.items[item.id] = item;
34074         this.items.push(item);
34075       //  if(this.resizeTabs){
34076     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34077   //         this.autoSizeTabs();
34078 //        }else{
34079 //            item.autoSize();
34080        // }
34081     },
34082
34083     /**
34084      * Removes a {@link Roo.TabPanelItem}.
34085      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34086      */
34087     removeTab : function(id){
34088         var items = this.items;
34089         var tab = items[id];
34090         if(!tab) { return; }
34091         var index = items.indexOf(tab);
34092         if(this.active == tab && items.length > 1){
34093             var newTab = this.getNextAvailable(index);
34094             if(newTab) {
34095                 newTab.activate();
34096             }
34097         }
34098         this.stripEl.dom.removeChild(tab.pnode.dom);
34099         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34100             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34101         }
34102         items.splice(index, 1);
34103         delete this.items[tab.id];
34104         tab.fireEvent("close", tab);
34105         tab.purgeListeners();
34106         this.autoSizeTabs();
34107     },
34108
34109     getNextAvailable : function(start){
34110         var items = this.items;
34111         var index = start;
34112         // look for a next tab that will slide over to
34113         // replace the one being removed
34114         while(index < items.length){
34115             var item = items[++index];
34116             if(item && !item.isHidden()){
34117                 return item;
34118             }
34119         }
34120         // if one isn't found select the previous tab (on the left)
34121         index = start;
34122         while(index >= 0){
34123             var item = items[--index];
34124             if(item && !item.isHidden()){
34125                 return item;
34126             }
34127         }
34128         return null;
34129     },
34130
34131     /**
34132      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34133      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34134      */
34135     disableTab : function(id){
34136         var tab = this.items[id];
34137         if(tab && this.active != tab){
34138             tab.disable();
34139         }
34140     },
34141
34142     /**
34143      * Enables a {@link Roo.TabPanelItem} that is disabled.
34144      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34145      */
34146     enableTab : function(id){
34147         var tab = this.items[id];
34148         tab.enable();
34149     },
34150
34151     /**
34152      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34153      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34154      * @return {Roo.TabPanelItem} The TabPanelItem.
34155      */
34156     activate : function(id){
34157         var tab = this.items[id];
34158         if(!tab){
34159             return null;
34160         }
34161         if(tab == this.active || tab.disabled){
34162             return tab;
34163         }
34164         var e = {};
34165         this.fireEvent("beforetabchange", this, e, tab);
34166         if(e.cancel !== true && !tab.disabled){
34167             if(this.active){
34168                 this.active.hide();
34169             }
34170             this.active = this.items[id];
34171             this.active.show();
34172             this.fireEvent("tabchange", this, this.active);
34173         }
34174         return tab;
34175     },
34176
34177     /**
34178      * Gets the active {@link Roo.TabPanelItem}.
34179      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34180      */
34181     getActiveTab : function(){
34182         return this.active;
34183     },
34184
34185     /**
34186      * Updates the tab body element to fit the height of the container element
34187      * for overflow scrolling
34188      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34189      */
34190     syncHeight : function(targetHeight){
34191         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34192         var bm = this.bodyEl.getMargins();
34193         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34194         this.bodyEl.setHeight(newHeight);
34195         return newHeight;
34196     },
34197
34198     onResize : function(){
34199         if(this.monitorResize){
34200             this.autoSizeTabs();
34201         }
34202     },
34203
34204     /**
34205      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34206      */
34207     beginUpdate : function(){
34208         this.updating = true;
34209     },
34210
34211     /**
34212      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34213      */
34214     endUpdate : function(){
34215         this.updating = false;
34216         this.autoSizeTabs();
34217     },
34218
34219     /**
34220      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34221      */
34222     autoSizeTabs : function(){
34223         var count = this.items.length;
34224         var vcount = count - this.hiddenCount;
34225         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34226             return;
34227         }
34228         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34229         var availWidth = Math.floor(w / vcount);
34230         var b = this.stripBody;
34231         if(b.getWidth() > w){
34232             var tabs = this.items;
34233             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34234             if(availWidth < this.minTabWidth){
34235                 /*if(!this.sleft){    // incomplete scrolling code
34236                     this.createScrollButtons();
34237                 }
34238                 this.showScroll();
34239                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34240             }
34241         }else{
34242             if(this.currentTabWidth < this.preferredTabWidth){
34243                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34244             }
34245         }
34246     },
34247
34248     /**
34249      * Returns the number of tabs in this TabPanel.
34250      * @return {Number}
34251      */
34252      getCount : function(){
34253          return this.items.length;
34254      },
34255
34256     /**
34257      * Resizes all the tabs to the passed width
34258      * @param {Number} The new width
34259      */
34260     setTabWidth : function(width){
34261         this.currentTabWidth = width;
34262         for(var i = 0, len = this.items.length; i < len; i++) {
34263                 if(!this.items[i].isHidden()) {
34264                 this.items[i].setWidth(width);
34265             }
34266         }
34267     },
34268
34269     /**
34270      * Destroys this TabPanel
34271      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34272      */
34273     destroy : function(removeEl){
34274         Roo.EventManager.removeResizeListener(this.onResize, this);
34275         for(var i = 0, len = this.items.length; i < len; i++){
34276             this.items[i].purgeListeners();
34277         }
34278         if(removeEl === true){
34279             this.el.update("");
34280             this.el.remove();
34281         }
34282     },
34283     
34284     createStrip : function(container)
34285     {
34286         var strip = document.createElement("nav");
34287         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34288         container.appendChild(strip);
34289         return strip;
34290     },
34291     
34292     createStripList : function(strip)
34293     {
34294         // div wrapper for retard IE
34295         // returns the "tr" element.
34296         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34297         //'<div class="x-tabs-strip-wrap">'+
34298           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34299           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34300         return strip.firstChild; //.firstChild.firstChild.firstChild;
34301     },
34302     createBody : function(container)
34303     {
34304         var body = document.createElement("div");
34305         Roo.id(body, "tab-body");
34306         //Roo.fly(body).addClass("x-tabs-body");
34307         Roo.fly(body).addClass("tab-content");
34308         container.appendChild(body);
34309         return body;
34310     },
34311     createItemBody :function(bodyEl, id){
34312         var body = Roo.getDom(id);
34313         if(!body){
34314             body = document.createElement("div");
34315             body.id = id;
34316         }
34317         //Roo.fly(body).addClass("x-tabs-item-body");
34318         Roo.fly(body).addClass("tab-pane");
34319          bodyEl.insertBefore(body, bodyEl.firstChild);
34320         return body;
34321     },
34322     /** @private */
34323     createStripElements :  function(stripEl, text, closable)
34324     {
34325         var td = document.createElement("li"); // was td..
34326         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34327         //stripEl.appendChild(td);
34328         /*if(closable){
34329             td.className = "x-tabs-closable";
34330             if(!this.closeTpl){
34331                 this.closeTpl = new Roo.Template(
34332                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34333                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34334                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34335                 );
34336             }
34337             var el = this.closeTpl.overwrite(td, {"text": text});
34338             var close = el.getElementsByTagName("div")[0];
34339             var inner = el.getElementsByTagName("em")[0];
34340             return {"el": el, "close": close, "inner": inner};
34341         } else {
34342         */
34343         // not sure what this is..
34344             if(!this.tabTpl){
34345                 //this.tabTpl = new Roo.Template(
34346                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34347                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34348                 //);
34349                 this.tabTpl = new Roo.Template(
34350                    '<a href="#">' +
34351                    '<span unselectable="on"' +
34352                             (this.disableTooltips ? '' : ' title="{text}"') +
34353                             ' >{text}</span></span></a>'
34354                 );
34355                 
34356             }
34357             var el = this.tabTpl.overwrite(td, {"text": text});
34358             var inner = el.getElementsByTagName("span")[0];
34359             return {"el": el, "inner": inner};
34360         //}
34361     }
34362         
34363     
34364 });
34365
34366 /**
34367  * @class Roo.TabPanelItem
34368  * @extends Roo.util.Observable
34369  * Represents an individual item (tab plus body) in a TabPanel.
34370  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34371  * @param {String} id The id of this TabPanelItem
34372  * @param {String} text The text for the tab of this TabPanelItem
34373  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34374  */
34375 Roo.bootstrap.panel.TabItem = function(config){
34376     /**
34377      * The {@link Roo.TabPanel} this TabPanelItem belongs to
34378      * @type Roo.TabPanel
34379      */
34380     this.tabPanel = config.panel;
34381     /**
34382      * The id for this TabPanelItem
34383      * @type String
34384      */
34385     this.id = config.id;
34386     /** @private */
34387     this.disabled = false;
34388     /** @private */
34389     this.text = config.text;
34390     /** @private */
34391     this.loaded = false;
34392     this.closable = config.closable;
34393
34394     /**
34395      * The body element for this TabPanelItem.
34396      * @type Roo.Element
34397      */
34398     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34399     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34400     this.bodyEl.setStyle("display", "block");
34401     this.bodyEl.setStyle("zoom", "1");
34402     //this.hideAction();
34403
34404     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34405     /** @private */
34406     this.el = Roo.get(els.el);
34407     this.inner = Roo.get(els.inner, true);
34408     this.textEl = Roo.get(this.el.dom.firstChild, true);
34409     this.pnode = Roo.get(els.el.parentNode, true);
34410     this.el.on("mousedown", this.onTabMouseDown, this);
34411     this.el.on("click", this.onTabClick, this);
34412     /** @private */
34413     if(config.closable){
34414         var c = Roo.get(els.close, true);
34415         c.dom.title = this.closeText;
34416         c.addClassOnOver("close-over");
34417         c.on("click", this.closeClick, this);
34418      }
34419
34420     this.addEvents({
34421          /**
34422          * @event activate
34423          * Fires when this tab becomes the active tab.
34424          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34425          * @param {Roo.TabPanelItem} this
34426          */
34427         "activate": true,
34428         /**
34429          * @event beforeclose
34430          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34431          * @param {Roo.TabPanelItem} this
34432          * @param {Object} e Set cancel to true on this object to cancel the close.
34433          */
34434         "beforeclose": true,
34435         /**
34436          * @event close
34437          * Fires when this tab is closed.
34438          * @param {Roo.TabPanelItem} this
34439          */
34440          "close": true,
34441         /**
34442          * @event deactivate
34443          * Fires when this tab is no longer the active tab.
34444          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34445          * @param {Roo.TabPanelItem} this
34446          */
34447          "deactivate" : true
34448     });
34449     this.hidden = false;
34450
34451     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34452 };
34453
34454 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34455            {
34456     purgeListeners : function(){
34457        Roo.util.Observable.prototype.purgeListeners.call(this);
34458        this.el.removeAllListeners();
34459     },
34460     /**
34461      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34462      */
34463     show : function(){
34464         this.pnode.addClass("active");
34465         this.showAction();
34466         if(Roo.isOpera){
34467             this.tabPanel.stripWrap.repaint();
34468         }
34469         this.fireEvent("activate", this.tabPanel, this);
34470     },
34471
34472     /**
34473      * Returns true if this tab is the active tab.
34474      * @return {Boolean}
34475      */
34476     isActive : function(){
34477         return this.tabPanel.getActiveTab() == this;
34478     },
34479
34480     /**
34481      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34482      */
34483     hide : function(){
34484         this.pnode.removeClass("active");
34485         this.hideAction();
34486         this.fireEvent("deactivate", this.tabPanel, this);
34487     },
34488
34489     hideAction : function(){
34490         this.bodyEl.hide();
34491         this.bodyEl.setStyle("position", "absolute");
34492         this.bodyEl.setLeft("-20000px");
34493         this.bodyEl.setTop("-20000px");
34494     },
34495
34496     showAction : function(){
34497         this.bodyEl.setStyle("position", "relative");
34498         this.bodyEl.setTop("");
34499         this.bodyEl.setLeft("");
34500         this.bodyEl.show();
34501     },
34502
34503     /**
34504      * Set the tooltip for the tab.
34505      * @param {String} tooltip The tab's tooltip
34506      */
34507     setTooltip : function(text){
34508         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34509             this.textEl.dom.qtip = text;
34510             this.textEl.dom.removeAttribute('title');
34511         }else{
34512             this.textEl.dom.title = text;
34513         }
34514     },
34515
34516     onTabClick : function(e){
34517         e.preventDefault();
34518         this.tabPanel.activate(this.id);
34519     },
34520
34521     onTabMouseDown : function(e){
34522         e.preventDefault();
34523         this.tabPanel.activate(this.id);
34524     },
34525 /*
34526     getWidth : function(){
34527         return this.inner.getWidth();
34528     },
34529
34530     setWidth : function(width){
34531         var iwidth = width - this.pnode.getPadding("lr");
34532         this.inner.setWidth(iwidth);
34533         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34534         this.pnode.setWidth(width);
34535     },
34536 */
34537     /**
34538      * Show or hide the tab
34539      * @param {Boolean} hidden True to hide or false to show.
34540      */
34541     setHidden : function(hidden){
34542         this.hidden = hidden;
34543         this.pnode.setStyle("display", hidden ? "none" : "");
34544     },
34545
34546     /**
34547      * Returns true if this tab is "hidden"
34548      * @return {Boolean}
34549      */
34550     isHidden : function(){
34551         return this.hidden;
34552     },
34553
34554     /**
34555      * Returns the text for this tab
34556      * @return {String}
34557      */
34558     getText : function(){
34559         return this.text;
34560     },
34561     /*
34562     autoSize : function(){
34563         //this.el.beginMeasure();
34564         this.textEl.setWidth(1);
34565         /*
34566          *  #2804 [new] Tabs in Roojs
34567          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34568          */
34569         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34570         //this.el.endMeasure();
34571     //},
34572
34573     /**
34574      * Sets the text for the tab (Note: this also sets the tooltip text)
34575      * @param {String} text The tab's text and tooltip
34576      */
34577     setText : function(text){
34578         this.text = text;
34579         this.textEl.update(text);
34580         this.setTooltip(text);
34581         //if(!this.tabPanel.resizeTabs){
34582         //    this.autoSize();
34583         //}
34584     },
34585     /**
34586      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34587      */
34588     activate : function(){
34589         this.tabPanel.activate(this.id);
34590     },
34591
34592     /**
34593      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34594      */
34595     disable : function(){
34596         if(this.tabPanel.active != this){
34597             this.disabled = true;
34598             this.pnode.addClass("disabled");
34599         }
34600     },
34601
34602     /**
34603      * Enables this TabPanelItem if it was previously disabled.
34604      */
34605     enable : function(){
34606         this.disabled = false;
34607         this.pnode.removeClass("disabled");
34608     },
34609
34610     /**
34611      * Sets the content for this TabPanelItem.
34612      * @param {String} content The content
34613      * @param {Boolean} loadScripts true to look for and load scripts
34614      */
34615     setContent : function(content, loadScripts){
34616         this.bodyEl.update(content, loadScripts);
34617     },
34618
34619     /**
34620      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
34621      * @return {Roo.UpdateManager} The UpdateManager
34622      */
34623     getUpdateManager : function(){
34624         return this.bodyEl.getUpdateManager();
34625     },
34626
34627     /**
34628      * Set a URL to be used to load the content for this TabPanelItem.
34629      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
34630      * @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)
34631      * @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)
34632      * @return {Roo.UpdateManager} The UpdateManager
34633      */
34634     setUrl : function(url, params, loadOnce){
34635         if(this.refreshDelegate){
34636             this.un('activate', this.refreshDelegate);
34637         }
34638         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34639         this.on("activate", this.refreshDelegate);
34640         return this.bodyEl.getUpdateManager();
34641     },
34642
34643     /** @private */
34644     _handleRefresh : function(url, params, loadOnce){
34645         if(!loadOnce || !this.loaded){
34646             var updater = this.bodyEl.getUpdateManager();
34647             updater.update(url, params, this._setLoaded.createDelegate(this));
34648         }
34649     },
34650
34651     /**
34652      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
34653      *   Will fail silently if the setUrl method has not been called.
34654      *   This does not activate the panel, just updates its content.
34655      */
34656     refresh : function(){
34657         if(this.refreshDelegate){
34658            this.loaded = false;
34659            this.refreshDelegate();
34660         }
34661     },
34662
34663     /** @private */
34664     _setLoaded : function(){
34665         this.loaded = true;
34666     },
34667
34668     /** @private */
34669     closeClick : function(e){
34670         var o = {};
34671         e.stopEvent();
34672         this.fireEvent("beforeclose", this, o);
34673         if(o.cancel !== true){
34674             this.tabPanel.removeTab(this.id);
34675         }
34676     },
34677     /**
34678      * The text displayed in the tooltip for the close icon.
34679      * @type String
34680      */
34681     closeText : "Close this tab"
34682 });