Roo/bootstrap/Table.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     if (config.container) {
5676         // ctor'ed from a Border/panel.grid
5677         this.container = Roo.get(config.container);
5678         this.container.update("");
5679         this.container.setStyle("overflow", "hidden");
5680         this.container.addClass('x-grid-container');
5681
5682     }
5683     
5684     // BC...
5685     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5686     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5687     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5688     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5689     
5690     
5691     if (this.sm) {
5692         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5693         this.sm = this.selModel;
5694         this.sm.xmodule = this.xmodule || false;
5695     }
5696     if (this.cm && typeof(this.cm.config) == 'undefined') {
5697         this.colModel = new Roo.grid.ColumnModel(this.cm);
5698         this.cm = this.colModel;
5699         this.cm.xmodule = this.xmodule || false;
5700     }
5701     if (this.store) {
5702         this.store= Roo.factory(this.store, Roo.data);
5703         this.ds = this.store;
5704         this.ds.xmodule = this.xmodule || false;
5705          
5706     }
5707     if (this.footer && this.store) {
5708         this.footer.dataSource = this.ds;
5709         this.footer = Roo.factory(this.footer);
5710     }
5711     
5712     /** @private */
5713     this.addEvents({
5714         /**
5715          * @event cellclick
5716          * Fires when a cell is clicked
5717          * @param {Roo.bootstrap.Table} this
5718          * @param {Roo.Element} el
5719          * @param {Number} rowIndex
5720          * @param {Number} columnIndex
5721          * @param {Roo.EventObject} e
5722          */
5723         "cellclick" : true,
5724         /**
5725          * @event celldblclick
5726          * Fires when a cell is double clicked
5727          * @param {Roo.bootstrap.Table} this
5728          * @param {Roo.Element} el
5729          * @param {Number} rowIndex
5730          * @param {Number} columnIndex
5731          * @param {Roo.EventObject} e
5732          */
5733         "celldblclick" : true,
5734         /**
5735          * @event rowclick
5736          * Fires when a row is clicked
5737          * @param {Roo.bootstrap.Table} this
5738          * @param {Roo.Element} el
5739          * @param {Number} rowIndex
5740          * @param {Roo.EventObject} e
5741          */
5742         "rowclick" : true,
5743         /**
5744          * @event rowdblclick
5745          * Fires when a row is double clicked
5746          * @param {Roo.bootstrap.Table} this
5747          * @param {Roo.Element} el
5748          * @param {Number} rowIndex
5749          * @param {Roo.EventObject} e
5750          */
5751         "rowdblclick" : true,
5752         /**
5753          * @event mouseover
5754          * Fires when a mouseover occur
5755          * @param {Roo.bootstrap.Table} this
5756          * @param {Roo.Element} el
5757          * @param {Number} rowIndex
5758          * @param {Number} columnIndex
5759          * @param {Roo.EventObject} e
5760          */
5761         "mouseover" : true,
5762         /**
5763          * @event mouseout
5764          * Fires when a mouseout occur
5765          * @param {Roo.bootstrap.Table} this
5766          * @param {Roo.Element} el
5767          * @param {Number} rowIndex
5768          * @param {Number} columnIndex
5769          * @param {Roo.EventObject} e
5770          */
5771         "mouseout" : true,
5772         /**
5773          * @event rowclass
5774          * Fires when a row is rendered, so you can change add a style to it.
5775          * @param {Roo.bootstrap.Table} this
5776          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5777          */
5778         'rowclass' : true,
5779           /**
5780          * @event rowsrendered
5781          * Fires when all the  rows have been rendered
5782          * @param {Roo.bootstrap.Table} this
5783          */
5784         'rowsrendered' : true,
5785         /**
5786          * @event contextmenu
5787          * The raw contextmenu event for the entire grid.
5788          * @param {Roo.EventObject} e
5789          */
5790         "contextmenu" : true,
5791         /**
5792          * @event rowcontextmenu
5793          * Fires when a row is right clicked
5794          * @param {Roo.bootstrap.Table} this
5795          * @param {Number} rowIndex
5796          * @param {Roo.EventObject} e
5797          */
5798         "rowcontextmenu" : true,
5799         /**
5800          * @event cellcontextmenu
5801          * Fires when a cell is right clicked
5802          * @param {Roo.bootstrap.Table} this
5803          * @param {Number} rowIndex
5804          * @param {Number} cellIndex
5805          * @param {Roo.EventObject} e
5806          */
5807          "cellcontextmenu" : true,
5808          /**
5809          * @event headercontextmenu
5810          * Fires when a header is right clicked
5811          * @param {Roo.bootstrap.Table} this
5812          * @param {Number} columnIndex
5813          * @param {Roo.EventObject} e
5814          */
5815         "headercontextmenu" : true
5816     });
5817 };
5818
5819 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5820     
5821     cls: false,
5822     align: false,
5823     bgcolor: false,
5824     border: false,
5825     cellpadding: false,
5826     cellspacing: false,
5827     frame: false,
5828     rules: false,
5829     sortable: false,
5830     summary: false,
5831     width: false,
5832     striped : false,
5833     bordered: false,
5834     hover:  false,
5835     condensed : false,
5836     responsive : false,
5837     sm : false,
5838     cm : false,
5839     store : false,
5840     loadMask : false,
5841     footerShow : true,
5842     headerShow : true,
5843   
5844     rowSelection : false,
5845     cellSelection : false,
5846     layout : false,
5847     
5848     // Roo.Element - the tbody
5849     mainBody: false,
5850     
5851     
5852     container: false, // used by gridpanel...
5853     
5854     getAutoCreate : function()
5855     {
5856         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5857         
5858         cfg = {
5859             tag: 'table',
5860             cls : 'table table-body-fixed',
5861             cn : []
5862         };
5863             
5864         if (this.striped) {
5865             cfg.cls += ' table-striped';
5866         }
5867         
5868         if (this.hover) {
5869             cfg.cls += ' table-hover';
5870         }
5871         if (this.bordered) {
5872             cfg.cls += ' table-bordered';
5873         }
5874         if (this.condensed) {
5875             cfg.cls += ' table-condensed';
5876         }
5877         if (this.responsive) {
5878             cfg.cls += ' table-responsive';
5879         }
5880         
5881         if (this.cls) {
5882             cfg.cls+=  ' ' +this.cls;
5883         }
5884         
5885         // this lot should be simplifed...
5886         
5887         if (this.align) {
5888             cfg.align=this.align;
5889         }
5890         if (this.bgcolor) {
5891             cfg.bgcolor=this.bgcolor;
5892         }
5893         if (this.border) {
5894             cfg.border=this.border;
5895         }
5896         if (this.cellpadding) {
5897             cfg.cellpadding=this.cellpadding;
5898         }
5899         if (this.cellspacing) {
5900             cfg.cellspacing=this.cellspacing;
5901         }
5902         if (this.frame) {
5903             cfg.frame=this.frame;
5904         }
5905         if (this.rules) {
5906             cfg.rules=this.rules;
5907         }
5908         if (this.sortable) {
5909             cfg.sortable=this.sortable;
5910         }
5911         if (this.summary) {
5912             cfg.summary=this.summary;
5913         }
5914         if (this.width) {
5915             cfg.width=this.width;
5916         }
5917         if (this.layout) {
5918             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5919         }
5920         
5921         if(this.store || this.cm){
5922             if(this.headerShow){
5923                 cfg.cn.push(this.renderHeader());
5924             }
5925             
5926             cfg.cn.push(this.renderBody());
5927             
5928             if(this.footerShow){
5929                 cfg.cn.push(this.renderFooter());
5930             }
5931             // where does this come from?
5932             //cfg.cls+=  ' TableGrid';
5933         }
5934         
5935         return { cn : [ cfg ] };
5936     },
5937     
5938     initEvents : function()
5939     {   
5940         if(!this.store || !this.cm){
5941             return;
5942         }
5943         
5944         //Roo.log('initEvents with ds!!!!');
5945         
5946         this.mainBody = this.el.select('tbody', true).first();
5947         
5948         
5949         var _this = this;
5950         
5951         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5952             e.on('click', _this.sort, _this);
5953         });
5954         
5955         this.el.on("click", this.onClick, this);
5956         this.el.on("dblclick", this.onDblClick, this);
5957         
5958         // why is this done????? = it breaks dialogs??
5959         //this.parent().el.setStyle('position', 'relative');
5960         
5961         
5962         if (this.footer) {
5963             this.footer.parentId = this.id;
5964             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5965         }
5966         
5967         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5968         
5969         this.store.on('load', this.onLoad, this);
5970         this.store.on('beforeload', this.onBeforeLoad, this);
5971         this.store.on('update', this.onUpdate, this);
5972         this.store.on('add', this.onAdd, this);
5973         
5974         this.el.on("contextmenu", this.onContextMenu, this);
5975         
5976         
5977         
5978     },
5979     
5980     onContextMenu : function(e, t)
5981     {
5982         this.processEvent("contextmenu", e);
5983     },
5984     
5985     processEvent : function(name, e)
5986     {
5987         if (name != 'touchstart' ) {
5988             this.fireEvent(name, e);    
5989         }
5990         
5991         var t = e.getTarget();
5992         
5993         var cell = Roo.get(t);
5994         
5995         if(!cell){
5996             return;
5997         }
5998         
5999         if(cell.findParent('tfoot', false, true)){
6000             return;
6001         }
6002         
6003         if(cell.findParent('thead', false, true)){
6004             
6005             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6006                 cell = Roo.get(t).findParent('th', false, true);
6007                 if (!cell) {
6008                     Roo.log("failed to find th in thead?");
6009                     Roo.log(e.getTarget());
6010                     return;
6011                 }
6012             }
6013             
6014             var cellIndex = cell.dom.cellIndex;
6015             
6016             var ename = name == 'touchstart' ? 'click' : name;
6017             this.fireEvent("header" + ename, this, cellIndex, e);
6018             
6019             return;
6020         }
6021         
6022         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6023             cell = Roo.get(t).findParent('td', false, true);
6024             if (!cell) {
6025                 Roo.log("failed to find th in tbody?");
6026                 Roo.log(e.getTarget());
6027                 return;
6028             }
6029         }
6030         
6031         var row = cell.findParent('tr', false, true);
6032         var cellIndex = cell.dom.cellIndex;
6033         var rowIndex = row.dom.rowIndex - 1;
6034         
6035         if(row !== false){
6036             
6037             this.fireEvent("row" + name, this, rowIndex, e);
6038             
6039             if(cell !== false){
6040             
6041                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6042             }
6043         }
6044         
6045     },
6046     
6047     onMouseover : function(e, el)
6048     {
6049         var cell = Roo.get(el);
6050         
6051         if(!cell){
6052             return;
6053         }
6054         
6055         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6056             cell = cell.findParent('td', false, true);
6057         }
6058         
6059         var row = cell.findParent('tr', false, true);
6060         var cellIndex = cell.dom.cellIndex;
6061         var rowIndex = row.dom.rowIndex - 1; // start from 0
6062         
6063         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6064         
6065     },
6066     
6067     onMouseout : function(e, el)
6068     {
6069         var cell = Roo.get(el);
6070         
6071         if(!cell){
6072             return;
6073         }
6074         
6075         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6076             cell = cell.findParent('td', false, true);
6077         }
6078         
6079         var row = cell.findParent('tr', false, true);
6080         var cellIndex = cell.dom.cellIndex;
6081         var rowIndex = row.dom.rowIndex - 1; // start from 0
6082         
6083         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6084         
6085     },
6086     
6087     onClick : function(e, el)
6088     {
6089         var cell = Roo.get(el);
6090         
6091         if(!cell || (!this.cellSelection && !this.rowSelection)){
6092             return;
6093         }
6094         
6095         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6096             cell = cell.findParent('td', false, true);
6097         }
6098         
6099         if(!cell || typeof(cell) == 'undefined'){
6100             return;
6101         }
6102         
6103         var row = cell.findParent('tr', false, true);
6104         
6105         if(!row || typeof(row) == 'undefined'){
6106             return;
6107         }
6108         
6109         var cellIndex = cell.dom.cellIndex;
6110         var rowIndex = this.getRowIndex(row);
6111         
6112         // why??? - should these not be based on SelectionModel?
6113         if(this.cellSelection){
6114             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6115         }
6116         
6117         if(this.rowSelection){
6118             this.fireEvent('rowclick', this, row, rowIndex, e);
6119         }
6120         
6121         
6122     },
6123     
6124     onDblClick : function(e,el)
6125     {
6126         var cell = Roo.get(el);
6127         
6128         if(!cell || (!this.CellSelection && !this.RowSelection)){
6129             return;
6130         }
6131         
6132         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6133             cell = cell.findParent('td', false, true);
6134         }
6135         
6136         if(!cell || typeof(cell) == 'undefined'){
6137             return;
6138         }
6139         
6140         var row = cell.findParent('tr', false, true);
6141         
6142         if(!row || typeof(row) == 'undefined'){
6143             return;
6144         }
6145         
6146         var cellIndex = cell.dom.cellIndex;
6147         var rowIndex = this.getRowIndex(row);
6148         
6149         if(this.CellSelection){
6150             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6151         }
6152         
6153         if(this.RowSelection){
6154             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6155         }
6156     },
6157     
6158     sort : function(e,el)
6159     {
6160         var col = Roo.get(el);
6161         
6162         if(!col.hasClass('sortable')){
6163             return;
6164         }
6165         
6166         var sort = col.attr('sort');
6167         var dir = 'ASC';
6168         
6169         if(col.hasClass('glyphicon-arrow-up')){
6170             dir = 'DESC';
6171         }
6172         
6173         this.store.sortInfo = {field : sort, direction : dir};
6174         
6175         if (this.footer) {
6176             Roo.log("calling footer first");
6177             this.footer.onClick('first');
6178         } else {
6179         
6180             this.store.load({ params : { start : 0 } });
6181         }
6182     },
6183     
6184     renderHeader : function()
6185     {
6186         var header = {
6187             tag: 'thead',
6188             cn : []
6189         };
6190         
6191         var cm = this.cm;
6192         this.totalWidth = 0;
6193         
6194         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6195             
6196             var config = cm.config[i];
6197             
6198             var c = {
6199                 tag: 'th',
6200                 style : '',
6201                 html: cm.getColumnHeader(i)
6202             };
6203             
6204             var hh = '';
6205             
6206             if(typeof(config.lgHeader) != 'undefined'){
6207                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6208             }
6209             
6210             if(typeof(config.mdHeader) != 'undefined'){
6211                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6212             }
6213             
6214             if(typeof(config.smHeader) != 'undefined'){
6215                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6216             }
6217             
6218             if(typeof(config.xsHeader) != 'undefined'){
6219                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6220             }
6221             
6222             if(hh.length){
6223                 c.html = hh;
6224             }
6225             
6226             if(typeof(config.tooltip) != 'undefined'){
6227                 c.tooltip = config.tooltip;
6228             }
6229             
6230             if(typeof(config.colspan) != 'undefined'){
6231                 c.colspan = config.colspan;
6232             }
6233             
6234             if(typeof(config.hidden) != 'undefined' && config.hidden){
6235                 c.style += ' display:none;';
6236             }
6237             
6238             if(typeof(config.dataIndex) != 'undefined'){
6239                 c.sort = config.dataIndex;
6240             }
6241             
6242             if(typeof(config.sortable) != 'undefined' && config.sortable){
6243                 c.cls = 'sortable';
6244             }
6245             
6246             if(typeof(config.align) != 'undefined' && config.align.length){
6247                 c.style += ' text-align:' + config.align + ';';
6248             }
6249             
6250             if(typeof(config.width) != 'undefined'){
6251                 c.style += ' width:' + config.width + 'px;';
6252                 this.totalWidth += config.width;
6253             } else {
6254                 this.totalWidth += 100; // assume minimum of 100 per column?
6255             }
6256             
6257             if(typeof(config.cls) != 'undefined'){
6258                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6259             }
6260             
6261             ['xs','sm','md','lg'].map(function(size){
6262                 
6263                 if(typeof(config[size]) == 'undefined'){
6264                     return;
6265                 }
6266                 
6267                 if (!config[size]) { // 0 = hidden
6268                     c.cls += ' hidden-' + size;
6269                     return;
6270                 }
6271                 
6272                 c.cls += ' col-' + size + '-' + config[size];
6273
6274             });
6275             
6276             header.cn.push(c)
6277         }
6278         
6279         return header;
6280     },
6281     
6282     renderBody : function()
6283     {
6284         var body = {
6285             tag: 'tbody',
6286             cn : [
6287                 {
6288                     tag: 'tr',
6289                     cn : [
6290                         {
6291                             tag : 'td',
6292                             colspan :  this.cm.getColumnCount()
6293                         }
6294                     ]
6295                 }
6296             ]
6297         };
6298         
6299         return body;
6300     },
6301     
6302     renderFooter : function()
6303     {
6304         var footer = {
6305             tag: 'tfoot',
6306             cn : [
6307                 {
6308                     tag: 'tr',
6309                     cn : [
6310                         {
6311                             tag : 'td',
6312                             colspan :  this.cm.getColumnCount()
6313                         }
6314                     ]
6315                 }
6316             ]
6317         };
6318         
6319         return footer;
6320     },
6321     
6322     
6323     
6324     onLoad : function()
6325     {
6326 //        Roo.log('ds onload');
6327         this.clear();
6328         
6329         var _this = this;
6330         var cm = this.cm;
6331         var ds = this.store;
6332         
6333         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6334             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6335             if (_this.store.sortInfo) {
6336                     
6337                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6338                     e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6339                 }
6340                 
6341                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6342                     e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6343                 }
6344             }
6345         });
6346         
6347         var tbody =  this.mainBody;
6348               
6349         if(ds.getCount() > 0){
6350             ds.data.each(function(d,rowIndex){
6351                 var row =  this.renderRow(cm, ds, rowIndex);
6352                 
6353                 tbody.createChild(row);
6354                 
6355                 var _this = this;
6356                 
6357                 if(row.cellObjects.length){
6358                     Roo.each(row.cellObjects, function(r){
6359                         _this.renderCellObject(r);
6360                     })
6361                 }
6362                 
6363             }, this);
6364         }
6365         
6366         Roo.each(this.el.select('tbody td', true).elements, function(e){
6367             e.on('mouseover', _this.onMouseover, _this);
6368         });
6369         
6370         Roo.each(this.el.select('tbody td', true).elements, function(e){
6371             e.on('mouseout', _this.onMouseout, _this);
6372         });
6373         this.fireEvent('rowsrendered', this);
6374         //if(this.loadMask){
6375         //    this.maskEl.hide();
6376         //}
6377         
6378         this.autoSize();
6379     },
6380     
6381     
6382     onUpdate : function(ds,record)
6383     {
6384         this.refreshRow(record);
6385     },
6386     
6387     onRemove : function(ds, record, index, isUpdate){
6388         if(isUpdate !== true){
6389             this.fireEvent("beforerowremoved", this, index, record);
6390         }
6391         var bt = this.mainBody.dom;
6392         
6393         var rows = this.el.select('tbody > tr', true).elements;
6394         
6395         if(typeof(rows[index]) != 'undefined'){
6396             bt.removeChild(rows[index].dom);
6397         }
6398         
6399 //        if(bt.rows[index]){
6400 //            bt.removeChild(bt.rows[index]);
6401 //        }
6402         
6403         if(isUpdate !== true){
6404             //this.stripeRows(index);
6405             //this.syncRowHeights(index, index);
6406             //this.layout();
6407             this.fireEvent("rowremoved", this, index, record);
6408         }
6409     },
6410     
6411     onAdd : function(ds, records, rowIndex)
6412     {
6413         //Roo.log('on Add called');
6414         // - note this does not handle multiple adding very well..
6415         var bt = this.mainBody.dom;
6416         for (var i =0 ; i < records.length;i++) {
6417             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6418             //Roo.log(records[i]);
6419             //Roo.log(this.store.getAt(rowIndex+i));
6420             this.insertRow(this.store, rowIndex + i, false);
6421             return;
6422         }
6423         
6424     },
6425     
6426     
6427     refreshRow : function(record){
6428         var ds = this.store, index;
6429         if(typeof record == 'number'){
6430             index = record;
6431             record = ds.getAt(index);
6432         }else{
6433             index = ds.indexOf(record);
6434         }
6435         this.insertRow(ds, index, true);
6436         this.onRemove(ds, record, index+1, true);
6437         //this.syncRowHeights(index, index);
6438         //this.layout();
6439         this.fireEvent("rowupdated", this, index, record);
6440     },
6441     
6442     insertRow : function(dm, rowIndex, isUpdate){
6443         
6444         if(!isUpdate){
6445             this.fireEvent("beforerowsinserted", this, rowIndex);
6446         }
6447             //var s = this.getScrollState();
6448         var row = this.renderRow(this.cm, this.store, rowIndex);
6449         // insert before rowIndex..
6450         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6451         
6452         var _this = this;
6453                 
6454         if(row.cellObjects.length){
6455             Roo.each(row.cellObjects, function(r){
6456                 _this.renderCellObject(r);
6457             })
6458         }
6459             
6460         if(!isUpdate){
6461             this.fireEvent("rowsinserted", this, rowIndex);
6462             //this.syncRowHeights(firstRow, lastRow);
6463             //this.stripeRows(firstRow);
6464             //this.layout();
6465         }
6466         
6467     },
6468     
6469     
6470     getRowDom : function(rowIndex)
6471     {
6472         var rows = this.el.select('tbody > tr', true).elements;
6473         
6474         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6475         
6476     },
6477     // returns the object tree for a tr..
6478   
6479     
6480     renderRow : function(cm, ds, rowIndex) 
6481     {
6482         
6483         var d = ds.getAt(rowIndex);
6484         
6485         var row = {
6486             tag : 'tr',
6487             cn : []
6488         };
6489             
6490         var cellObjects = [];
6491         
6492         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6493             var config = cm.config[i];
6494             
6495             var renderer = cm.getRenderer(i);
6496             var value = '';
6497             var id = false;
6498             
6499             if(typeof(renderer) !== 'undefined'){
6500                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6501             }
6502             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6503             // and are rendered into the cells after the row is rendered - using the id for the element.
6504             
6505             if(typeof(value) === 'object'){
6506                 id = Roo.id();
6507                 cellObjects.push({
6508                     container : id,
6509                     cfg : value 
6510                 })
6511             }
6512             
6513             var rowcfg = {
6514                 record: d,
6515                 rowIndex : rowIndex,
6516                 colIndex : i,
6517                 rowClass : ''
6518             };
6519
6520             this.fireEvent('rowclass', this, rowcfg);
6521             
6522             var td = {
6523                 tag: 'td',
6524                 cls : rowcfg.rowClass,
6525                 style: '',
6526                 html: (typeof(value) === 'object') ? '' : value
6527             };
6528             
6529             if (id) {
6530                 td.id = id;
6531             }
6532             
6533             if(typeof(config.colspan) != 'undefined'){
6534                 td.colspan = config.colspan;
6535             }
6536             
6537             if(typeof(config.hidden) != 'undefined' && config.hidden){
6538                 td.style += ' display:none;';
6539             }
6540             
6541             if(typeof(config.align) != 'undefined' && config.align.length){
6542                 td.style += ' text-align:' + config.align + ';';
6543             }
6544             
6545             if(typeof(config.width) != 'undefined'){
6546                 td.style += ' width:' +  config.width + 'px;';
6547             }
6548             
6549             if(typeof(config.cursor) != 'undefined'){
6550                 td.style += ' cursor:' +  config.cursor + ';';
6551             }
6552             
6553             if(typeof(config.cls) != 'undefined'){
6554                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6555             }
6556             
6557             ['xs','sm','md','lg'].map(function(size){
6558                 
6559                 if(typeof(config[size]) == 'undefined'){
6560                     return;
6561                 }
6562                 
6563                 if (!config[size]) { // 0 = hidden
6564                     td.cls += ' hidden-' + size;
6565                     return;
6566                 }
6567                 
6568                 td.cls += ' col-' + size + '-' + config[size];
6569
6570             });
6571              
6572             row.cn.push(td);
6573            
6574         }
6575         
6576         row.cellObjects = cellObjects;
6577         
6578         return row;
6579           
6580     },
6581     
6582     
6583     
6584     onBeforeLoad : function()
6585     {
6586         //Roo.log('ds onBeforeLoad');
6587         
6588         //this.clear();
6589         
6590         //if(this.loadMask){
6591         //    this.maskEl.show();
6592         //}
6593     },
6594      /**
6595      * Remove all rows
6596      */
6597     clear : function()
6598     {
6599         this.el.select('tbody', true).first().dom.innerHTML = '';
6600     },
6601     /**
6602      * Show or hide a row.
6603      * @param {Number} rowIndex to show or hide
6604      * @param {Boolean} state hide
6605      */
6606     setRowVisibility : function(rowIndex, state)
6607     {
6608         var bt = this.mainBody.dom;
6609         
6610         var rows = this.el.select('tbody > tr', true).elements;
6611         
6612         if(typeof(rows[rowIndex]) == 'undefined'){
6613             return;
6614         }
6615         rows[rowIndex].dom.style.display = state ? '' : 'none';
6616     },
6617     
6618     
6619     getSelectionModel : function(){
6620         if(!this.selModel){
6621             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6622         }
6623         return this.selModel;
6624     },
6625     /*
6626      * Render the Roo.bootstrap object from renderder
6627      */
6628     renderCellObject : function(r)
6629     {
6630         var _this = this;
6631         
6632         var t = r.cfg.render(r.container);
6633         
6634         if(r.cfg.cn){
6635             Roo.each(r.cfg.cn, function(c){
6636                 var child = {
6637                     container: t.getChildContainer(),
6638                     cfg: c
6639                 };
6640                 _this.renderCellObject(child);
6641             })
6642         }
6643     },
6644     
6645     getRowIndex : function(row)
6646     {
6647         var rowIndex = -1;
6648         
6649         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6650             if(el != row){
6651                 return;
6652             }
6653             
6654             rowIndex = index;
6655         });
6656         
6657         return rowIndex;
6658     },
6659      /**
6660      * Returns the grid's underlying element = used by panel.Grid
6661      * @return {Element} The element
6662      */
6663     getGridEl : function(){
6664         return this.container;
6665     },
6666      /**
6667      * Forces a resize - used by panel.Grid
6668      * @return {Element} The element
6669      */
6670     autoSize : function(){
6671         var ctr = Roo.get(this.container.dom.parentElement);
6672         
6673         var thd = this.getGridEl().select('thead',true).first();
6674         var tbd = this.getGridEl().select('tbody', true).first();
6675         
6676         
6677         var cw = ctr.getWidth();
6678         
6679         if (tbd) {
6680             
6681             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6682             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6683             cw -= barsize;
6684         }
6685         cw = Math.max(cw, this.totalWidth);
6686         this.getGridEl().select('tr',true).setWidth(cw);
6687         
6688         return; // we doe not have a view in this design..
6689         if(this.rendered){
6690             this.view.layout();
6691             if(this.view.adjustForScroll){
6692                 this.view.adjustForScroll();
6693             }
6694         }
6695     }
6696 });
6697
6698  
6699
6700  /*
6701  * - LGPL
6702  *
6703  * table cell
6704  * 
6705  */
6706
6707 /**
6708  * @class Roo.bootstrap.TableCell
6709  * @extends Roo.bootstrap.Component
6710  * Bootstrap TableCell class
6711  * @cfg {String} html cell contain text
6712  * @cfg {String} cls cell class
6713  * @cfg {String} tag cell tag (td|th) default td
6714  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6715  * @cfg {String} align Aligns the content in a cell
6716  * @cfg {String} axis Categorizes cells
6717  * @cfg {String} bgcolor Specifies the background color of a cell
6718  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6719  * @cfg {Number} colspan Specifies the number of columns a cell should span
6720  * @cfg {String} headers Specifies one or more header cells a cell is related to
6721  * @cfg {Number} height Sets the height of a cell
6722  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6723  * @cfg {Number} rowspan Sets the number of rows a cell should span
6724  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6725  * @cfg {String} valign Vertical aligns the content in a cell
6726  * @cfg {Number} width Specifies the width of a cell
6727  * 
6728  * @constructor
6729  * Create a new TableCell
6730  * @param {Object} config The config object
6731  */
6732
6733 Roo.bootstrap.TableCell = function(config){
6734     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6735 };
6736
6737 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6738     
6739     html: false,
6740     cls: false,
6741     tag: false,
6742     abbr: false,
6743     align: false,
6744     axis: false,
6745     bgcolor: false,
6746     charoff: false,
6747     colspan: false,
6748     headers: false,
6749     height: false,
6750     nowrap: false,
6751     rowspan: false,
6752     scope: false,
6753     valign: false,
6754     width: false,
6755     
6756     
6757     getAutoCreate : function(){
6758         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6759         
6760         cfg = {
6761             tag: 'td'
6762         };
6763         
6764         if(this.tag){
6765             cfg.tag = this.tag;
6766         }
6767         
6768         if (this.html) {
6769             cfg.html=this.html
6770         }
6771         if (this.cls) {
6772             cfg.cls=this.cls
6773         }
6774         if (this.abbr) {
6775             cfg.abbr=this.abbr
6776         }
6777         if (this.align) {
6778             cfg.align=this.align
6779         }
6780         if (this.axis) {
6781             cfg.axis=this.axis
6782         }
6783         if (this.bgcolor) {
6784             cfg.bgcolor=this.bgcolor
6785         }
6786         if (this.charoff) {
6787             cfg.charoff=this.charoff
6788         }
6789         if (this.colspan) {
6790             cfg.colspan=this.colspan
6791         }
6792         if (this.headers) {
6793             cfg.headers=this.headers
6794         }
6795         if (this.height) {
6796             cfg.height=this.height
6797         }
6798         if (this.nowrap) {
6799             cfg.nowrap=this.nowrap
6800         }
6801         if (this.rowspan) {
6802             cfg.rowspan=this.rowspan
6803         }
6804         if (this.scope) {
6805             cfg.scope=this.scope
6806         }
6807         if (this.valign) {
6808             cfg.valign=this.valign
6809         }
6810         if (this.width) {
6811             cfg.width=this.width
6812         }
6813         
6814         
6815         return cfg;
6816     }
6817    
6818 });
6819
6820  
6821
6822  /*
6823  * - LGPL
6824  *
6825  * table row
6826  * 
6827  */
6828
6829 /**
6830  * @class Roo.bootstrap.TableRow
6831  * @extends Roo.bootstrap.Component
6832  * Bootstrap TableRow class
6833  * @cfg {String} cls row class
6834  * @cfg {String} align Aligns the content in a table row
6835  * @cfg {String} bgcolor Specifies a background color for a table row
6836  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6837  * @cfg {String} valign Vertical aligns the content in a table row
6838  * 
6839  * @constructor
6840  * Create a new TableRow
6841  * @param {Object} config The config object
6842  */
6843
6844 Roo.bootstrap.TableRow = function(config){
6845     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6846 };
6847
6848 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6849     
6850     cls: false,
6851     align: false,
6852     bgcolor: false,
6853     charoff: false,
6854     valign: false,
6855     
6856     getAutoCreate : function(){
6857         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6858         
6859         cfg = {
6860             tag: 'tr'
6861         };
6862             
6863         if(this.cls){
6864             cfg.cls = this.cls;
6865         }
6866         if(this.align){
6867             cfg.align = this.align;
6868         }
6869         if(this.bgcolor){
6870             cfg.bgcolor = this.bgcolor;
6871         }
6872         if(this.charoff){
6873             cfg.charoff = this.charoff;
6874         }
6875         if(this.valign){
6876             cfg.valign = this.valign;
6877         }
6878         
6879         return cfg;
6880     }
6881    
6882 });
6883
6884  
6885
6886  /*
6887  * - LGPL
6888  *
6889  * table body
6890  * 
6891  */
6892
6893 /**
6894  * @class Roo.bootstrap.TableBody
6895  * @extends Roo.bootstrap.Component
6896  * Bootstrap TableBody class
6897  * @cfg {String} cls element class
6898  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6899  * @cfg {String} align Aligns the content inside the element
6900  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6901  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6902  * 
6903  * @constructor
6904  * Create a new TableBody
6905  * @param {Object} config The config object
6906  */
6907
6908 Roo.bootstrap.TableBody = function(config){
6909     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6910 };
6911
6912 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6913     
6914     cls: false,
6915     tag: false,
6916     align: false,
6917     charoff: false,
6918     valign: false,
6919     
6920     getAutoCreate : function(){
6921         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6922         
6923         cfg = {
6924             tag: 'tbody'
6925         };
6926             
6927         if (this.cls) {
6928             cfg.cls=this.cls
6929         }
6930         if(this.tag){
6931             cfg.tag = this.tag;
6932         }
6933         
6934         if(this.align){
6935             cfg.align = this.align;
6936         }
6937         if(this.charoff){
6938             cfg.charoff = this.charoff;
6939         }
6940         if(this.valign){
6941             cfg.valign = this.valign;
6942         }
6943         
6944         return cfg;
6945     }
6946     
6947     
6948 //    initEvents : function()
6949 //    {
6950 //        
6951 //        if(!this.store){
6952 //            return;
6953 //        }
6954 //        
6955 //        this.store = Roo.factory(this.store, Roo.data);
6956 //        this.store.on('load', this.onLoad, this);
6957 //        
6958 //        this.store.load();
6959 //        
6960 //    },
6961 //    
6962 //    onLoad: function () 
6963 //    {   
6964 //        this.fireEvent('load', this);
6965 //    }
6966 //    
6967 //   
6968 });
6969
6970  
6971
6972  /*
6973  * Based on:
6974  * Ext JS Library 1.1.1
6975  * Copyright(c) 2006-2007, Ext JS, LLC.
6976  *
6977  * Originally Released Under LGPL - original licence link has changed is not relivant.
6978  *
6979  * Fork - LGPL
6980  * <script type="text/javascript">
6981  */
6982
6983 // as we use this in bootstrap.
6984 Roo.namespace('Roo.form');
6985  /**
6986  * @class Roo.form.Action
6987  * Internal Class used to handle form actions
6988  * @constructor
6989  * @param {Roo.form.BasicForm} el The form element or its id
6990  * @param {Object} config Configuration options
6991  */
6992
6993  
6994  
6995 // define the action interface
6996 Roo.form.Action = function(form, options){
6997     this.form = form;
6998     this.options = options || {};
6999 };
7000 /**
7001  * Client Validation Failed
7002  * @const 
7003  */
7004 Roo.form.Action.CLIENT_INVALID = 'client';
7005 /**
7006  * Server Validation Failed
7007  * @const 
7008  */
7009 Roo.form.Action.SERVER_INVALID = 'server';
7010  /**
7011  * Connect to Server Failed
7012  * @const 
7013  */
7014 Roo.form.Action.CONNECT_FAILURE = 'connect';
7015 /**
7016  * Reading Data from Server Failed
7017  * @const 
7018  */
7019 Roo.form.Action.LOAD_FAILURE = 'load';
7020
7021 Roo.form.Action.prototype = {
7022     type : 'default',
7023     failureType : undefined,
7024     response : undefined,
7025     result : undefined,
7026
7027     // interface method
7028     run : function(options){
7029
7030     },
7031
7032     // interface method
7033     success : function(response){
7034
7035     },
7036
7037     // interface method
7038     handleResponse : function(response){
7039
7040     },
7041
7042     // default connection failure
7043     failure : function(response){
7044         
7045         this.response = response;
7046         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7047         this.form.afterAction(this, false);
7048     },
7049
7050     processResponse : function(response){
7051         this.response = response;
7052         if(!response.responseText){
7053             return true;
7054         }
7055         this.result = this.handleResponse(response);
7056         return this.result;
7057     },
7058
7059     // utility functions used internally
7060     getUrl : function(appendParams){
7061         var url = this.options.url || this.form.url || this.form.el.dom.action;
7062         if(appendParams){
7063             var p = this.getParams();
7064             if(p){
7065                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7066             }
7067         }
7068         return url;
7069     },
7070
7071     getMethod : function(){
7072         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7073     },
7074
7075     getParams : function(){
7076         var bp = this.form.baseParams;
7077         var p = this.options.params;
7078         if(p){
7079             if(typeof p == "object"){
7080                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7081             }else if(typeof p == 'string' && bp){
7082                 p += '&' + Roo.urlEncode(bp);
7083             }
7084         }else if(bp){
7085             p = Roo.urlEncode(bp);
7086         }
7087         return p;
7088     },
7089
7090     createCallback : function(){
7091         return {
7092             success: this.success,
7093             failure: this.failure,
7094             scope: this,
7095             timeout: (this.form.timeout*1000),
7096             upload: this.form.fileUpload ? this.success : undefined
7097         };
7098     }
7099 };
7100
7101 Roo.form.Action.Submit = function(form, options){
7102     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7103 };
7104
7105 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7106     type : 'submit',
7107
7108     haveProgress : false,
7109     uploadComplete : false,
7110     
7111     // uploadProgress indicator.
7112     uploadProgress : function()
7113     {
7114         if (!this.form.progressUrl) {
7115             return;
7116         }
7117         
7118         if (!this.haveProgress) {
7119             Roo.MessageBox.progress("Uploading", "Uploading");
7120         }
7121         if (this.uploadComplete) {
7122            Roo.MessageBox.hide();
7123            return;
7124         }
7125         
7126         this.haveProgress = true;
7127    
7128         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7129         
7130         var c = new Roo.data.Connection();
7131         c.request({
7132             url : this.form.progressUrl,
7133             params: {
7134                 id : uid
7135             },
7136             method: 'GET',
7137             success : function(req){
7138                //console.log(data);
7139                 var rdata = false;
7140                 var edata;
7141                 try  {
7142                    rdata = Roo.decode(req.responseText)
7143                 } catch (e) {
7144                     Roo.log("Invalid data from server..");
7145                     Roo.log(edata);
7146                     return;
7147                 }
7148                 if (!rdata || !rdata.success) {
7149                     Roo.log(rdata);
7150                     Roo.MessageBox.alert(Roo.encode(rdata));
7151                     return;
7152                 }
7153                 var data = rdata.data;
7154                 
7155                 if (this.uploadComplete) {
7156                    Roo.MessageBox.hide();
7157                    return;
7158                 }
7159                    
7160                 if (data){
7161                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7162                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7163                     );
7164                 }
7165                 this.uploadProgress.defer(2000,this);
7166             },
7167        
7168             failure: function(data) {
7169                 Roo.log('progress url failed ');
7170                 Roo.log(data);
7171             },
7172             scope : this
7173         });
7174            
7175     },
7176     
7177     
7178     run : function()
7179     {
7180         // run get Values on the form, so it syncs any secondary forms.
7181         this.form.getValues();
7182         
7183         var o = this.options;
7184         var method = this.getMethod();
7185         var isPost = method == 'POST';
7186         if(o.clientValidation === false || this.form.isValid()){
7187             
7188             if (this.form.progressUrl) {
7189                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7190                     (new Date() * 1) + '' + Math.random());
7191                     
7192             } 
7193             
7194             
7195             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7196                 form:this.form.el.dom,
7197                 url:this.getUrl(!isPost),
7198                 method: method,
7199                 params:isPost ? this.getParams() : null,
7200                 isUpload: this.form.fileUpload
7201             }));
7202             
7203             this.uploadProgress();
7204
7205         }else if (o.clientValidation !== false){ // client validation failed
7206             this.failureType = Roo.form.Action.CLIENT_INVALID;
7207             this.form.afterAction(this, false);
7208         }
7209     },
7210
7211     success : function(response)
7212     {
7213         this.uploadComplete= true;
7214         if (this.haveProgress) {
7215             Roo.MessageBox.hide();
7216         }
7217         
7218         
7219         var result = this.processResponse(response);
7220         if(result === true || result.success){
7221             this.form.afterAction(this, true);
7222             return;
7223         }
7224         if(result.errors){
7225             this.form.markInvalid(result.errors);
7226             this.failureType = Roo.form.Action.SERVER_INVALID;
7227         }
7228         this.form.afterAction(this, false);
7229     },
7230     failure : function(response)
7231     {
7232         this.uploadComplete= true;
7233         if (this.haveProgress) {
7234             Roo.MessageBox.hide();
7235         }
7236         
7237         this.response = response;
7238         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7239         this.form.afterAction(this, false);
7240     },
7241     
7242     handleResponse : function(response){
7243         if(this.form.errorReader){
7244             var rs = this.form.errorReader.read(response);
7245             var errors = [];
7246             if(rs.records){
7247                 for(var i = 0, len = rs.records.length; i < len; i++) {
7248                     var r = rs.records[i];
7249                     errors[i] = r.data;
7250                 }
7251             }
7252             if(errors.length < 1){
7253                 errors = null;
7254             }
7255             return {
7256                 success : rs.success,
7257                 errors : errors
7258             };
7259         }
7260         var ret = false;
7261         try {
7262             ret = Roo.decode(response.responseText);
7263         } catch (e) {
7264             ret = {
7265                 success: false,
7266                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7267                 errors : []
7268             };
7269         }
7270         return ret;
7271         
7272     }
7273 });
7274
7275
7276 Roo.form.Action.Load = function(form, options){
7277     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7278     this.reader = this.form.reader;
7279 };
7280
7281 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7282     type : 'load',
7283
7284     run : function(){
7285         
7286         Roo.Ajax.request(Roo.apply(
7287                 this.createCallback(), {
7288                     method:this.getMethod(),
7289                     url:this.getUrl(false),
7290                     params:this.getParams()
7291         }));
7292     },
7293
7294     success : function(response){
7295         
7296         var result = this.processResponse(response);
7297         if(result === true || !result.success || !result.data){
7298             this.failureType = Roo.form.Action.LOAD_FAILURE;
7299             this.form.afterAction(this, false);
7300             return;
7301         }
7302         this.form.clearInvalid();
7303         this.form.setValues(result.data);
7304         this.form.afterAction(this, true);
7305     },
7306
7307     handleResponse : function(response){
7308         if(this.form.reader){
7309             var rs = this.form.reader.read(response);
7310             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7311             return {
7312                 success : rs.success,
7313                 data : data
7314             };
7315         }
7316         return Roo.decode(response.responseText);
7317     }
7318 });
7319
7320 Roo.form.Action.ACTION_TYPES = {
7321     'load' : Roo.form.Action.Load,
7322     'submit' : Roo.form.Action.Submit
7323 };/*
7324  * - LGPL
7325  *
7326  * form
7327  * 
7328  */
7329
7330 /**
7331  * @class Roo.bootstrap.Form
7332  * @extends Roo.bootstrap.Component
7333  * Bootstrap Form class
7334  * @cfg {String} method  GET | POST (default POST)
7335  * @cfg {String} labelAlign top | left (default top)
7336  * @cfg {String} align left  | right - for navbars
7337  * @cfg {Boolean} loadMask load mask when submit (default true)
7338
7339  * 
7340  * @constructor
7341  * Create a new Form
7342  * @param {Object} config The config object
7343  */
7344
7345
7346 Roo.bootstrap.Form = function(config){
7347     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7348     this.addEvents({
7349         /**
7350          * @event clientvalidation
7351          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7352          * @param {Form} this
7353          * @param {Boolean} valid true if the form has passed client-side validation
7354          */
7355         clientvalidation: true,
7356         /**
7357          * @event beforeaction
7358          * Fires before any action is performed. Return false to cancel the action.
7359          * @param {Form} this
7360          * @param {Action} action The action to be performed
7361          */
7362         beforeaction: true,
7363         /**
7364          * @event actionfailed
7365          * Fires when an action fails.
7366          * @param {Form} this
7367          * @param {Action} action The action that failed
7368          */
7369         actionfailed : true,
7370         /**
7371          * @event actioncomplete
7372          * Fires when an action is completed.
7373          * @param {Form} this
7374          * @param {Action} action The action that completed
7375          */
7376         actioncomplete : true
7377     });
7378     
7379 };
7380
7381 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7382       
7383      /**
7384      * @cfg {String} method
7385      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7386      */
7387     method : 'POST',
7388     /**
7389      * @cfg {String} url
7390      * The URL to use for form actions if one isn't supplied in the action options.
7391      */
7392     /**
7393      * @cfg {Boolean} fileUpload
7394      * Set to true if this form is a file upload.
7395      */
7396      
7397     /**
7398      * @cfg {Object} baseParams
7399      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7400      */
7401       
7402     /**
7403      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7404      */
7405     timeout: 30,
7406     /**
7407      * @cfg {Sting} align (left|right) for navbar forms
7408      */
7409     align : 'left',
7410
7411     // private
7412     activeAction : null,
7413  
7414     /**
7415      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7416      * element by passing it or its id or mask the form itself by passing in true.
7417      * @type Mixed
7418      */
7419     waitMsgTarget : false,
7420     
7421     loadMask : true,
7422     
7423     getAutoCreate : function(){
7424         
7425         var cfg = {
7426             tag: 'form',
7427             method : this.method || 'POST',
7428             id : this.id || Roo.id(),
7429             cls : ''
7430         };
7431         if (this.parent().xtype.match(/^Nav/)) {
7432             cfg.cls = 'navbar-form navbar-' + this.align;
7433             
7434         }
7435         
7436         if (this.labelAlign == 'left' ) {
7437             cfg.cls += ' form-horizontal';
7438         }
7439         
7440         
7441         return cfg;
7442     },
7443     initEvents : function()
7444     {
7445         this.el.on('submit', this.onSubmit, this);
7446         // this was added as random key presses on the form where triggering form submit.
7447         this.el.on('keypress', function(e) {
7448             if (e.getCharCode() != 13) {
7449                 return true;
7450             }
7451             // we might need to allow it for textareas.. and some other items.
7452             // check e.getTarget().
7453             
7454             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7455                 return true;
7456             }
7457         
7458             Roo.log("keypress blocked");
7459             
7460             e.preventDefault();
7461             return false;
7462         });
7463         
7464     },
7465     // private
7466     onSubmit : function(e){
7467         e.stopEvent();
7468     },
7469     
7470      /**
7471      * Returns true if client-side validation on the form is successful.
7472      * @return Boolean
7473      */
7474     isValid : function(){
7475         var items = this.getItems();
7476         var valid = true;
7477         items.each(function(f){
7478            if(!f.validate()){
7479                valid = false;
7480                
7481            }
7482         });
7483         return valid;
7484     },
7485     /**
7486      * Returns true if any fields in this form have changed since their original load.
7487      * @return Boolean
7488      */
7489     isDirty : function(){
7490         var dirty = false;
7491         var items = this.getItems();
7492         items.each(function(f){
7493            if(f.isDirty()){
7494                dirty = true;
7495                return false;
7496            }
7497            return true;
7498         });
7499         return dirty;
7500     },
7501      /**
7502      * Performs a predefined action (submit or load) or custom actions you define on this form.
7503      * @param {String} actionName The name of the action type
7504      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7505      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7506      * accept other config options):
7507      * <pre>
7508 Property          Type             Description
7509 ----------------  ---------------  ----------------------------------------------------------------------------------
7510 url               String           The url for the action (defaults to the form's url)
7511 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7512 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7513 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7514                                    validate the form on the client (defaults to false)
7515      * </pre>
7516      * @return {BasicForm} this
7517      */
7518     doAction : function(action, options){
7519         if(typeof action == 'string'){
7520             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7521         }
7522         if(this.fireEvent('beforeaction', this, action) !== false){
7523             this.beforeAction(action);
7524             action.run.defer(100, action);
7525         }
7526         return this;
7527     },
7528     
7529     // private
7530     beforeAction : function(action){
7531         var o = action.options;
7532         
7533         if(this.loadMask){
7534             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7535         }
7536         // not really supported yet.. ??
7537         
7538         //if(this.waitMsgTarget === true){
7539         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7540         //}else if(this.waitMsgTarget){
7541         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7542         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7543         //}else {
7544         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7545        // }
7546          
7547     },
7548
7549     // private
7550     afterAction : function(action, success){
7551         this.activeAction = null;
7552         var o = action.options;
7553         
7554         //if(this.waitMsgTarget === true){
7555             this.el.unmask();
7556         //}else if(this.waitMsgTarget){
7557         //    this.waitMsgTarget.unmask();
7558         //}else{
7559         //    Roo.MessageBox.updateProgress(1);
7560         //    Roo.MessageBox.hide();
7561        // }
7562         // 
7563         if(success){
7564             if(o.reset){
7565                 this.reset();
7566             }
7567             Roo.callback(o.success, o.scope, [this, action]);
7568             this.fireEvent('actioncomplete', this, action);
7569             
7570         }else{
7571             
7572             // failure condition..
7573             // we have a scenario where updates need confirming.
7574             // eg. if a locking scenario exists..
7575             // we look for { errors : { needs_confirm : true }} in the response.
7576             if (
7577                 (typeof(action.result) != 'undefined')  &&
7578                 (typeof(action.result.errors) != 'undefined')  &&
7579                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7580            ){
7581                 var _t = this;
7582                 Roo.log("not supported yet");
7583                  /*
7584                 
7585                 Roo.MessageBox.confirm(
7586                     "Change requires confirmation",
7587                     action.result.errorMsg,
7588                     function(r) {
7589                         if (r != 'yes') {
7590                             return;
7591                         }
7592                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7593                     }
7594                     
7595                 );
7596                 */
7597                 
7598                 
7599                 return;
7600             }
7601             
7602             Roo.callback(o.failure, o.scope, [this, action]);
7603             // show an error message if no failed handler is set..
7604             if (!this.hasListener('actionfailed')) {
7605                 Roo.log("need to add dialog support");
7606                 /*
7607                 Roo.MessageBox.alert("Error",
7608                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7609                         action.result.errorMsg :
7610                         "Saving Failed, please check your entries or try again"
7611                 );
7612                 */
7613             }
7614             
7615             this.fireEvent('actionfailed', this, action);
7616         }
7617         
7618     },
7619     /**
7620      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7621      * @param {String} id The value to search for
7622      * @return Field
7623      */
7624     findField : function(id){
7625         var items = this.getItems();
7626         var field = items.get(id);
7627         if(!field){
7628              items.each(function(f){
7629                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7630                     field = f;
7631                     return false;
7632                 }
7633                 return true;
7634             });
7635         }
7636         return field || null;
7637     },
7638      /**
7639      * Mark fields in this form invalid in bulk.
7640      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7641      * @return {BasicForm} this
7642      */
7643     markInvalid : function(errors){
7644         if(errors instanceof Array){
7645             for(var i = 0, len = errors.length; i < len; i++){
7646                 var fieldError = errors[i];
7647                 var f = this.findField(fieldError.id);
7648                 if(f){
7649                     f.markInvalid(fieldError.msg);
7650                 }
7651             }
7652         }else{
7653             var field, id;
7654             for(id in errors){
7655                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7656                     field.markInvalid(errors[id]);
7657                 }
7658             }
7659         }
7660         //Roo.each(this.childForms || [], function (f) {
7661         //    f.markInvalid(errors);
7662         //});
7663         
7664         return this;
7665     },
7666
7667     /**
7668      * Set values for fields in this form in bulk.
7669      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7670      * @return {BasicForm} this
7671      */
7672     setValues : function(values){
7673         if(values instanceof Array){ // array of objects
7674             for(var i = 0, len = values.length; i < len; i++){
7675                 var v = values[i];
7676                 var f = this.findField(v.id);
7677                 if(f){
7678                     f.setValue(v.value);
7679                     if(this.trackResetOnLoad){
7680                         f.originalValue = f.getValue();
7681                     }
7682                 }
7683             }
7684         }else{ // object hash
7685             var field, id;
7686             for(id in values){
7687                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7688                     
7689                     if (field.setFromData && 
7690                         field.valueField && 
7691                         field.displayField &&
7692                         // combos' with local stores can 
7693                         // be queried via setValue()
7694                         // to set their value..
7695                         (field.store && !field.store.isLocal)
7696                         ) {
7697                         // it's a combo
7698                         var sd = { };
7699                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7700                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7701                         field.setFromData(sd);
7702                         
7703                     } else {
7704                         field.setValue(values[id]);
7705                     }
7706                     
7707                     
7708                     if(this.trackResetOnLoad){
7709                         field.originalValue = field.getValue();
7710                     }
7711                 }
7712             }
7713         }
7714          
7715         //Roo.each(this.childForms || [], function (f) {
7716         //    f.setValues(values);
7717         //});
7718                 
7719         return this;
7720     },
7721
7722     /**
7723      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7724      * they are returned as an array.
7725      * @param {Boolean} asString
7726      * @return {Object}
7727      */
7728     getValues : function(asString){
7729         //if (this.childForms) {
7730             // copy values from the child forms
7731         //    Roo.each(this.childForms, function (f) {
7732         //        this.setValues(f.getValues());
7733         //    }, this);
7734         //}
7735         
7736         
7737         
7738         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7739         if(asString === true){
7740             return fs;
7741         }
7742         return Roo.urlDecode(fs);
7743     },
7744     
7745     /**
7746      * Returns the fields in this form as an object with key/value pairs. 
7747      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7748      * @return {Object}
7749      */
7750     getFieldValues : function(with_hidden)
7751     {
7752         var items = this.getItems();
7753         var ret = {};
7754         items.each(function(f){
7755             if (!f.getName()) {
7756                 return;
7757             }
7758             var v = f.getValue();
7759             if (f.inputType =='radio') {
7760                 if (typeof(ret[f.getName()]) == 'undefined') {
7761                     ret[f.getName()] = ''; // empty..
7762                 }
7763                 
7764                 if (!f.el.dom.checked) {
7765                     return;
7766                     
7767                 }
7768                 v = f.el.dom.value;
7769                 
7770             }
7771             
7772             // not sure if this supported any more..
7773             if ((typeof(v) == 'object') && f.getRawValue) {
7774                 v = f.getRawValue() ; // dates..
7775             }
7776             // combo boxes where name != hiddenName...
7777             if (f.name != f.getName()) {
7778                 ret[f.name] = f.getRawValue();
7779             }
7780             ret[f.getName()] = v;
7781         });
7782         
7783         return ret;
7784     },
7785
7786     /**
7787      * Clears all invalid messages in this form.
7788      * @return {BasicForm} this
7789      */
7790     clearInvalid : function(){
7791         var items = this.getItems();
7792         
7793         items.each(function(f){
7794            f.clearInvalid();
7795         });
7796         
7797         
7798         
7799         return this;
7800     },
7801
7802     /**
7803      * Resets this form.
7804      * @return {BasicForm} this
7805      */
7806     reset : function(){
7807         var items = this.getItems();
7808         items.each(function(f){
7809             f.reset();
7810         });
7811         
7812         Roo.each(this.childForms || [], function (f) {
7813             f.reset();
7814         });
7815        
7816         
7817         return this;
7818     },
7819     getItems : function()
7820     {
7821         var r=new Roo.util.MixedCollection(false, function(o){
7822             return o.id || (o.id = Roo.id());
7823         });
7824         var iter = function(el) {
7825             if (el.inputEl) {
7826                 r.add(el);
7827             }
7828             if (!el.items) {
7829                 return;
7830             }
7831             Roo.each(el.items,function(e) {
7832                 iter(e);
7833             });
7834             
7835             
7836         };
7837         
7838         iter(this);
7839         return r;
7840         
7841         
7842         
7843         
7844     }
7845     
7846 });
7847
7848  
7849 /*
7850  * Based on:
7851  * Ext JS Library 1.1.1
7852  * Copyright(c) 2006-2007, Ext JS, LLC.
7853  *
7854  * Originally Released Under LGPL - original licence link has changed is not relivant.
7855  *
7856  * Fork - LGPL
7857  * <script type="text/javascript">
7858  */
7859 /**
7860  * @class Roo.form.VTypes
7861  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7862  * @singleton
7863  */
7864 Roo.form.VTypes = function(){
7865     // closure these in so they are only created once.
7866     var alpha = /^[a-zA-Z_]+$/;
7867     var alphanum = /^[a-zA-Z0-9_]+$/;
7868     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7869     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7870
7871     // All these messages and functions are configurable
7872     return {
7873         /**
7874          * The function used to validate email addresses
7875          * @param {String} value The email address
7876          */
7877         'email' : function(v){
7878             return email.test(v);
7879         },
7880         /**
7881          * The error text to display when the email validation function returns false
7882          * @type String
7883          */
7884         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7885         /**
7886          * The keystroke filter mask to be applied on email input
7887          * @type RegExp
7888          */
7889         'emailMask' : /[a-z0-9_\.\-@]/i,
7890
7891         /**
7892          * The function used to validate URLs
7893          * @param {String} value The URL
7894          */
7895         'url' : function(v){
7896             return url.test(v);
7897         },
7898         /**
7899          * The error text to display when the url validation function returns false
7900          * @type String
7901          */
7902         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7903         
7904         /**
7905          * The function used to validate alpha values
7906          * @param {String} value The value
7907          */
7908         'alpha' : function(v){
7909             return alpha.test(v);
7910         },
7911         /**
7912          * The error text to display when the alpha validation function returns false
7913          * @type String
7914          */
7915         'alphaText' : 'This field should only contain letters and _',
7916         /**
7917          * The keystroke filter mask to be applied on alpha input
7918          * @type RegExp
7919          */
7920         'alphaMask' : /[a-z_]/i,
7921
7922         /**
7923          * The function used to validate alphanumeric values
7924          * @param {String} value The value
7925          */
7926         'alphanum' : function(v){
7927             return alphanum.test(v);
7928         },
7929         /**
7930          * The error text to display when the alphanumeric validation function returns false
7931          * @type String
7932          */
7933         'alphanumText' : 'This field should only contain letters, numbers and _',
7934         /**
7935          * The keystroke filter mask to be applied on alphanumeric input
7936          * @type RegExp
7937          */
7938         'alphanumMask' : /[a-z0-9_]/i
7939     };
7940 }();/*
7941  * - LGPL
7942  *
7943  * Input
7944  * 
7945  */
7946
7947 /**
7948  * @class Roo.bootstrap.Input
7949  * @extends Roo.bootstrap.Component
7950  * Bootstrap Input class
7951  * @cfg {Boolean} disabled is it disabled
7952  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7953  * @cfg {String} name name of the input
7954  * @cfg {string} fieldLabel - the label associated
7955  * @cfg {string} placeholder - placeholder to put in text.
7956  * @cfg {string}  before - input group add on before
7957  * @cfg {string} after - input group add on after
7958  * @cfg {string} size - (lg|sm) or leave empty..
7959  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7960  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7961  * @cfg {Number} md colspan out of 12 for computer-sized screens
7962  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7963  * @cfg {string} value default value of the input
7964  * @cfg {Number} labelWidth set the width of label (0-12)
7965  * @cfg {String} labelAlign (top|left)
7966  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7967  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7968
7969  * @cfg {String} align (left|center|right) Default left
7970  * @cfg {Boolean} forceFeedback (true|false) Default false
7971  * 
7972  * 
7973  * 
7974  * 
7975  * @constructor
7976  * Create a new Input
7977  * @param {Object} config The config object
7978  */
7979
7980 Roo.bootstrap.Input = function(config){
7981     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7982    
7983         this.addEvents({
7984             /**
7985              * @event focus
7986              * Fires when this field receives input focus.
7987              * @param {Roo.form.Field} this
7988              */
7989             focus : true,
7990             /**
7991              * @event blur
7992              * Fires when this field loses input focus.
7993              * @param {Roo.form.Field} this
7994              */
7995             blur : true,
7996             /**
7997              * @event specialkey
7998              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7999              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8000              * @param {Roo.form.Field} this
8001              * @param {Roo.EventObject} e The event object
8002              */
8003             specialkey : true,
8004             /**
8005              * @event change
8006              * Fires just before the field blurs if the field value has changed.
8007              * @param {Roo.form.Field} this
8008              * @param {Mixed} newValue The new value
8009              * @param {Mixed} oldValue The original value
8010              */
8011             change : true,
8012             /**
8013              * @event invalid
8014              * Fires after the field has been marked as invalid.
8015              * @param {Roo.form.Field} this
8016              * @param {String} msg The validation message
8017              */
8018             invalid : true,
8019             /**
8020              * @event valid
8021              * Fires after the field has been validated with no errors.
8022              * @param {Roo.form.Field} this
8023              */
8024             valid : true,
8025              /**
8026              * @event keyup
8027              * Fires after the key up
8028              * @param {Roo.form.Field} this
8029              * @param {Roo.EventObject}  e The event Object
8030              */
8031             keyup : true
8032         });
8033 };
8034
8035 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8036      /**
8037      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8038       automatic validation (defaults to "keyup").
8039      */
8040     validationEvent : "keyup",
8041      /**
8042      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8043      */
8044     validateOnBlur : true,
8045     /**
8046      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8047      */
8048     validationDelay : 250,
8049      /**
8050      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8051      */
8052     focusClass : "x-form-focus",  // not needed???
8053     
8054        
8055     /**
8056      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8057      */
8058     invalidClass : "has-warning",
8059     
8060     /**
8061      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8062      */
8063     validClass : "has-success",
8064     
8065     /**
8066      * @cfg {Boolean} hasFeedback (true|false) default true
8067      */
8068     hasFeedback : true,
8069     
8070     /**
8071      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8072      */
8073     invalidFeedbackClass : "glyphicon-warning-sign",
8074     
8075     /**
8076      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8077      */
8078     validFeedbackClass : "glyphicon-ok",
8079     
8080     /**
8081      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8082      */
8083     selectOnFocus : false,
8084     
8085      /**
8086      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8087      */
8088     maskRe : null,
8089        /**
8090      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8091      */
8092     vtype : null,
8093     
8094       /**
8095      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8096      */
8097     disableKeyFilter : false,
8098     
8099        /**
8100      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8101      */
8102     disabled : false,
8103      /**
8104      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8105      */
8106     allowBlank : true,
8107     /**
8108      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8109      */
8110     blankText : "This field is required",
8111     
8112      /**
8113      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8114      */
8115     minLength : 0,
8116     /**
8117      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8118      */
8119     maxLength : Number.MAX_VALUE,
8120     /**
8121      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8122      */
8123     minLengthText : "The minimum length for this field is {0}",
8124     /**
8125      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8126      */
8127     maxLengthText : "The maximum length for this field is {0}",
8128   
8129     
8130     /**
8131      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8132      * If available, this function will be called only after the basic validators all return true, and will be passed the
8133      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8134      */
8135     validator : null,
8136     /**
8137      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8138      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8139      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8140      */
8141     regex : null,
8142     /**
8143      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8144      */
8145     regexText : "",
8146     
8147     autocomplete: false,
8148     
8149     
8150     fieldLabel : '',
8151     inputType : 'text',
8152     
8153     name : false,
8154     placeholder: false,
8155     before : false,
8156     after : false,
8157     size : false,
8158     hasFocus : false,
8159     preventMark: false,
8160     isFormField : true,
8161     value : '',
8162     labelWidth : 2,
8163     labelAlign : false,
8164     readOnly : false,
8165     align : false,
8166     formatedValue : false,
8167     forceFeedback : false,
8168     
8169     parentLabelAlign : function()
8170     {
8171         var parent = this;
8172         while (parent.parent()) {
8173             parent = parent.parent();
8174             if (typeof(parent.labelAlign) !='undefined') {
8175                 return parent.labelAlign;
8176             }
8177         }
8178         return 'left';
8179         
8180     },
8181     
8182     getAutoCreate : function(){
8183         
8184         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8185         
8186         var id = Roo.id();
8187         
8188         var cfg = {};
8189         
8190         if(this.inputType != 'hidden'){
8191             cfg.cls = 'form-group' //input-group
8192         }
8193         
8194         var input =  {
8195             tag: 'input',
8196             id : id,
8197             type : this.inputType,
8198             value : this.value,
8199             cls : 'form-control',
8200             placeholder : this.placeholder || '',
8201             autocomplete : this.autocomplete || 'new-password'
8202         };
8203         
8204         
8205         if(this.align){
8206             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8207         }
8208         
8209         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8210             input.maxLength = this.maxLength;
8211         }
8212         
8213         if (this.disabled) {
8214             input.disabled=true;
8215         }
8216         
8217         if (this.readOnly) {
8218             input.readonly=true;
8219         }
8220         
8221         if (this.name) {
8222             input.name = this.name;
8223         }
8224         if (this.size) {
8225             input.cls += ' input-' + this.size;
8226         }
8227         var settings=this;
8228         ['xs','sm','md','lg'].map(function(size){
8229             if (settings[size]) {
8230                 cfg.cls += ' col-' + size + '-' + settings[size];
8231             }
8232         });
8233         
8234         var inputblock = input;
8235         
8236         var feedback = {
8237             tag: 'span',
8238             cls: 'glyphicon form-control-feedback'
8239         };
8240             
8241         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8242             
8243             inputblock = {
8244                 cls : 'has-feedback',
8245                 cn :  [
8246                     input,
8247                     feedback
8248                 ] 
8249             };  
8250         }
8251         
8252         if (this.before || this.after) {
8253             
8254             inputblock = {
8255                 cls : 'input-group',
8256                 cn :  [] 
8257             };
8258             
8259             if (this.before && typeof(this.before) == 'string') {
8260                 
8261                 inputblock.cn.push({
8262                     tag :'span',
8263                     cls : 'roo-input-before input-group-addon',
8264                     html : this.before
8265                 });
8266             }
8267             if (this.before && typeof(this.before) == 'object') {
8268                 this.before = Roo.factory(this.before);
8269                 
8270                 inputblock.cn.push({
8271                     tag :'span',
8272                     cls : 'roo-input-before input-group-' +
8273                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8274                 });
8275             }
8276             
8277             inputblock.cn.push(input);
8278             
8279             if (this.after && typeof(this.after) == 'string') {
8280                 inputblock.cn.push({
8281                     tag :'span',
8282                     cls : 'roo-input-after input-group-addon',
8283                     html : this.after
8284                 });
8285             }
8286             if (this.after && typeof(this.after) == 'object') {
8287                 this.after = Roo.factory(this.after);
8288                 
8289                 inputblock.cn.push({
8290                     tag :'span',
8291                     cls : 'roo-input-after input-group-' +
8292                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8293                 });
8294             }
8295             
8296             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8297                 inputblock.cls += ' has-feedback';
8298                 inputblock.cn.push(feedback);
8299             }
8300         };
8301         
8302         if (align ==='left' && this.fieldLabel.length) {
8303                 
8304                 cfg.cn = [
8305                     
8306                     {
8307                         tag: 'label',
8308                         'for' :  id,
8309                         cls : 'control-label col-sm-' + this.labelWidth,
8310                         html : this.fieldLabel
8311                         
8312                     },
8313                     {
8314                         cls : "col-sm-" + (12 - this.labelWidth), 
8315                         cn: [
8316                             inputblock
8317                         ]
8318                     }
8319                     
8320                 ];
8321         } else if ( this.fieldLabel.length) {
8322                 
8323                  cfg.cn = [
8324                    
8325                     {
8326                         tag: 'label',
8327                         //cls : 'input-group-addon',
8328                         html : this.fieldLabel
8329                         
8330                     },
8331                     
8332                     inputblock
8333                     
8334                 ];
8335
8336         } else {
8337             
8338                 cfg.cn = [
8339                     
8340                         inputblock
8341                     
8342                 ];
8343                 
8344                 
8345         };
8346         
8347         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8348            cfg.cls += ' navbar-form';
8349         }
8350         
8351         return cfg;
8352         
8353     },
8354     /**
8355      * return the real input element.
8356      */
8357     inputEl: function ()
8358     {
8359         return this.el.select('input.form-control',true).first();
8360     },
8361     
8362     tooltipEl : function()
8363     {
8364         return this.inputEl();
8365     },
8366     
8367     setDisabled : function(v)
8368     {
8369         var i  = this.inputEl().dom;
8370         if (!v) {
8371             i.removeAttribute('disabled');
8372             return;
8373             
8374         }
8375         i.setAttribute('disabled','true');
8376     },
8377     initEvents : function()
8378     {
8379           
8380         this.inputEl().on("keydown" , this.fireKey,  this);
8381         this.inputEl().on("focus", this.onFocus,  this);
8382         this.inputEl().on("blur", this.onBlur,  this);
8383         
8384         this.inputEl().relayEvent('keyup', this);
8385  
8386         // reference to original value for reset
8387         this.originalValue = this.getValue();
8388         //Roo.form.TextField.superclass.initEvents.call(this);
8389         if(this.validationEvent == 'keyup'){
8390             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8391             this.inputEl().on('keyup', this.filterValidation, this);
8392         }
8393         else if(this.validationEvent !== false){
8394             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8395         }
8396         
8397         if(this.selectOnFocus){
8398             this.on("focus", this.preFocus, this);
8399             
8400         }
8401         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8402             this.inputEl().on("keypress", this.filterKeys, this);
8403         }
8404        /* if(this.grow){
8405             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8406             this.el.on("click", this.autoSize,  this);
8407         }
8408         */
8409         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8410             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8411         }
8412         
8413         if (typeof(this.before) == 'object') {
8414             this.before.render(this.el.select('.roo-input-before',true).first());
8415         }
8416         if (typeof(this.after) == 'object') {
8417             this.after.render(this.el.select('.roo-input-after',true).first());
8418         }
8419         
8420         
8421     },
8422     filterValidation : function(e){
8423         if(!e.isNavKeyPress()){
8424             this.validationTask.delay(this.validationDelay);
8425         }
8426     },
8427      /**
8428      * Validates the field value
8429      * @return {Boolean} True if the value is valid, else false
8430      */
8431     validate : function(){
8432         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8433         if(this.disabled || this.validateValue(this.getRawValue())){
8434             this.markValid();
8435             return true;
8436         }
8437         
8438         this.markInvalid();
8439         return false;
8440     },
8441     
8442     
8443     /**
8444      * Validates a value according to the field's validation rules and marks the field as invalid
8445      * if the validation fails
8446      * @param {Mixed} value The value to validate
8447      * @return {Boolean} True if the value is valid, else false
8448      */
8449     validateValue : function(value){
8450         if(value.length < 1)  { // if it's blank
8451             if(this.allowBlank){
8452                 return true;
8453             }
8454             return false;
8455         }
8456         
8457         if(value.length < this.minLength){
8458             return false;
8459         }
8460         if(value.length > this.maxLength){
8461             return false;
8462         }
8463         if(this.vtype){
8464             var vt = Roo.form.VTypes;
8465             if(!vt[this.vtype](value, this)){
8466                 return false;
8467             }
8468         }
8469         if(typeof this.validator == "function"){
8470             var msg = this.validator(value);
8471             if(msg !== true){
8472                 return false;
8473             }
8474         }
8475         
8476         if(this.regex && !this.regex.test(value)){
8477             return false;
8478         }
8479         
8480         return true;
8481     },
8482
8483     
8484     
8485      // private
8486     fireKey : function(e){
8487         //Roo.log('field ' + e.getKey());
8488         if(e.isNavKeyPress()){
8489             this.fireEvent("specialkey", this, e);
8490         }
8491     },
8492     focus : function (selectText){
8493         if(this.rendered){
8494             this.inputEl().focus();
8495             if(selectText === true){
8496                 this.inputEl().dom.select();
8497             }
8498         }
8499         return this;
8500     } ,
8501     
8502     onFocus : function(){
8503         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8504            // this.el.addClass(this.focusClass);
8505         }
8506         if(!this.hasFocus){
8507             this.hasFocus = true;
8508             this.startValue = this.getValue();
8509             this.fireEvent("focus", this);
8510         }
8511     },
8512     
8513     beforeBlur : Roo.emptyFn,
8514
8515     
8516     // private
8517     onBlur : function(){
8518         this.beforeBlur();
8519         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8520             //this.el.removeClass(this.focusClass);
8521         }
8522         this.hasFocus = false;
8523         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8524             this.validate();
8525         }
8526         var v = this.getValue();
8527         if(String(v) !== String(this.startValue)){
8528             this.fireEvent('change', this, v, this.startValue);
8529         }
8530         this.fireEvent("blur", this);
8531     },
8532     
8533     /**
8534      * Resets the current field value to the originally loaded value and clears any validation messages
8535      */
8536     reset : function(){
8537         this.setValue(this.originalValue);
8538         this.validate();
8539     },
8540      /**
8541      * Returns the name of the field
8542      * @return {Mixed} name The name field
8543      */
8544     getName: function(){
8545         return this.name;
8546     },
8547      /**
8548      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8549      * @return {Mixed} value The field value
8550      */
8551     getValue : function(){
8552         
8553         var v = this.inputEl().getValue();
8554         
8555         return v;
8556     },
8557     /**
8558      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8559      * @return {Mixed} value The field value
8560      */
8561     getRawValue : function(){
8562         var v = this.inputEl().getValue();
8563         
8564         return v;
8565     },
8566     
8567     /**
8568      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8569      * @param {Mixed} value The value to set
8570      */
8571     setRawValue : function(v){
8572         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8573     },
8574     
8575     selectText : function(start, end){
8576         var v = this.getRawValue();
8577         if(v.length > 0){
8578             start = start === undefined ? 0 : start;
8579             end = end === undefined ? v.length : end;
8580             var d = this.inputEl().dom;
8581             if(d.setSelectionRange){
8582                 d.setSelectionRange(start, end);
8583             }else if(d.createTextRange){
8584                 var range = d.createTextRange();
8585                 range.moveStart("character", start);
8586                 range.moveEnd("character", v.length-end);
8587                 range.select();
8588             }
8589         }
8590     },
8591     
8592     /**
8593      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8594      * @param {Mixed} value The value to set
8595      */
8596     setValue : function(v){
8597         this.value = v;
8598         if(this.rendered){
8599             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8600             this.validate();
8601         }
8602     },
8603     
8604     /*
8605     processValue : function(value){
8606         if(this.stripCharsRe){
8607             var newValue = value.replace(this.stripCharsRe, '');
8608             if(newValue !== value){
8609                 this.setRawValue(newValue);
8610                 return newValue;
8611             }
8612         }
8613         return value;
8614     },
8615   */
8616     preFocus : function(){
8617         
8618         if(this.selectOnFocus){
8619             this.inputEl().dom.select();
8620         }
8621     },
8622     filterKeys : function(e){
8623         var k = e.getKey();
8624         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8625             return;
8626         }
8627         var c = e.getCharCode(), cc = String.fromCharCode(c);
8628         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8629             return;
8630         }
8631         if(!this.maskRe.test(cc)){
8632             e.stopEvent();
8633         }
8634     },
8635      /**
8636      * Clear any invalid styles/messages for this field
8637      */
8638     clearInvalid : function(){
8639         
8640         if(!this.el || this.preventMark){ // not rendered
8641             return;
8642         }
8643         
8644         var label = this.el.select('label', true).first();
8645         var icon = this.el.select('i.fa-star', true).first();
8646         
8647         if(label && icon){
8648             icon.remove();
8649         }
8650         
8651         this.el.removeClass(this.invalidClass);
8652         
8653         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8654             
8655             var feedback = this.el.select('.form-control-feedback', true).first();
8656             
8657             if(feedback){
8658                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8659             }
8660             
8661         }
8662         
8663         this.fireEvent('valid', this);
8664     },
8665     
8666      /**
8667      * Mark this field as valid
8668      */
8669     markValid : function()
8670     {
8671         if(!this.el  || this.preventMark){ // not rendered
8672             return;
8673         }
8674         
8675         this.el.removeClass([this.invalidClass, this.validClass]);
8676         
8677         var feedback = this.el.select('.form-control-feedback', true).first();
8678             
8679         if(feedback){
8680             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8681         }
8682
8683         if(this.disabled || this.allowBlank){
8684             return;
8685         }
8686         
8687         var formGroup = this.el.findParent('.form-group', false, true);
8688         
8689         if(formGroup){
8690             
8691             var label = formGroup.select('label', true).first();
8692             var icon = formGroup.select('i.fa-star', true).first();
8693             
8694             if(label && icon){
8695                 icon.remove();
8696             }
8697         }
8698         
8699         this.el.addClass(this.validClass);
8700         
8701         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8702             
8703             var feedback = this.el.select('.form-control-feedback', true).first();
8704             
8705             if(feedback){
8706                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8707                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8708             }
8709             
8710         }
8711         
8712         this.fireEvent('valid', this);
8713     },
8714     
8715      /**
8716      * Mark this field as invalid
8717      * @param {String} msg The validation message
8718      */
8719     markInvalid : function(msg)
8720     {
8721         if(!this.el  || this.preventMark){ // not rendered
8722             return;
8723         }
8724         
8725         this.el.removeClass([this.invalidClass, this.validClass]);
8726         
8727         var feedback = this.el.select('.form-control-feedback', true).first();
8728             
8729         if(feedback){
8730             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8731         }
8732
8733         if(this.disabled || this.allowBlank){
8734             return;
8735         }
8736         
8737         var formGroup = this.el.findParent('.form-group', false, true);
8738         
8739         if(formGroup){
8740             var label = formGroup.select('label', true).first();
8741             var icon = formGroup.select('i.fa-star', true).first();
8742
8743             if(!this.getValue().length && label && !icon){
8744                 this.el.findParent('.form-group', false, true).createChild({
8745                     tag : 'i',
8746                     cls : 'text-danger fa fa-lg fa-star',
8747                     tooltip : 'This field is required',
8748                     style : 'margin-right:5px;'
8749                 }, label, true);
8750             }
8751         }
8752         
8753         
8754         this.el.addClass(this.invalidClass);
8755         
8756         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8757             
8758             var feedback = this.el.select('.form-control-feedback', true).first();
8759             
8760             if(feedback){
8761                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8762                 
8763                 if(this.getValue().length || this.forceFeedback){
8764                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8765                 }
8766                 
8767             }
8768             
8769         }
8770         
8771         this.fireEvent('invalid', this, msg);
8772     },
8773     // private
8774     SafariOnKeyDown : function(event)
8775     {
8776         // this is a workaround for a password hang bug on chrome/ webkit.
8777         
8778         var isSelectAll = false;
8779         
8780         if(this.inputEl().dom.selectionEnd > 0){
8781             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8782         }
8783         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8784             event.preventDefault();
8785             this.setValue('');
8786             return;
8787         }
8788         
8789         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8790             
8791             event.preventDefault();
8792             // this is very hacky as keydown always get's upper case.
8793             //
8794             var cc = String.fromCharCode(event.getCharCode());
8795             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8796             
8797         }
8798     },
8799     adjustWidth : function(tag, w){
8800         tag = tag.toLowerCase();
8801         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8802             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8803                 if(tag == 'input'){
8804                     return w + 2;
8805                 }
8806                 if(tag == 'textarea'){
8807                     return w-2;
8808                 }
8809             }else if(Roo.isOpera){
8810                 if(tag == 'input'){
8811                     return w + 2;
8812                 }
8813                 if(tag == 'textarea'){
8814                     return w-2;
8815                 }
8816             }
8817         }
8818         return w;
8819     }
8820     
8821 });
8822
8823  
8824 /*
8825  * - LGPL
8826  *
8827  * Input
8828  * 
8829  */
8830
8831 /**
8832  * @class Roo.bootstrap.TextArea
8833  * @extends Roo.bootstrap.Input
8834  * Bootstrap TextArea class
8835  * @cfg {Number} cols Specifies the visible width of a text area
8836  * @cfg {Number} rows Specifies the visible number of lines in a text area
8837  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8838  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8839  * @cfg {string} html text
8840  * 
8841  * @constructor
8842  * Create a new TextArea
8843  * @param {Object} config The config object
8844  */
8845
8846 Roo.bootstrap.TextArea = function(config){
8847     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8848    
8849 };
8850
8851 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8852      
8853     cols : false,
8854     rows : 5,
8855     readOnly : false,
8856     warp : 'soft',
8857     resize : false,
8858     value: false,
8859     html: false,
8860     
8861     getAutoCreate : function(){
8862         
8863         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8864         
8865         var id = Roo.id();
8866         
8867         var cfg = {};
8868         
8869         var input =  {
8870             tag: 'textarea',
8871             id : id,
8872             warp : this.warp,
8873             rows : this.rows,
8874             value : this.value || '',
8875             html: this.html || '',
8876             cls : 'form-control',
8877             placeholder : this.placeholder || '' 
8878             
8879         };
8880         
8881         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8882             input.maxLength = this.maxLength;
8883         }
8884         
8885         if(this.resize){
8886             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8887         }
8888         
8889         if(this.cols){
8890             input.cols = this.cols;
8891         }
8892         
8893         if (this.readOnly) {
8894             input.readonly = true;
8895         }
8896         
8897         if (this.name) {
8898             input.name = this.name;
8899         }
8900         
8901         if (this.size) {
8902             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8903         }
8904         
8905         var settings=this;
8906         ['xs','sm','md','lg'].map(function(size){
8907             if (settings[size]) {
8908                 cfg.cls += ' col-' + size + '-' + settings[size];
8909             }
8910         });
8911         
8912         var inputblock = input;
8913         
8914         if(this.hasFeedback && !this.allowBlank){
8915             
8916             var feedback = {
8917                 tag: 'span',
8918                 cls: 'glyphicon form-control-feedback'
8919             };
8920
8921             inputblock = {
8922                 cls : 'has-feedback',
8923                 cn :  [
8924                     input,
8925                     feedback
8926                 ] 
8927             };  
8928         }
8929         
8930         
8931         if (this.before || this.after) {
8932             
8933             inputblock = {
8934                 cls : 'input-group',
8935                 cn :  [] 
8936             };
8937             if (this.before) {
8938                 inputblock.cn.push({
8939                     tag :'span',
8940                     cls : 'input-group-addon',
8941                     html : this.before
8942                 });
8943             }
8944             
8945             inputblock.cn.push(input);
8946             
8947             if(this.hasFeedback && !this.allowBlank){
8948                 inputblock.cls += ' has-feedback';
8949                 inputblock.cn.push(feedback);
8950             }
8951             
8952             if (this.after) {
8953                 inputblock.cn.push({
8954                     tag :'span',
8955                     cls : 'input-group-addon',
8956                     html : this.after
8957                 });
8958             }
8959             
8960         }
8961         
8962         if (align ==='left' && this.fieldLabel.length) {
8963 //                Roo.log("left and has label");
8964                 cfg.cn = [
8965                     
8966                     {
8967                         tag: 'label',
8968                         'for' :  id,
8969                         cls : 'control-label col-sm-' + this.labelWidth,
8970                         html : this.fieldLabel
8971                         
8972                     },
8973                     {
8974                         cls : "col-sm-" + (12 - this.labelWidth), 
8975                         cn: [
8976                             inputblock
8977                         ]
8978                     }
8979                     
8980                 ];
8981         } else if ( this.fieldLabel.length) {
8982 //                Roo.log(" label");
8983                  cfg.cn = [
8984                    
8985                     {
8986                         tag: 'label',
8987                         //cls : 'input-group-addon',
8988                         html : this.fieldLabel
8989                         
8990                     },
8991                     
8992                     inputblock
8993                     
8994                 ];
8995
8996         } else {
8997             
8998 //                   Roo.log(" no label && no align");
8999                 cfg.cn = [
9000                     
9001                         inputblock
9002                     
9003                 ];
9004                 
9005                 
9006         }
9007         
9008         if (this.disabled) {
9009             input.disabled=true;
9010         }
9011         
9012         return cfg;
9013         
9014     },
9015     /**
9016      * return the real textarea element.
9017      */
9018     inputEl: function ()
9019     {
9020         return this.el.select('textarea.form-control',true).first();
9021     },
9022     
9023     /**
9024      * Clear any invalid styles/messages for this field
9025      */
9026     clearInvalid : function()
9027     {
9028         
9029         if(!this.el || this.preventMark){ // not rendered
9030             return;
9031         }
9032         
9033         var label = this.el.select('label', true).first();
9034         var icon = this.el.select('i.fa-star', true).first();
9035         
9036         if(label && icon){
9037             icon.remove();
9038         }
9039         
9040         this.el.removeClass(this.invalidClass);
9041         
9042         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9043             
9044             var feedback = this.el.select('.form-control-feedback', true).first();
9045             
9046             if(feedback){
9047                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9048             }
9049             
9050         }
9051         
9052         this.fireEvent('valid', this);
9053     },
9054     
9055      /**
9056      * Mark this field as valid
9057      */
9058     markValid : function()
9059     {
9060         if(!this.el  || this.preventMark){ // not rendered
9061             return;
9062         }
9063         
9064         this.el.removeClass([this.invalidClass, this.validClass]);
9065         
9066         var feedback = this.el.select('.form-control-feedback', true).first();
9067             
9068         if(feedback){
9069             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9070         }
9071
9072         if(this.disabled || this.allowBlank){
9073             return;
9074         }
9075         
9076         var label = this.el.select('label', true).first();
9077         var icon = this.el.select('i.fa-star', true).first();
9078         
9079         if(label && icon){
9080             icon.remove();
9081         }
9082         
9083         this.el.addClass(this.validClass);
9084         
9085         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9086             
9087             var feedback = this.el.select('.form-control-feedback', true).first();
9088             
9089             if(feedback){
9090                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9091                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9092             }
9093             
9094         }
9095         
9096         this.fireEvent('valid', this);
9097     },
9098     
9099      /**
9100      * Mark this field as invalid
9101      * @param {String} msg The validation message
9102      */
9103     markInvalid : function(msg)
9104     {
9105         if(!this.el  || this.preventMark){ // not rendered
9106             return;
9107         }
9108         
9109         this.el.removeClass([this.invalidClass, this.validClass]);
9110         
9111         var feedback = this.el.select('.form-control-feedback', true).first();
9112             
9113         if(feedback){
9114             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9115         }
9116
9117         if(this.disabled || this.allowBlank){
9118             return;
9119         }
9120         
9121         var label = this.el.select('label', true).first();
9122         var icon = this.el.select('i.fa-star', true).first();
9123         
9124         if(!this.getValue().length && label && !icon){
9125             this.el.createChild({
9126                 tag : 'i',
9127                 cls : 'text-danger fa fa-lg fa-star',
9128                 tooltip : 'This field is required',
9129                 style : 'margin-right:5px;'
9130             }, label, true);
9131         }
9132
9133         this.el.addClass(this.invalidClass);
9134         
9135         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9136             
9137             var feedback = this.el.select('.form-control-feedback', true).first();
9138             
9139             if(feedback){
9140                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9141                 
9142                 if(this.getValue().length || this.forceFeedback){
9143                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9144                 }
9145                 
9146             }
9147             
9148         }
9149         
9150         this.fireEvent('invalid', this, msg);
9151     }
9152 });
9153
9154  
9155 /*
9156  * - LGPL
9157  *
9158  * trigger field - base class for combo..
9159  * 
9160  */
9161  
9162 /**
9163  * @class Roo.bootstrap.TriggerField
9164  * @extends Roo.bootstrap.Input
9165  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9166  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9167  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9168  * for which you can provide a custom implementation.  For example:
9169  * <pre><code>
9170 var trigger = new Roo.bootstrap.TriggerField();
9171 trigger.onTriggerClick = myTriggerFn;
9172 trigger.applyTo('my-field');
9173 </code></pre>
9174  *
9175  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9176  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9177  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9178  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9179  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9180
9181  * @constructor
9182  * Create a new TriggerField.
9183  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9184  * to the base TextField)
9185  */
9186 Roo.bootstrap.TriggerField = function(config){
9187     this.mimicing = false;
9188     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9189 };
9190
9191 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9192     /**
9193      * @cfg {String} triggerClass A CSS class to apply to the trigger
9194      */
9195      /**
9196      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9197      */
9198     hideTrigger:false,
9199
9200     /**
9201      * @cfg {Boolean} removable (true|false) special filter default false
9202      */
9203     removable : false,
9204     
9205     /** @cfg {Boolean} grow @hide */
9206     /** @cfg {Number} growMin @hide */
9207     /** @cfg {Number} growMax @hide */
9208
9209     /**
9210      * @hide 
9211      * @method
9212      */
9213     autoSize: Roo.emptyFn,
9214     // private
9215     monitorTab : true,
9216     // private
9217     deferHeight : true,
9218
9219     
9220     actionMode : 'wrap',
9221     
9222     caret : false,
9223     
9224     
9225     getAutoCreate : function(){
9226        
9227         var align = this.labelAlign || this.parentLabelAlign();
9228         
9229         var id = Roo.id();
9230         
9231         var cfg = {
9232             cls: 'form-group' //input-group
9233         };
9234         
9235         
9236         var input =  {
9237             tag: 'input',
9238             id : id,
9239             type : this.inputType,
9240             cls : 'form-control',
9241             autocomplete: 'new-password',
9242             placeholder : this.placeholder || '' 
9243             
9244         };
9245         if (this.name) {
9246             input.name = this.name;
9247         }
9248         if (this.size) {
9249             input.cls += ' input-' + this.size;
9250         }
9251         
9252         if (this.disabled) {
9253             input.disabled=true;
9254         }
9255         
9256         var inputblock = input;
9257         
9258         if(this.hasFeedback && !this.allowBlank){
9259             
9260             var feedback = {
9261                 tag: 'span',
9262                 cls: 'glyphicon form-control-feedback'
9263             };
9264             
9265             if(this.removable && !this.editable && !this.tickable){
9266                 inputblock = {
9267                     cls : 'has-feedback',
9268                     cn :  [
9269                         inputblock,
9270                         {
9271                             tag: 'button',
9272                             html : 'x',
9273                             cls : 'roo-combo-removable-btn close'
9274                         },
9275                         feedback
9276                     ] 
9277                 };
9278             } else {
9279                 inputblock = {
9280                     cls : 'has-feedback',
9281                     cn :  [
9282                         inputblock,
9283                         feedback
9284                     ] 
9285                 };
9286             }
9287
9288         } else {
9289             if(this.removable && !this.editable && !this.tickable){
9290                 inputblock = {
9291                     cls : 'roo-removable',
9292                     cn :  [
9293                         inputblock,
9294                         {
9295                             tag: 'button',
9296                             html : 'x',
9297                             cls : 'roo-combo-removable-btn close'
9298                         }
9299                     ] 
9300                 };
9301             }
9302         }
9303         
9304         if (this.before || this.after) {
9305             
9306             inputblock = {
9307                 cls : 'input-group',
9308                 cn :  [] 
9309             };
9310             if (this.before) {
9311                 inputblock.cn.push({
9312                     tag :'span',
9313                     cls : 'input-group-addon',
9314                     html : this.before
9315                 });
9316             }
9317             
9318             inputblock.cn.push(input);
9319             
9320             if(this.hasFeedback && !this.allowBlank){
9321                 inputblock.cls += ' has-feedback';
9322                 inputblock.cn.push(feedback);
9323             }
9324             
9325             if (this.after) {
9326                 inputblock.cn.push({
9327                     tag :'span',
9328                     cls : 'input-group-addon',
9329                     html : this.after
9330                 });
9331             }
9332             
9333         };
9334         
9335         var box = {
9336             tag: 'div',
9337             cn: [
9338                 {
9339                     tag: 'input',
9340                     type : 'hidden',
9341                     cls: 'form-hidden-field'
9342                 },
9343                 inputblock
9344             ]
9345             
9346         };
9347         
9348         if(this.multiple){
9349             box = {
9350                 tag: 'div',
9351                 cn: [
9352                     {
9353                         tag: 'input',
9354                         type : 'hidden',
9355                         cls: 'form-hidden-field'
9356                     },
9357                     {
9358                         tag: 'ul',
9359                         cls: 'roo-select2-choices',
9360                         cn:[
9361                             {
9362                                 tag: 'li',
9363                                 cls: 'roo-select2-search-field',
9364                                 cn: [
9365
9366                                     inputblock
9367                                 ]
9368                             }
9369                         ]
9370                     }
9371                 ]
9372             }
9373         };
9374         
9375         var combobox = {
9376             cls: 'roo-select2-container input-group',
9377             cn: [
9378                 box
9379 //                {
9380 //                    tag: 'ul',
9381 //                    cls: 'typeahead typeahead-long dropdown-menu',
9382 //                    style: 'display:none'
9383 //                }
9384             ]
9385         };
9386         
9387         if(!this.multiple && this.showToggleBtn){
9388             
9389             var caret = {
9390                         tag: 'span',
9391                         cls: 'caret'
9392              };
9393             if (this.caret != false) {
9394                 caret = {
9395                      tag: 'i',
9396                      cls: 'fa fa-' + this.caret
9397                 };
9398                 
9399             }
9400             
9401             combobox.cn.push({
9402                 tag :'span',
9403                 cls : 'input-group-addon btn dropdown-toggle',
9404                 cn : [
9405                     caret,
9406                     {
9407                         tag: 'span',
9408                         cls: 'combobox-clear',
9409                         cn  : [
9410                             {
9411                                 tag : 'i',
9412                                 cls: 'icon-remove'
9413                             }
9414                         ]
9415                     }
9416                 ]
9417
9418             })
9419         }
9420         
9421         if(this.multiple){
9422             combobox.cls += ' roo-select2-container-multi';
9423         }
9424         
9425         if (align ==='left' && this.fieldLabel.length) {
9426             
9427 //                Roo.log("left and has label");
9428                 cfg.cn = [
9429                     
9430                     {
9431                         tag: 'label',
9432                         'for' :  id,
9433                         cls : 'control-label col-sm-' + this.labelWidth,
9434                         html : this.fieldLabel
9435                         
9436                     },
9437                     {
9438                         cls : "col-sm-" + (12 - this.labelWidth), 
9439                         cn: [
9440                             combobox
9441                         ]
9442                     }
9443                     
9444                 ];
9445         } else if ( this.fieldLabel.length) {
9446 //                Roo.log(" label");
9447                  cfg.cn = [
9448                    
9449                     {
9450                         tag: 'label',
9451                         //cls : 'input-group-addon',
9452                         html : this.fieldLabel
9453                         
9454                     },
9455                     
9456                     combobox
9457                     
9458                 ];
9459
9460         } else {
9461             
9462 //                Roo.log(" no label && no align");
9463                 cfg = combobox
9464                      
9465                 
9466         }
9467          
9468         var settings=this;
9469         ['xs','sm','md','lg'].map(function(size){
9470             if (settings[size]) {
9471                 cfg.cls += ' col-' + size + '-' + settings[size];
9472             }
9473         });
9474         
9475         return cfg;
9476         
9477     },
9478     
9479     
9480     
9481     // private
9482     onResize : function(w, h){
9483 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9484 //        if(typeof w == 'number'){
9485 //            var x = w - this.trigger.getWidth();
9486 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9487 //            this.trigger.setStyle('left', x+'px');
9488 //        }
9489     },
9490
9491     // private
9492     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9493
9494     // private
9495     getResizeEl : function(){
9496         return this.inputEl();
9497     },
9498
9499     // private
9500     getPositionEl : function(){
9501         return this.inputEl();
9502     },
9503
9504     // private
9505     alignErrorIcon : function(){
9506         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9507     },
9508
9509     // private
9510     initEvents : function(){
9511         
9512         this.createList();
9513         
9514         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9515         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9516         if(!this.multiple && this.showToggleBtn){
9517             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9518             if(this.hideTrigger){
9519                 this.trigger.setDisplayed(false);
9520             }
9521             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9522         }
9523         
9524         if(this.multiple){
9525             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9526         }
9527         
9528         if(this.removable && !this.editable && !this.tickable){
9529             var close = this.closeTriggerEl();
9530             
9531             if(close){
9532                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9533                 close.on('click', this.removeBtnClick, this, close);
9534             }
9535         }
9536         
9537         //this.trigger.addClassOnOver('x-form-trigger-over');
9538         //this.trigger.addClassOnClick('x-form-trigger-click');
9539         
9540         //if(!this.width){
9541         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9542         //}
9543     },
9544     
9545     closeTriggerEl : function()
9546     {
9547         var close = this.el.select('.roo-combo-removable-btn', true).first();
9548         return close ? close : false;
9549     },
9550     
9551     removeBtnClick : function(e, h, el)
9552     {
9553         e.preventDefault();
9554         
9555         if(this.fireEvent("remove", this) !== false){
9556             this.reset();
9557             this.fireEvent("afterremove", this)
9558         }
9559     },
9560     
9561     createList : function()
9562     {
9563         this.list = Roo.get(document.body).createChild({
9564             tag: 'ul',
9565             cls: 'typeahead typeahead-long dropdown-menu',
9566             style: 'display:none'
9567         });
9568         
9569         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9570         
9571     },
9572
9573     // private
9574     initTrigger : function(){
9575        
9576     },
9577
9578     // private
9579     onDestroy : function(){
9580         if(this.trigger){
9581             this.trigger.removeAllListeners();
9582           //  this.trigger.remove();
9583         }
9584         //if(this.wrap){
9585         //    this.wrap.remove();
9586         //}
9587         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9588     },
9589
9590     // private
9591     onFocus : function(){
9592         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9593         /*
9594         if(!this.mimicing){
9595             this.wrap.addClass('x-trigger-wrap-focus');
9596             this.mimicing = true;
9597             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9598             if(this.monitorTab){
9599                 this.el.on("keydown", this.checkTab, this);
9600             }
9601         }
9602         */
9603     },
9604
9605     // private
9606     checkTab : function(e){
9607         if(e.getKey() == e.TAB){
9608             this.triggerBlur();
9609         }
9610     },
9611
9612     // private
9613     onBlur : function(){
9614         // do nothing
9615     },
9616
9617     // private
9618     mimicBlur : function(e, t){
9619         /*
9620         if(!this.wrap.contains(t) && this.validateBlur()){
9621             this.triggerBlur();
9622         }
9623         */
9624     },
9625
9626     // private
9627     triggerBlur : function(){
9628         this.mimicing = false;
9629         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9630         if(this.monitorTab){
9631             this.el.un("keydown", this.checkTab, this);
9632         }
9633         //this.wrap.removeClass('x-trigger-wrap-focus');
9634         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9635     },
9636
9637     // private
9638     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9639     validateBlur : function(e, t){
9640         return true;
9641     },
9642
9643     // private
9644     onDisable : function(){
9645         this.inputEl().dom.disabled = true;
9646         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9647         //if(this.wrap){
9648         //    this.wrap.addClass('x-item-disabled');
9649         //}
9650     },
9651
9652     // private
9653     onEnable : function(){
9654         this.inputEl().dom.disabled = false;
9655         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9656         //if(this.wrap){
9657         //    this.el.removeClass('x-item-disabled');
9658         //}
9659     },
9660
9661     // private
9662     onShow : function(){
9663         var ae = this.getActionEl();
9664         
9665         if(ae){
9666             ae.dom.style.display = '';
9667             ae.dom.style.visibility = 'visible';
9668         }
9669     },
9670
9671     // private
9672     
9673     onHide : function(){
9674         var ae = this.getActionEl();
9675         ae.dom.style.display = 'none';
9676     },
9677
9678     /**
9679      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9680      * by an implementing function.
9681      * @method
9682      * @param {EventObject} e
9683      */
9684     onTriggerClick : Roo.emptyFn
9685 });
9686  /*
9687  * Based on:
9688  * Ext JS Library 1.1.1
9689  * Copyright(c) 2006-2007, Ext JS, LLC.
9690  *
9691  * Originally Released Under LGPL - original licence link has changed is not relivant.
9692  *
9693  * Fork - LGPL
9694  * <script type="text/javascript">
9695  */
9696
9697
9698 /**
9699  * @class Roo.data.SortTypes
9700  * @singleton
9701  * Defines the default sorting (casting?) comparison functions used when sorting data.
9702  */
9703 Roo.data.SortTypes = {
9704     /**
9705      * Default sort that does nothing
9706      * @param {Mixed} s The value being converted
9707      * @return {Mixed} The comparison value
9708      */
9709     none : function(s){
9710         return s;
9711     },
9712     
9713     /**
9714      * The regular expression used to strip tags
9715      * @type {RegExp}
9716      * @property
9717      */
9718     stripTagsRE : /<\/?[^>]+>/gi,
9719     
9720     /**
9721      * Strips all HTML tags to sort on text only
9722      * @param {Mixed} s The value being converted
9723      * @return {String} The comparison value
9724      */
9725     asText : function(s){
9726         return String(s).replace(this.stripTagsRE, "");
9727     },
9728     
9729     /**
9730      * Strips all HTML tags to sort on text only - Case insensitive
9731      * @param {Mixed} s The value being converted
9732      * @return {String} The comparison value
9733      */
9734     asUCText : function(s){
9735         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9736     },
9737     
9738     /**
9739      * Case insensitive string
9740      * @param {Mixed} s The value being converted
9741      * @return {String} The comparison value
9742      */
9743     asUCString : function(s) {
9744         return String(s).toUpperCase();
9745     },
9746     
9747     /**
9748      * Date sorting
9749      * @param {Mixed} s The value being converted
9750      * @return {Number} The comparison value
9751      */
9752     asDate : function(s) {
9753         if(!s){
9754             return 0;
9755         }
9756         if(s instanceof Date){
9757             return s.getTime();
9758         }
9759         return Date.parse(String(s));
9760     },
9761     
9762     /**
9763      * Float sorting
9764      * @param {Mixed} s The value being converted
9765      * @return {Float} The comparison value
9766      */
9767     asFloat : function(s) {
9768         var val = parseFloat(String(s).replace(/,/g, ""));
9769         if(isNaN(val)) {
9770             val = 0;
9771         }
9772         return val;
9773     },
9774     
9775     /**
9776      * Integer sorting
9777      * @param {Mixed} s The value being converted
9778      * @return {Number} The comparison value
9779      */
9780     asInt : function(s) {
9781         var val = parseInt(String(s).replace(/,/g, ""));
9782         if(isNaN(val)) {
9783             val = 0;
9784         }
9785         return val;
9786     }
9787 };/*
9788  * Based on:
9789  * Ext JS Library 1.1.1
9790  * Copyright(c) 2006-2007, Ext JS, LLC.
9791  *
9792  * Originally Released Under LGPL - original licence link has changed is not relivant.
9793  *
9794  * Fork - LGPL
9795  * <script type="text/javascript">
9796  */
9797
9798 /**
9799 * @class Roo.data.Record
9800  * Instances of this class encapsulate both record <em>definition</em> information, and record
9801  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9802  * to access Records cached in an {@link Roo.data.Store} object.<br>
9803  * <p>
9804  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9805  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9806  * objects.<br>
9807  * <p>
9808  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9809  * @constructor
9810  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9811  * {@link #create}. The parameters are the same.
9812  * @param {Array} data An associative Array of data values keyed by the field name.
9813  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9814  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9815  * not specified an integer id is generated.
9816  */
9817 Roo.data.Record = function(data, id){
9818     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9819     this.data = data;
9820 };
9821
9822 /**
9823  * Generate a constructor for a specific record layout.
9824  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9825  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9826  * Each field definition object may contain the following properties: <ul>
9827  * <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,
9828  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9829  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9830  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9831  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9832  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9833  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9834  * this may be omitted.</p></li>
9835  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9836  * <ul><li>auto (Default, implies no conversion)</li>
9837  * <li>string</li>
9838  * <li>int</li>
9839  * <li>float</li>
9840  * <li>boolean</li>
9841  * <li>date</li></ul></p></li>
9842  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9843  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9844  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9845  * by the Reader into an object that will be stored in the Record. It is passed the
9846  * following parameters:<ul>
9847  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9848  * </ul></p></li>
9849  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9850  * </ul>
9851  * <br>usage:<br><pre><code>
9852 var TopicRecord = Roo.data.Record.create(
9853     {name: 'title', mapping: 'topic_title'},
9854     {name: 'author', mapping: 'username'},
9855     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9856     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9857     {name: 'lastPoster', mapping: 'user2'},
9858     {name: 'excerpt', mapping: 'post_text'}
9859 );
9860
9861 var myNewRecord = new TopicRecord({
9862     title: 'Do my job please',
9863     author: 'noobie',
9864     totalPosts: 1,
9865     lastPost: new Date(),
9866     lastPoster: 'Animal',
9867     excerpt: 'No way dude!'
9868 });
9869 myStore.add(myNewRecord);
9870 </code></pre>
9871  * @method create
9872  * @static
9873  */
9874 Roo.data.Record.create = function(o){
9875     var f = function(){
9876         f.superclass.constructor.apply(this, arguments);
9877     };
9878     Roo.extend(f, Roo.data.Record);
9879     var p = f.prototype;
9880     p.fields = new Roo.util.MixedCollection(false, function(field){
9881         return field.name;
9882     });
9883     for(var i = 0, len = o.length; i < len; i++){
9884         p.fields.add(new Roo.data.Field(o[i]));
9885     }
9886     f.getField = function(name){
9887         return p.fields.get(name);  
9888     };
9889     return f;
9890 };
9891
9892 Roo.data.Record.AUTO_ID = 1000;
9893 Roo.data.Record.EDIT = 'edit';
9894 Roo.data.Record.REJECT = 'reject';
9895 Roo.data.Record.COMMIT = 'commit';
9896
9897 Roo.data.Record.prototype = {
9898     /**
9899      * Readonly flag - true if this record has been modified.
9900      * @type Boolean
9901      */
9902     dirty : false,
9903     editing : false,
9904     error: null,
9905     modified: null,
9906
9907     // private
9908     join : function(store){
9909         this.store = store;
9910     },
9911
9912     /**
9913      * Set the named field to the specified value.
9914      * @param {String} name The name of the field to set.
9915      * @param {Object} value The value to set the field to.
9916      */
9917     set : function(name, value){
9918         if(this.data[name] == value){
9919             return;
9920         }
9921         this.dirty = true;
9922         if(!this.modified){
9923             this.modified = {};
9924         }
9925         if(typeof this.modified[name] == 'undefined'){
9926             this.modified[name] = this.data[name];
9927         }
9928         this.data[name] = value;
9929         if(!this.editing && this.store){
9930             this.store.afterEdit(this);
9931         }       
9932     },
9933
9934     /**
9935      * Get the value of the named field.
9936      * @param {String} name The name of the field to get the value of.
9937      * @return {Object} The value of the field.
9938      */
9939     get : function(name){
9940         return this.data[name]; 
9941     },
9942
9943     // private
9944     beginEdit : function(){
9945         this.editing = true;
9946         this.modified = {}; 
9947     },
9948
9949     // private
9950     cancelEdit : function(){
9951         this.editing = false;
9952         delete this.modified;
9953     },
9954
9955     // private
9956     endEdit : function(){
9957         this.editing = false;
9958         if(this.dirty && this.store){
9959             this.store.afterEdit(this);
9960         }
9961     },
9962
9963     /**
9964      * Usually called by the {@link Roo.data.Store} which owns the Record.
9965      * Rejects all changes made to the Record since either creation, or the last commit operation.
9966      * Modified fields are reverted to their original values.
9967      * <p>
9968      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9969      * of reject operations.
9970      */
9971     reject : function(){
9972         var m = this.modified;
9973         for(var n in m){
9974             if(typeof m[n] != "function"){
9975                 this.data[n] = m[n];
9976             }
9977         }
9978         this.dirty = false;
9979         delete this.modified;
9980         this.editing = false;
9981         if(this.store){
9982             this.store.afterReject(this);
9983         }
9984     },
9985
9986     /**
9987      * Usually called by the {@link Roo.data.Store} which owns the Record.
9988      * Commits all changes made to the Record since either creation, or the last commit operation.
9989      * <p>
9990      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9991      * of commit operations.
9992      */
9993     commit : function(){
9994         this.dirty = false;
9995         delete this.modified;
9996         this.editing = false;
9997         if(this.store){
9998             this.store.afterCommit(this);
9999         }
10000     },
10001
10002     // private
10003     hasError : function(){
10004         return this.error != null;
10005     },
10006
10007     // private
10008     clearError : function(){
10009         this.error = null;
10010     },
10011
10012     /**
10013      * Creates a copy of this record.
10014      * @param {String} id (optional) A new record id if you don't want to use this record's id
10015      * @return {Record}
10016      */
10017     copy : function(newId) {
10018         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10019     }
10020 };/*
10021  * Based on:
10022  * Ext JS Library 1.1.1
10023  * Copyright(c) 2006-2007, Ext JS, LLC.
10024  *
10025  * Originally Released Under LGPL - original licence link has changed is not relivant.
10026  *
10027  * Fork - LGPL
10028  * <script type="text/javascript">
10029  */
10030
10031
10032
10033 /**
10034  * @class Roo.data.Store
10035  * @extends Roo.util.Observable
10036  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10037  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10038  * <p>
10039  * 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
10040  * has no knowledge of the format of the data returned by the Proxy.<br>
10041  * <p>
10042  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10043  * instances from the data object. These records are cached and made available through accessor functions.
10044  * @constructor
10045  * Creates a new Store.
10046  * @param {Object} config A config object containing the objects needed for the Store to access data,
10047  * and read the data into Records.
10048  */
10049 Roo.data.Store = function(config){
10050     this.data = new Roo.util.MixedCollection(false);
10051     this.data.getKey = function(o){
10052         return o.id;
10053     };
10054     this.baseParams = {};
10055     // private
10056     this.paramNames = {
10057         "start" : "start",
10058         "limit" : "limit",
10059         "sort" : "sort",
10060         "dir" : "dir",
10061         "multisort" : "_multisort"
10062     };
10063
10064     if(config && config.data){
10065         this.inlineData = config.data;
10066         delete config.data;
10067     }
10068
10069     Roo.apply(this, config);
10070     
10071     if(this.reader){ // reader passed
10072         this.reader = Roo.factory(this.reader, Roo.data);
10073         this.reader.xmodule = this.xmodule || false;
10074         if(!this.recordType){
10075             this.recordType = this.reader.recordType;
10076         }
10077         if(this.reader.onMetaChange){
10078             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10079         }
10080     }
10081
10082     if(this.recordType){
10083         this.fields = this.recordType.prototype.fields;
10084     }
10085     this.modified = [];
10086
10087     this.addEvents({
10088         /**
10089          * @event datachanged
10090          * Fires when the data cache has changed, and a widget which is using this Store
10091          * as a Record cache should refresh its view.
10092          * @param {Store} this
10093          */
10094         datachanged : true,
10095         /**
10096          * @event metachange
10097          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10098          * @param {Store} this
10099          * @param {Object} meta The JSON metadata
10100          */
10101         metachange : true,
10102         /**
10103          * @event add
10104          * Fires when Records have been added to the Store
10105          * @param {Store} this
10106          * @param {Roo.data.Record[]} records The array of Records added
10107          * @param {Number} index The index at which the record(s) were added
10108          */
10109         add : true,
10110         /**
10111          * @event remove
10112          * Fires when a Record has been removed from the Store
10113          * @param {Store} this
10114          * @param {Roo.data.Record} record The Record that was removed
10115          * @param {Number} index The index at which the record was removed
10116          */
10117         remove : true,
10118         /**
10119          * @event update
10120          * Fires when a Record has been updated
10121          * @param {Store} this
10122          * @param {Roo.data.Record} record The Record that was updated
10123          * @param {String} operation The update operation being performed.  Value may be one of:
10124          * <pre><code>
10125  Roo.data.Record.EDIT
10126  Roo.data.Record.REJECT
10127  Roo.data.Record.COMMIT
10128          * </code></pre>
10129          */
10130         update : true,
10131         /**
10132          * @event clear
10133          * Fires when the data cache has been cleared.
10134          * @param {Store} this
10135          */
10136         clear : true,
10137         /**
10138          * @event beforeload
10139          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10140          * the load action will be canceled.
10141          * @param {Store} this
10142          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10143          */
10144         beforeload : true,
10145         /**
10146          * @event beforeloadadd
10147          * Fires after a new set of Records has been loaded.
10148          * @param {Store} this
10149          * @param {Roo.data.Record[]} records The Records that were loaded
10150          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10151          */
10152         beforeloadadd : true,
10153         /**
10154          * @event load
10155          * Fires after a new set of Records has been loaded, before they are added to the store.
10156          * @param {Store} this
10157          * @param {Roo.data.Record[]} records The Records that were loaded
10158          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10159          * @params {Object} return from reader
10160          */
10161         load : true,
10162         /**
10163          * @event loadexception
10164          * Fires if an exception occurs in the Proxy during loading.
10165          * Called with the signature of the Proxy's "loadexception" event.
10166          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10167          * 
10168          * @param {Proxy} 
10169          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10170          * @param {Object} load options 
10171          * @param {Object} jsonData from your request (normally this contains the Exception)
10172          */
10173         loadexception : true
10174     });
10175     
10176     if(this.proxy){
10177         this.proxy = Roo.factory(this.proxy, Roo.data);
10178         this.proxy.xmodule = this.xmodule || false;
10179         this.relayEvents(this.proxy,  ["loadexception"]);
10180     }
10181     this.sortToggle = {};
10182     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10183
10184     Roo.data.Store.superclass.constructor.call(this);
10185
10186     if(this.inlineData){
10187         this.loadData(this.inlineData);
10188         delete this.inlineData;
10189     }
10190 };
10191
10192 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10193      /**
10194     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10195     * without a remote query - used by combo/forms at present.
10196     */
10197     
10198     /**
10199     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10200     */
10201     /**
10202     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10203     */
10204     /**
10205     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10206     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10207     */
10208     /**
10209     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10210     * on any HTTP request
10211     */
10212     /**
10213     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10214     */
10215     /**
10216     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10217     */
10218     multiSort: false,
10219     /**
10220     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10221     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10222     */
10223     remoteSort : false,
10224
10225     /**
10226     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10227      * loaded or when a record is removed. (defaults to false).
10228     */
10229     pruneModifiedRecords : false,
10230
10231     // private
10232     lastOptions : null,
10233
10234     /**
10235      * Add Records to the Store and fires the add event.
10236      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10237      */
10238     add : function(records){
10239         records = [].concat(records);
10240         for(var i = 0, len = records.length; i < len; i++){
10241             records[i].join(this);
10242         }
10243         var index = this.data.length;
10244         this.data.addAll(records);
10245         this.fireEvent("add", this, records, index);
10246     },
10247
10248     /**
10249      * Remove a Record from the Store and fires the remove event.
10250      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10251      */
10252     remove : function(record){
10253         var index = this.data.indexOf(record);
10254         this.data.removeAt(index);
10255         if(this.pruneModifiedRecords){
10256             this.modified.remove(record);
10257         }
10258         this.fireEvent("remove", this, record, index);
10259     },
10260
10261     /**
10262      * Remove all Records from the Store and fires the clear event.
10263      */
10264     removeAll : function(){
10265         this.data.clear();
10266         if(this.pruneModifiedRecords){
10267             this.modified = [];
10268         }
10269         this.fireEvent("clear", this);
10270     },
10271
10272     /**
10273      * Inserts Records to the Store at the given index and fires the add event.
10274      * @param {Number} index The start index at which to insert the passed Records.
10275      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10276      */
10277     insert : function(index, records){
10278         records = [].concat(records);
10279         for(var i = 0, len = records.length; i < len; i++){
10280             this.data.insert(index, records[i]);
10281             records[i].join(this);
10282         }
10283         this.fireEvent("add", this, records, index);
10284     },
10285
10286     /**
10287      * Get the index within the cache of the passed Record.
10288      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10289      * @return {Number} The index of the passed Record. Returns -1 if not found.
10290      */
10291     indexOf : function(record){
10292         return this.data.indexOf(record);
10293     },
10294
10295     /**
10296      * Get the index within the cache of the Record with the passed id.
10297      * @param {String} id The id of the Record to find.
10298      * @return {Number} The index of the Record. Returns -1 if not found.
10299      */
10300     indexOfId : function(id){
10301         return this.data.indexOfKey(id);
10302     },
10303
10304     /**
10305      * Get the Record with the specified id.
10306      * @param {String} id The id of the Record to find.
10307      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10308      */
10309     getById : function(id){
10310         return this.data.key(id);
10311     },
10312
10313     /**
10314      * Get the Record at the specified index.
10315      * @param {Number} index The index of the Record to find.
10316      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10317      */
10318     getAt : function(index){
10319         return this.data.itemAt(index);
10320     },
10321
10322     /**
10323      * Returns a range of Records between specified indices.
10324      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10325      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10326      * @return {Roo.data.Record[]} An array of Records
10327      */
10328     getRange : function(start, end){
10329         return this.data.getRange(start, end);
10330     },
10331
10332     // private
10333     storeOptions : function(o){
10334         o = Roo.apply({}, o);
10335         delete o.callback;
10336         delete o.scope;
10337         this.lastOptions = o;
10338     },
10339
10340     /**
10341      * Loads the Record cache from the configured Proxy using the configured Reader.
10342      * <p>
10343      * If using remote paging, then the first load call must specify the <em>start</em>
10344      * and <em>limit</em> properties in the options.params property to establish the initial
10345      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10346      * <p>
10347      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10348      * and this call will return before the new data has been loaded. Perform any post-processing
10349      * in a callback function, or in a "load" event handler.</strong>
10350      * <p>
10351      * @param {Object} options An object containing properties which control loading options:<ul>
10352      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10353      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10354      * passed the following arguments:<ul>
10355      * <li>r : Roo.data.Record[]</li>
10356      * <li>options: Options object from the load call</li>
10357      * <li>success: Boolean success indicator</li></ul></li>
10358      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10359      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10360      * </ul>
10361      */
10362     load : function(options){
10363         options = options || {};
10364         if(this.fireEvent("beforeload", this, options) !== false){
10365             this.storeOptions(options);
10366             var p = Roo.apply(options.params || {}, this.baseParams);
10367             // if meta was not loaded from remote source.. try requesting it.
10368             if (!this.reader.metaFromRemote) {
10369                 p._requestMeta = 1;
10370             }
10371             if(this.sortInfo && this.remoteSort){
10372                 var pn = this.paramNames;
10373                 p[pn["sort"]] = this.sortInfo.field;
10374                 p[pn["dir"]] = this.sortInfo.direction;
10375             }
10376             if (this.multiSort) {
10377                 var pn = this.paramNames;
10378                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10379             }
10380             
10381             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10382         }
10383     },
10384
10385     /**
10386      * Reloads the Record cache from the configured Proxy using the configured Reader and
10387      * the options from the last load operation performed.
10388      * @param {Object} options (optional) An object containing properties which may override the options
10389      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10390      * the most recently used options are reused).
10391      */
10392     reload : function(options){
10393         this.load(Roo.applyIf(options||{}, this.lastOptions));
10394     },
10395
10396     // private
10397     // Called as a callback by the Reader during a load operation.
10398     loadRecords : function(o, options, success){
10399         if(!o || success === false){
10400             if(success !== false){
10401                 this.fireEvent("load", this, [], options, o);
10402             }
10403             if(options.callback){
10404                 options.callback.call(options.scope || this, [], options, false);
10405             }
10406             return;
10407         }
10408         // if data returned failure - throw an exception.
10409         if (o.success === false) {
10410             // show a message if no listener is registered.
10411             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10412                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10413             }
10414             // loadmask wil be hooked into this..
10415             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10416             return;
10417         }
10418         var r = o.records, t = o.totalRecords || r.length;
10419         
10420         this.fireEvent("beforeloadadd", this, r, options, o);
10421         
10422         if(!options || options.add !== true){
10423             if(this.pruneModifiedRecords){
10424                 this.modified = [];
10425             }
10426             for(var i = 0, len = r.length; i < len; i++){
10427                 r[i].join(this);
10428             }
10429             if(this.snapshot){
10430                 this.data = this.snapshot;
10431                 delete this.snapshot;
10432             }
10433             this.data.clear();
10434             this.data.addAll(r);
10435             this.totalLength = t;
10436             this.applySort();
10437             this.fireEvent("datachanged", this);
10438         }else{
10439             this.totalLength = Math.max(t, this.data.length+r.length);
10440             this.add(r);
10441         }
10442         this.fireEvent("load", this, r, options, o);
10443         if(options.callback){
10444             options.callback.call(options.scope || this, r, options, true);
10445         }
10446     },
10447
10448
10449     /**
10450      * Loads data from a passed data block. A Reader which understands the format of the data
10451      * must have been configured in the constructor.
10452      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10453      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10454      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10455      */
10456     loadData : function(o, append){
10457         var r = this.reader.readRecords(o);
10458         this.loadRecords(r, {add: append}, true);
10459     },
10460
10461     /**
10462      * Gets the number of cached records.
10463      * <p>
10464      * <em>If using paging, this may not be the total size of the dataset. If the data object
10465      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10466      * the data set size</em>
10467      */
10468     getCount : function(){
10469         return this.data.length || 0;
10470     },
10471
10472     /**
10473      * Gets the total number of records in the dataset as returned by the server.
10474      * <p>
10475      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10476      * the dataset size</em>
10477      */
10478     getTotalCount : function(){
10479         return this.totalLength || 0;
10480     },
10481
10482     /**
10483      * Returns the sort state of the Store as an object with two properties:
10484      * <pre><code>
10485  field {String} The name of the field by which the Records are sorted
10486  direction {String} The sort order, "ASC" or "DESC"
10487      * </code></pre>
10488      */
10489     getSortState : function(){
10490         return this.sortInfo;
10491     },
10492
10493     // private
10494     applySort : function(){
10495         if(this.sortInfo && !this.remoteSort){
10496             var s = this.sortInfo, f = s.field;
10497             var st = this.fields.get(f).sortType;
10498             var fn = function(r1, r2){
10499                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10500                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10501             };
10502             this.data.sort(s.direction, fn);
10503             if(this.snapshot && this.snapshot != this.data){
10504                 this.snapshot.sort(s.direction, fn);
10505             }
10506         }
10507     },
10508
10509     /**
10510      * Sets the default sort column and order to be used by the next load operation.
10511      * @param {String} fieldName The name of the field to sort by.
10512      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10513      */
10514     setDefaultSort : function(field, dir){
10515         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10516     },
10517
10518     /**
10519      * Sort the Records.
10520      * If remote sorting is used, the sort is performed on the server, and the cache is
10521      * reloaded. If local sorting is used, the cache is sorted internally.
10522      * @param {String} fieldName The name of the field to sort by.
10523      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10524      */
10525     sort : function(fieldName, dir){
10526         var f = this.fields.get(fieldName);
10527         if(!dir){
10528             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10529             
10530             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10531                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10532             }else{
10533                 dir = f.sortDir;
10534             }
10535         }
10536         this.sortToggle[f.name] = dir;
10537         this.sortInfo = {field: f.name, direction: dir};
10538         if(!this.remoteSort){
10539             this.applySort();
10540             this.fireEvent("datachanged", this);
10541         }else{
10542             this.load(this.lastOptions);
10543         }
10544     },
10545
10546     /**
10547      * Calls the specified function for each of the Records in the cache.
10548      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10549      * Returning <em>false</em> aborts and exits the iteration.
10550      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10551      */
10552     each : function(fn, scope){
10553         this.data.each(fn, scope);
10554     },
10555
10556     /**
10557      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10558      * (e.g., during paging).
10559      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10560      */
10561     getModifiedRecords : function(){
10562         return this.modified;
10563     },
10564
10565     // private
10566     createFilterFn : function(property, value, anyMatch){
10567         if(!value.exec){ // not a regex
10568             value = String(value);
10569             if(value.length == 0){
10570                 return false;
10571             }
10572             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10573         }
10574         return function(r){
10575             return value.test(r.data[property]);
10576         };
10577     },
10578
10579     /**
10580      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10581      * @param {String} property A field on your records
10582      * @param {Number} start The record index to start at (defaults to 0)
10583      * @param {Number} end The last record index to include (defaults to length - 1)
10584      * @return {Number} The sum
10585      */
10586     sum : function(property, start, end){
10587         var rs = this.data.items, v = 0;
10588         start = start || 0;
10589         end = (end || end === 0) ? end : rs.length-1;
10590
10591         for(var i = start; i <= end; i++){
10592             v += (rs[i].data[property] || 0);
10593         }
10594         return v;
10595     },
10596
10597     /**
10598      * Filter the records by a specified property.
10599      * @param {String} field A field on your records
10600      * @param {String/RegExp} value Either a string that the field
10601      * should start with or a RegExp to test against the field
10602      * @param {Boolean} anyMatch True to match any part not just the beginning
10603      */
10604     filter : function(property, value, anyMatch){
10605         var fn = this.createFilterFn(property, value, anyMatch);
10606         return fn ? this.filterBy(fn) : this.clearFilter();
10607     },
10608
10609     /**
10610      * Filter by a function. The specified function will be called with each
10611      * record in this data source. If the function returns true the record is included,
10612      * otherwise it is filtered.
10613      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10614      * @param {Object} scope (optional) The scope of the function (defaults to this)
10615      */
10616     filterBy : function(fn, scope){
10617         this.snapshot = this.snapshot || this.data;
10618         this.data = this.queryBy(fn, scope||this);
10619         this.fireEvent("datachanged", this);
10620     },
10621
10622     /**
10623      * Query the records by a specified property.
10624      * @param {String} field A field on your records
10625      * @param {String/RegExp} value Either a string that the field
10626      * should start with or a RegExp to test against the field
10627      * @param {Boolean} anyMatch True to match any part not just the beginning
10628      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10629      */
10630     query : function(property, value, anyMatch){
10631         var fn = this.createFilterFn(property, value, anyMatch);
10632         return fn ? this.queryBy(fn) : this.data.clone();
10633     },
10634
10635     /**
10636      * Query by a function. The specified function will be called with each
10637      * record in this data source. If the function returns true the record is included
10638      * in the results.
10639      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10640      * @param {Object} scope (optional) The scope of the function (defaults to this)
10641       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10642      **/
10643     queryBy : function(fn, scope){
10644         var data = this.snapshot || this.data;
10645         return data.filterBy(fn, scope||this);
10646     },
10647
10648     /**
10649      * Collects unique values for a particular dataIndex from this store.
10650      * @param {String} dataIndex The property to collect
10651      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10652      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10653      * @return {Array} An array of the unique values
10654      **/
10655     collect : function(dataIndex, allowNull, bypassFilter){
10656         var d = (bypassFilter === true && this.snapshot) ?
10657                 this.snapshot.items : this.data.items;
10658         var v, sv, r = [], l = {};
10659         for(var i = 0, len = d.length; i < len; i++){
10660             v = d[i].data[dataIndex];
10661             sv = String(v);
10662             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10663                 l[sv] = true;
10664                 r[r.length] = v;
10665             }
10666         }
10667         return r;
10668     },
10669
10670     /**
10671      * Revert to a view of the Record cache with no filtering applied.
10672      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10673      */
10674     clearFilter : function(suppressEvent){
10675         if(this.snapshot && this.snapshot != this.data){
10676             this.data = this.snapshot;
10677             delete this.snapshot;
10678             if(suppressEvent !== true){
10679                 this.fireEvent("datachanged", this);
10680             }
10681         }
10682     },
10683
10684     // private
10685     afterEdit : function(record){
10686         if(this.modified.indexOf(record) == -1){
10687             this.modified.push(record);
10688         }
10689         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10690     },
10691     
10692     // private
10693     afterReject : function(record){
10694         this.modified.remove(record);
10695         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10696     },
10697
10698     // private
10699     afterCommit : function(record){
10700         this.modified.remove(record);
10701         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10702     },
10703
10704     /**
10705      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10706      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10707      */
10708     commitChanges : function(){
10709         var m = this.modified.slice(0);
10710         this.modified = [];
10711         for(var i = 0, len = m.length; i < len; i++){
10712             m[i].commit();
10713         }
10714     },
10715
10716     /**
10717      * Cancel outstanding changes on all changed records.
10718      */
10719     rejectChanges : function(){
10720         var m = this.modified.slice(0);
10721         this.modified = [];
10722         for(var i = 0, len = m.length; i < len; i++){
10723             m[i].reject();
10724         }
10725     },
10726
10727     onMetaChange : function(meta, rtype, o){
10728         this.recordType = rtype;
10729         this.fields = rtype.prototype.fields;
10730         delete this.snapshot;
10731         this.sortInfo = meta.sortInfo || this.sortInfo;
10732         this.modified = [];
10733         this.fireEvent('metachange', this, this.reader.meta);
10734     },
10735     
10736     moveIndex : function(data, type)
10737     {
10738         var index = this.indexOf(data);
10739         
10740         var newIndex = index + type;
10741         
10742         this.remove(data);
10743         
10744         this.insert(newIndex, data);
10745         
10746     }
10747 });/*
10748  * Based on:
10749  * Ext JS Library 1.1.1
10750  * Copyright(c) 2006-2007, Ext JS, LLC.
10751  *
10752  * Originally Released Under LGPL - original licence link has changed is not relivant.
10753  *
10754  * Fork - LGPL
10755  * <script type="text/javascript">
10756  */
10757
10758 /**
10759  * @class Roo.data.SimpleStore
10760  * @extends Roo.data.Store
10761  * Small helper class to make creating Stores from Array data easier.
10762  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10763  * @cfg {Array} fields An array of field definition objects, or field name strings.
10764  * @cfg {Array} data The multi-dimensional array of data
10765  * @constructor
10766  * @param {Object} config
10767  */
10768 Roo.data.SimpleStore = function(config){
10769     Roo.data.SimpleStore.superclass.constructor.call(this, {
10770         isLocal : true,
10771         reader: new Roo.data.ArrayReader({
10772                 id: config.id
10773             },
10774             Roo.data.Record.create(config.fields)
10775         ),
10776         proxy : new Roo.data.MemoryProxy(config.data)
10777     });
10778     this.load();
10779 };
10780 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10781  * Based on:
10782  * Ext JS Library 1.1.1
10783  * Copyright(c) 2006-2007, Ext JS, LLC.
10784  *
10785  * Originally Released Under LGPL - original licence link has changed is not relivant.
10786  *
10787  * Fork - LGPL
10788  * <script type="text/javascript">
10789  */
10790
10791 /**
10792 /**
10793  * @extends Roo.data.Store
10794  * @class Roo.data.JsonStore
10795  * Small helper class to make creating Stores for JSON data easier. <br/>
10796 <pre><code>
10797 var store = new Roo.data.JsonStore({
10798     url: 'get-images.php',
10799     root: 'images',
10800     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10801 });
10802 </code></pre>
10803  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10804  * JsonReader and HttpProxy (unless inline data is provided).</b>
10805  * @cfg {Array} fields An array of field definition objects, or field name strings.
10806  * @constructor
10807  * @param {Object} config
10808  */
10809 Roo.data.JsonStore = function(c){
10810     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10811         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10812         reader: new Roo.data.JsonReader(c, c.fields)
10813     }));
10814 };
10815 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10816  * Based on:
10817  * Ext JS Library 1.1.1
10818  * Copyright(c) 2006-2007, Ext JS, LLC.
10819  *
10820  * Originally Released Under LGPL - original licence link has changed is not relivant.
10821  *
10822  * Fork - LGPL
10823  * <script type="text/javascript">
10824  */
10825
10826  
10827 Roo.data.Field = function(config){
10828     if(typeof config == "string"){
10829         config = {name: config};
10830     }
10831     Roo.apply(this, config);
10832     
10833     if(!this.type){
10834         this.type = "auto";
10835     }
10836     
10837     var st = Roo.data.SortTypes;
10838     // named sortTypes are supported, here we look them up
10839     if(typeof this.sortType == "string"){
10840         this.sortType = st[this.sortType];
10841     }
10842     
10843     // set default sortType for strings and dates
10844     if(!this.sortType){
10845         switch(this.type){
10846             case "string":
10847                 this.sortType = st.asUCString;
10848                 break;
10849             case "date":
10850                 this.sortType = st.asDate;
10851                 break;
10852             default:
10853                 this.sortType = st.none;
10854         }
10855     }
10856
10857     // define once
10858     var stripRe = /[\$,%]/g;
10859
10860     // prebuilt conversion function for this field, instead of
10861     // switching every time we're reading a value
10862     if(!this.convert){
10863         var cv, dateFormat = this.dateFormat;
10864         switch(this.type){
10865             case "":
10866             case "auto":
10867             case undefined:
10868                 cv = function(v){ return v; };
10869                 break;
10870             case "string":
10871                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10872                 break;
10873             case "int":
10874                 cv = function(v){
10875                     return v !== undefined && v !== null && v !== '' ?
10876                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10877                     };
10878                 break;
10879             case "float":
10880                 cv = function(v){
10881                     return v !== undefined && v !== null && v !== '' ?
10882                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10883                     };
10884                 break;
10885             case "bool":
10886             case "boolean":
10887                 cv = function(v){ return v === true || v === "true" || v == 1; };
10888                 break;
10889             case "date":
10890                 cv = function(v){
10891                     if(!v){
10892                         return '';
10893                     }
10894                     if(v instanceof Date){
10895                         return v;
10896                     }
10897                     if(dateFormat){
10898                         if(dateFormat == "timestamp"){
10899                             return new Date(v*1000);
10900                         }
10901                         return Date.parseDate(v, dateFormat);
10902                     }
10903                     var parsed = Date.parse(v);
10904                     return parsed ? new Date(parsed) : null;
10905                 };
10906              break;
10907             
10908         }
10909         this.convert = cv;
10910     }
10911 };
10912
10913 Roo.data.Field.prototype = {
10914     dateFormat: null,
10915     defaultValue: "",
10916     mapping: null,
10917     sortType : null,
10918     sortDir : "ASC"
10919 };/*
10920  * Based on:
10921  * Ext JS Library 1.1.1
10922  * Copyright(c) 2006-2007, Ext JS, LLC.
10923  *
10924  * Originally Released Under LGPL - original licence link has changed is not relivant.
10925  *
10926  * Fork - LGPL
10927  * <script type="text/javascript">
10928  */
10929  
10930 // Base class for reading structured data from a data source.  This class is intended to be
10931 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10932
10933 /**
10934  * @class Roo.data.DataReader
10935  * Base class for reading structured data from a data source.  This class is intended to be
10936  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10937  */
10938
10939 Roo.data.DataReader = function(meta, recordType){
10940     
10941     this.meta = meta;
10942     
10943     this.recordType = recordType instanceof Array ? 
10944         Roo.data.Record.create(recordType) : recordType;
10945 };
10946
10947 Roo.data.DataReader.prototype = {
10948      /**
10949      * Create an empty record
10950      * @param {Object} data (optional) - overlay some values
10951      * @return {Roo.data.Record} record created.
10952      */
10953     newRow :  function(d) {
10954         var da =  {};
10955         this.recordType.prototype.fields.each(function(c) {
10956             switch( c.type) {
10957                 case 'int' : da[c.name] = 0; break;
10958                 case 'date' : da[c.name] = new Date(); break;
10959                 case 'float' : da[c.name] = 0.0; break;
10960                 case 'boolean' : da[c.name] = false; break;
10961                 default : da[c.name] = ""; break;
10962             }
10963             
10964         });
10965         return new this.recordType(Roo.apply(da, d));
10966     }
10967     
10968 };/*
10969  * Based on:
10970  * Ext JS Library 1.1.1
10971  * Copyright(c) 2006-2007, Ext JS, LLC.
10972  *
10973  * Originally Released Under LGPL - original licence link has changed is not relivant.
10974  *
10975  * Fork - LGPL
10976  * <script type="text/javascript">
10977  */
10978
10979 /**
10980  * @class Roo.data.DataProxy
10981  * @extends Roo.data.Observable
10982  * This class is an abstract base class for implementations which provide retrieval of
10983  * unformatted data objects.<br>
10984  * <p>
10985  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10986  * (of the appropriate type which knows how to parse the data object) to provide a block of
10987  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10988  * <p>
10989  * Custom implementations must implement the load method as described in
10990  * {@link Roo.data.HttpProxy#load}.
10991  */
10992 Roo.data.DataProxy = function(){
10993     this.addEvents({
10994         /**
10995          * @event beforeload
10996          * Fires before a network request is made to retrieve a data object.
10997          * @param {Object} This DataProxy object.
10998          * @param {Object} params The params parameter to the load function.
10999          */
11000         beforeload : true,
11001         /**
11002          * @event load
11003          * Fires before the load method's callback is called.
11004          * @param {Object} This DataProxy object.
11005          * @param {Object} o The data object.
11006          * @param {Object} arg The callback argument object passed to the load function.
11007          */
11008         load : true,
11009         /**
11010          * @event loadexception
11011          * Fires if an Exception occurs during data retrieval.
11012          * @param {Object} This DataProxy object.
11013          * @param {Object} o The data object.
11014          * @param {Object} arg The callback argument object passed to the load function.
11015          * @param {Object} e The Exception.
11016          */
11017         loadexception : true
11018     });
11019     Roo.data.DataProxy.superclass.constructor.call(this);
11020 };
11021
11022 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11023
11024     /**
11025      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11026      */
11027 /*
11028  * Based on:
11029  * Ext JS Library 1.1.1
11030  * Copyright(c) 2006-2007, Ext JS, LLC.
11031  *
11032  * Originally Released Under LGPL - original licence link has changed is not relivant.
11033  *
11034  * Fork - LGPL
11035  * <script type="text/javascript">
11036  */
11037 /**
11038  * @class Roo.data.MemoryProxy
11039  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11040  * to the Reader when its load method is called.
11041  * @constructor
11042  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11043  */
11044 Roo.data.MemoryProxy = function(data){
11045     if (data.data) {
11046         data = data.data;
11047     }
11048     Roo.data.MemoryProxy.superclass.constructor.call(this);
11049     this.data = data;
11050 };
11051
11052 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11053     
11054     /**
11055      * Load data from the requested source (in this case an in-memory
11056      * data object passed to the constructor), read the data object into
11057      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11058      * process that block using the passed callback.
11059      * @param {Object} params This parameter is not used by the MemoryProxy class.
11060      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11061      * object into a block of Roo.data.Records.
11062      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11063      * The function must be passed <ul>
11064      * <li>The Record block object</li>
11065      * <li>The "arg" argument from the load function</li>
11066      * <li>A boolean success indicator</li>
11067      * </ul>
11068      * @param {Object} scope The scope in which to call the callback
11069      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11070      */
11071     load : function(params, reader, callback, scope, arg){
11072         params = params || {};
11073         var result;
11074         try {
11075             result = reader.readRecords(this.data);
11076         }catch(e){
11077             this.fireEvent("loadexception", this, arg, null, e);
11078             callback.call(scope, null, arg, false);
11079             return;
11080         }
11081         callback.call(scope, result, arg, true);
11082     },
11083     
11084     // private
11085     update : function(params, records){
11086         
11087     }
11088 });/*
11089  * Based on:
11090  * Ext JS Library 1.1.1
11091  * Copyright(c) 2006-2007, Ext JS, LLC.
11092  *
11093  * Originally Released Under LGPL - original licence link has changed is not relivant.
11094  *
11095  * Fork - LGPL
11096  * <script type="text/javascript">
11097  */
11098 /**
11099  * @class Roo.data.HttpProxy
11100  * @extends Roo.data.DataProxy
11101  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11102  * configured to reference a certain URL.<br><br>
11103  * <p>
11104  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11105  * from which the running page was served.<br><br>
11106  * <p>
11107  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11108  * <p>
11109  * Be aware that to enable the browser to parse an XML document, the server must set
11110  * the Content-Type header in the HTTP response to "text/xml".
11111  * @constructor
11112  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11113  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11114  * will be used to make the request.
11115  */
11116 Roo.data.HttpProxy = function(conn){
11117     Roo.data.HttpProxy.superclass.constructor.call(this);
11118     // is conn a conn config or a real conn?
11119     this.conn = conn;
11120     this.useAjax = !conn || !conn.events;
11121   
11122 };
11123
11124 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11125     // thse are take from connection...
11126     
11127     /**
11128      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11129      */
11130     /**
11131      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11132      * extra parameters to each request made by this object. (defaults to undefined)
11133      */
11134     /**
11135      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11136      *  to each request made by this object. (defaults to undefined)
11137      */
11138     /**
11139      * @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)
11140      */
11141     /**
11142      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11143      */
11144      /**
11145      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11146      * @type Boolean
11147      */
11148   
11149
11150     /**
11151      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11152      * @type Boolean
11153      */
11154     /**
11155      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11156      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11157      * a finer-grained basis than the DataProxy events.
11158      */
11159     getConnection : function(){
11160         return this.useAjax ? Roo.Ajax : this.conn;
11161     },
11162
11163     /**
11164      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11165      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11166      * process that block using the passed callback.
11167      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11168      * for the request to the remote server.
11169      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11170      * object into a block of Roo.data.Records.
11171      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11172      * The function must be passed <ul>
11173      * <li>The Record block object</li>
11174      * <li>The "arg" argument from the load function</li>
11175      * <li>A boolean success indicator</li>
11176      * </ul>
11177      * @param {Object} scope The scope in which to call the callback
11178      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11179      */
11180     load : function(params, reader, callback, scope, arg){
11181         if(this.fireEvent("beforeload", this, params) !== false){
11182             var  o = {
11183                 params : params || {},
11184                 request: {
11185                     callback : callback,
11186                     scope : scope,
11187                     arg : arg
11188                 },
11189                 reader: reader,
11190                 callback : this.loadResponse,
11191                 scope: this
11192             };
11193             if(this.useAjax){
11194                 Roo.applyIf(o, this.conn);
11195                 if(this.activeRequest){
11196                     Roo.Ajax.abort(this.activeRequest);
11197                 }
11198                 this.activeRequest = Roo.Ajax.request(o);
11199             }else{
11200                 this.conn.request(o);
11201             }
11202         }else{
11203             callback.call(scope||this, null, arg, false);
11204         }
11205     },
11206
11207     // private
11208     loadResponse : function(o, success, response){
11209         delete this.activeRequest;
11210         if(!success){
11211             this.fireEvent("loadexception", this, o, response);
11212             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11213             return;
11214         }
11215         var result;
11216         try {
11217             result = o.reader.read(response);
11218         }catch(e){
11219             this.fireEvent("loadexception", this, o, response, e);
11220             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11221             return;
11222         }
11223         
11224         this.fireEvent("load", this, o, o.request.arg);
11225         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11226     },
11227
11228     // private
11229     update : function(dataSet){
11230
11231     },
11232
11233     // private
11234     updateResponse : function(dataSet){
11235
11236     }
11237 });/*
11238  * Based on:
11239  * Ext JS Library 1.1.1
11240  * Copyright(c) 2006-2007, Ext JS, LLC.
11241  *
11242  * Originally Released Under LGPL - original licence link has changed is not relivant.
11243  *
11244  * Fork - LGPL
11245  * <script type="text/javascript">
11246  */
11247
11248 /**
11249  * @class Roo.data.ScriptTagProxy
11250  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11251  * other than the originating domain of the running page.<br><br>
11252  * <p>
11253  * <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
11254  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11255  * <p>
11256  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11257  * source code that is used as the source inside a &lt;script> tag.<br><br>
11258  * <p>
11259  * In order for the browser to process the returned data, the server must wrap the data object
11260  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11261  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11262  * depending on whether the callback name was passed:
11263  * <p>
11264  * <pre><code>
11265 boolean scriptTag = false;
11266 String cb = request.getParameter("callback");
11267 if (cb != null) {
11268     scriptTag = true;
11269     response.setContentType("text/javascript");
11270 } else {
11271     response.setContentType("application/x-json");
11272 }
11273 Writer out = response.getWriter();
11274 if (scriptTag) {
11275     out.write(cb + "(");
11276 }
11277 out.print(dataBlock.toJsonString());
11278 if (scriptTag) {
11279     out.write(");");
11280 }
11281 </pre></code>
11282  *
11283  * @constructor
11284  * @param {Object} config A configuration object.
11285  */
11286 Roo.data.ScriptTagProxy = function(config){
11287     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11288     Roo.apply(this, config);
11289     this.head = document.getElementsByTagName("head")[0];
11290 };
11291
11292 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11293
11294 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11295     /**
11296      * @cfg {String} url The URL from which to request the data object.
11297      */
11298     /**
11299      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11300      */
11301     timeout : 30000,
11302     /**
11303      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11304      * the server the name of the callback function set up by the load call to process the returned data object.
11305      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11306      * javascript output which calls this named function passing the data object as its only parameter.
11307      */
11308     callbackParam : "callback",
11309     /**
11310      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11311      * name to the request.
11312      */
11313     nocache : true,
11314
11315     /**
11316      * Load data from the configured URL, read the data object into
11317      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11318      * process that block using the passed callback.
11319      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11320      * for the request to the remote server.
11321      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11322      * object into a block of Roo.data.Records.
11323      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11324      * The function must be passed <ul>
11325      * <li>The Record block object</li>
11326      * <li>The "arg" argument from the load function</li>
11327      * <li>A boolean success indicator</li>
11328      * </ul>
11329      * @param {Object} scope The scope in which to call the callback
11330      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11331      */
11332     load : function(params, reader, callback, scope, arg){
11333         if(this.fireEvent("beforeload", this, params) !== false){
11334
11335             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11336
11337             var url = this.url;
11338             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11339             if(this.nocache){
11340                 url += "&_dc=" + (new Date().getTime());
11341             }
11342             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11343             var trans = {
11344                 id : transId,
11345                 cb : "stcCallback"+transId,
11346                 scriptId : "stcScript"+transId,
11347                 params : params,
11348                 arg : arg,
11349                 url : url,
11350                 callback : callback,
11351                 scope : scope,
11352                 reader : reader
11353             };
11354             var conn = this;
11355
11356             window[trans.cb] = function(o){
11357                 conn.handleResponse(o, trans);
11358             };
11359
11360             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11361
11362             if(this.autoAbort !== false){
11363                 this.abort();
11364             }
11365
11366             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11367
11368             var script = document.createElement("script");
11369             script.setAttribute("src", url);
11370             script.setAttribute("type", "text/javascript");
11371             script.setAttribute("id", trans.scriptId);
11372             this.head.appendChild(script);
11373
11374             this.trans = trans;
11375         }else{
11376             callback.call(scope||this, null, arg, false);
11377         }
11378     },
11379
11380     // private
11381     isLoading : function(){
11382         return this.trans ? true : false;
11383     },
11384
11385     /**
11386      * Abort the current server request.
11387      */
11388     abort : function(){
11389         if(this.isLoading()){
11390             this.destroyTrans(this.trans);
11391         }
11392     },
11393
11394     // private
11395     destroyTrans : function(trans, isLoaded){
11396         this.head.removeChild(document.getElementById(trans.scriptId));
11397         clearTimeout(trans.timeoutId);
11398         if(isLoaded){
11399             window[trans.cb] = undefined;
11400             try{
11401                 delete window[trans.cb];
11402             }catch(e){}
11403         }else{
11404             // if hasn't been loaded, wait for load to remove it to prevent script error
11405             window[trans.cb] = function(){
11406                 window[trans.cb] = undefined;
11407                 try{
11408                     delete window[trans.cb];
11409                 }catch(e){}
11410             };
11411         }
11412     },
11413
11414     // private
11415     handleResponse : function(o, trans){
11416         this.trans = false;
11417         this.destroyTrans(trans, true);
11418         var result;
11419         try {
11420             result = trans.reader.readRecords(o);
11421         }catch(e){
11422             this.fireEvent("loadexception", this, o, trans.arg, e);
11423             trans.callback.call(trans.scope||window, null, trans.arg, false);
11424             return;
11425         }
11426         this.fireEvent("load", this, o, trans.arg);
11427         trans.callback.call(trans.scope||window, result, trans.arg, true);
11428     },
11429
11430     // private
11431     handleFailure : function(trans){
11432         this.trans = false;
11433         this.destroyTrans(trans, false);
11434         this.fireEvent("loadexception", this, null, trans.arg);
11435         trans.callback.call(trans.scope||window, null, trans.arg, false);
11436     }
11437 });/*
11438  * Based on:
11439  * Ext JS Library 1.1.1
11440  * Copyright(c) 2006-2007, Ext JS, LLC.
11441  *
11442  * Originally Released Under LGPL - original licence link has changed is not relivant.
11443  *
11444  * Fork - LGPL
11445  * <script type="text/javascript">
11446  */
11447
11448 /**
11449  * @class Roo.data.JsonReader
11450  * @extends Roo.data.DataReader
11451  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11452  * based on mappings in a provided Roo.data.Record constructor.
11453  * 
11454  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11455  * in the reply previously. 
11456  * 
11457  * <p>
11458  * Example code:
11459  * <pre><code>
11460 var RecordDef = Roo.data.Record.create([
11461     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11462     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11463 ]);
11464 var myReader = new Roo.data.JsonReader({
11465     totalProperty: "results",    // The property which contains the total dataset size (optional)
11466     root: "rows",                // The property which contains an Array of row objects
11467     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11468 }, RecordDef);
11469 </code></pre>
11470  * <p>
11471  * This would consume a JSON file like this:
11472  * <pre><code>
11473 { 'results': 2, 'rows': [
11474     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11475     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11476 }
11477 </code></pre>
11478  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11479  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11480  * paged from the remote server.
11481  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11482  * @cfg {String} root name of the property which contains the Array of row objects.
11483  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11484  * @cfg {Array} fields Array of field definition objects
11485  * @constructor
11486  * Create a new JsonReader
11487  * @param {Object} meta Metadata configuration options
11488  * @param {Object} recordType Either an Array of field definition objects,
11489  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11490  */
11491 Roo.data.JsonReader = function(meta, recordType){
11492     
11493     meta = meta || {};
11494     // set some defaults:
11495     Roo.applyIf(meta, {
11496         totalProperty: 'total',
11497         successProperty : 'success',
11498         root : 'data',
11499         id : 'id'
11500     });
11501     
11502     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11503 };
11504 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11505     
11506     /**
11507      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11508      * Used by Store query builder to append _requestMeta to params.
11509      * 
11510      */
11511     metaFromRemote : false,
11512     /**
11513      * This method is only used by a DataProxy which has retrieved data from a remote server.
11514      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11515      * @return {Object} data A data block which is used by an Roo.data.Store object as
11516      * a cache of Roo.data.Records.
11517      */
11518     read : function(response){
11519         var json = response.responseText;
11520        
11521         var o = /* eval:var:o */ eval("("+json+")");
11522         if(!o) {
11523             throw {message: "JsonReader.read: Json object not found"};
11524         }
11525         
11526         if(o.metaData){
11527             
11528             delete this.ef;
11529             this.metaFromRemote = true;
11530             this.meta = o.metaData;
11531             this.recordType = Roo.data.Record.create(o.metaData.fields);
11532             this.onMetaChange(this.meta, this.recordType, o);
11533         }
11534         return this.readRecords(o);
11535     },
11536
11537     // private function a store will implement
11538     onMetaChange : function(meta, recordType, o){
11539
11540     },
11541
11542     /**
11543          * @ignore
11544          */
11545     simpleAccess: function(obj, subsc) {
11546         return obj[subsc];
11547     },
11548
11549         /**
11550          * @ignore
11551          */
11552     getJsonAccessor: function(){
11553         var re = /[\[\.]/;
11554         return function(expr) {
11555             try {
11556                 return(re.test(expr))
11557                     ? new Function("obj", "return obj." + expr)
11558                     : function(obj){
11559                         return obj[expr];
11560                     };
11561             } catch(e){}
11562             return Roo.emptyFn;
11563         };
11564     }(),
11565
11566     /**
11567      * Create a data block containing Roo.data.Records from an XML document.
11568      * @param {Object} o An object which contains an Array of row objects in the property specified
11569      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11570      * which contains the total size of the dataset.
11571      * @return {Object} data A data block which is used by an Roo.data.Store object as
11572      * a cache of Roo.data.Records.
11573      */
11574     readRecords : function(o){
11575         /**
11576          * After any data loads, the raw JSON data is available for further custom processing.
11577          * @type Object
11578          */
11579         this.o = o;
11580         var s = this.meta, Record = this.recordType,
11581             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11582
11583 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11584         if (!this.ef) {
11585             if(s.totalProperty) {
11586                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11587                 }
11588                 if(s.successProperty) {
11589                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11590                 }
11591                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11592                 if (s.id) {
11593                         var g = this.getJsonAccessor(s.id);
11594                         this.getId = function(rec) {
11595                                 var r = g(rec);  
11596                                 return (r === undefined || r === "") ? null : r;
11597                         };
11598                 } else {
11599                         this.getId = function(){return null;};
11600                 }
11601             this.ef = [];
11602             for(var jj = 0; jj < fl; jj++){
11603                 f = fi[jj];
11604                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11605                 this.ef[jj] = this.getJsonAccessor(map);
11606             }
11607         }
11608
11609         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11610         if(s.totalProperty){
11611             var vt = parseInt(this.getTotal(o), 10);
11612             if(!isNaN(vt)){
11613                 totalRecords = vt;
11614             }
11615         }
11616         if(s.successProperty){
11617             var vs = this.getSuccess(o);
11618             if(vs === false || vs === 'false'){
11619                 success = false;
11620             }
11621         }
11622         var records = [];
11623         for(var i = 0; i < c; i++){
11624                 var n = root[i];
11625             var values = {};
11626             var id = this.getId(n);
11627             for(var j = 0; j < fl; j++){
11628                 f = fi[j];
11629             var v = this.ef[j](n);
11630             if (!f.convert) {
11631                 Roo.log('missing convert for ' + f.name);
11632                 Roo.log(f);
11633                 continue;
11634             }
11635             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11636             }
11637             var record = new Record(values, id);
11638             record.json = n;
11639             records[i] = record;
11640         }
11641         return {
11642             raw : o,
11643             success : success,
11644             records : records,
11645             totalRecords : totalRecords
11646         };
11647     }
11648 });/*
11649  * Based on:
11650  * Ext JS Library 1.1.1
11651  * Copyright(c) 2006-2007, Ext JS, LLC.
11652  *
11653  * Originally Released Under LGPL - original licence link has changed is not relivant.
11654  *
11655  * Fork - LGPL
11656  * <script type="text/javascript">
11657  */
11658
11659 /**
11660  * @class Roo.data.ArrayReader
11661  * @extends Roo.data.DataReader
11662  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11663  * Each element of that Array represents a row of data fields. The
11664  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11665  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11666  * <p>
11667  * Example code:.
11668  * <pre><code>
11669 var RecordDef = Roo.data.Record.create([
11670     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11671     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11672 ]);
11673 var myReader = new Roo.data.ArrayReader({
11674     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11675 }, RecordDef);
11676 </code></pre>
11677  * <p>
11678  * This would consume an Array like this:
11679  * <pre><code>
11680 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11681   </code></pre>
11682  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11683  * @constructor
11684  * Create a new JsonReader
11685  * @param {Object} meta Metadata configuration options.
11686  * @param {Object} recordType Either an Array of field definition objects
11687  * as specified to {@link Roo.data.Record#create},
11688  * or an {@link Roo.data.Record} object
11689  * created using {@link Roo.data.Record#create}.
11690  */
11691 Roo.data.ArrayReader = function(meta, recordType){
11692     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11693 };
11694
11695 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11696     /**
11697      * Create a data block containing Roo.data.Records from an XML document.
11698      * @param {Object} o An Array of row objects which represents the dataset.
11699      * @return {Object} data A data block which is used by an Roo.data.Store object as
11700      * a cache of Roo.data.Records.
11701      */
11702     readRecords : function(o){
11703         var sid = this.meta ? this.meta.id : null;
11704         var recordType = this.recordType, fields = recordType.prototype.fields;
11705         var records = [];
11706         var root = o;
11707             for(var i = 0; i < root.length; i++){
11708                     var n = root[i];
11709                 var values = {};
11710                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11711                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11712                 var f = fields.items[j];
11713                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11714                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11715                 v = f.convert(v);
11716                 values[f.name] = v;
11717             }
11718                 var record = new recordType(values, id);
11719                 record.json = n;
11720                 records[records.length] = record;
11721             }
11722             return {
11723                 records : records,
11724                 totalRecords : records.length
11725             };
11726     }
11727 });/*
11728  * - LGPL
11729  * * 
11730  */
11731
11732 /**
11733  * @class Roo.bootstrap.ComboBox
11734  * @extends Roo.bootstrap.TriggerField
11735  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11736  * @cfg {Boolean} append (true|false) default false
11737  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11738  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11739  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11740  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11741  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11742  * @cfg {Boolean} animate default true
11743  * @cfg {Boolean} emptyResultText only for touch device
11744  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11745  * @constructor
11746  * Create a new ComboBox.
11747  * @param {Object} config Configuration options
11748  */
11749 Roo.bootstrap.ComboBox = function(config){
11750     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11751     this.addEvents({
11752         /**
11753          * @event expand
11754          * Fires when the dropdown list is expanded
11755              * @param {Roo.bootstrap.ComboBox} combo This combo box
11756              */
11757         'expand' : true,
11758         /**
11759          * @event collapse
11760          * Fires when the dropdown list is collapsed
11761              * @param {Roo.bootstrap.ComboBox} combo This combo box
11762              */
11763         'collapse' : true,
11764         /**
11765          * @event beforeselect
11766          * Fires before a list item is selected. Return false to cancel the selection.
11767              * @param {Roo.bootstrap.ComboBox} combo This combo box
11768              * @param {Roo.data.Record} record The data record returned from the underlying store
11769              * @param {Number} index The index of the selected item in the dropdown list
11770              */
11771         'beforeselect' : true,
11772         /**
11773          * @event select
11774          * Fires when a list item is selected
11775              * @param {Roo.bootstrap.ComboBox} combo This combo box
11776              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11777              * @param {Number} index The index of the selected item in the dropdown list
11778              */
11779         'select' : true,
11780         /**
11781          * @event beforequery
11782          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11783          * The event object passed has these properties:
11784              * @param {Roo.bootstrap.ComboBox} combo This combo box
11785              * @param {String} query The query
11786              * @param {Boolean} forceAll true to force "all" query
11787              * @param {Boolean} cancel true to cancel the query
11788              * @param {Object} e The query event object
11789              */
11790         'beforequery': true,
11791          /**
11792          * @event add
11793          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11794              * @param {Roo.bootstrap.ComboBox} combo This combo box
11795              */
11796         'add' : true,
11797         /**
11798          * @event edit
11799          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11800              * @param {Roo.bootstrap.ComboBox} combo This combo box
11801              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11802              */
11803         'edit' : true,
11804         /**
11805          * @event remove
11806          * Fires when the remove value from the combobox array
11807              * @param {Roo.bootstrap.ComboBox} combo This combo box
11808              */
11809         'remove' : true,
11810         /**
11811          * @event afterremove
11812          * Fires when the remove value from the combobox array
11813              * @param {Roo.bootstrap.ComboBox} combo This combo box
11814              */
11815         'afterremove' : true,
11816         /**
11817          * @event specialfilter
11818          * Fires when specialfilter
11819             * @param {Roo.bootstrap.ComboBox} combo This combo box
11820             */
11821         'specialfilter' : true,
11822         /**
11823          * @event tick
11824          * Fires when tick the element
11825             * @param {Roo.bootstrap.ComboBox} combo This combo box
11826             */
11827         'tick' : true,
11828         /**
11829          * @event touchviewdisplay
11830          * Fires when touch view require special display (default is using displayField)
11831             * @param {Roo.bootstrap.ComboBox} combo This combo box
11832             * @param {Object} cfg set html .
11833             */
11834         'touchviewdisplay' : true
11835         
11836     });
11837     
11838     this.item = [];
11839     this.tickItems = [];
11840     
11841     this.selectedIndex = -1;
11842     if(this.mode == 'local'){
11843         if(config.queryDelay === undefined){
11844             this.queryDelay = 10;
11845         }
11846         if(config.minChars === undefined){
11847             this.minChars = 0;
11848         }
11849     }
11850 };
11851
11852 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11853      
11854     /**
11855      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11856      * rendering into an Roo.Editor, defaults to false)
11857      */
11858     /**
11859      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11860      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11861      */
11862     /**
11863      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11864      */
11865     /**
11866      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11867      * the dropdown list (defaults to undefined, with no header element)
11868      */
11869
11870      /**
11871      * @cfg {String/Roo.Template} tpl The template to use to render the output
11872      */
11873      
11874      /**
11875      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11876      */
11877     listWidth: undefined,
11878     /**
11879      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11880      * mode = 'remote' or 'text' if mode = 'local')
11881      */
11882     displayField: undefined,
11883     
11884     /**
11885      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11886      * mode = 'remote' or 'value' if mode = 'local'). 
11887      * Note: use of a valueField requires the user make a selection
11888      * in order for a value to be mapped.
11889      */
11890     valueField: undefined,
11891     
11892     
11893     /**
11894      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11895      * field's data value (defaults to the underlying DOM element's name)
11896      */
11897     hiddenName: undefined,
11898     /**
11899      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11900      */
11901     listClass: '',
11902     /**
11903      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11904      */
11905     selectedClass: 'active',
11906     
11907     /**
11908      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11909      */
11910     shadow:'sides',
11911     /**
11912      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11913      * anchor positions (defaults to 'tl-bl')
11914      */
11915     listAlign: 'tl-bl?',
11916     /**
11917      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11918      */
11919     maxHeight: 300,
11920     /**
11921      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11922      * query specified by the allQuery config option (defaults to 'query')
11923      */
11924     triggerAction: 'query',
11925     /**
11926      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11927      * (defaults to 4, does not apply if editable = false)
11928      */
11929     minChars : 4,
11930     /**
11931      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11932      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11933      */
11934     typeAhead: false,
11935     /**
11936      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11937      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11938      */
11939     queryDelay: 500,
11940     /**
11941      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11942      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11943      */
11944     pageSize: 0,
11945     /**
11946      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11947      * when editable = true (defaults to false)
11948      */
11949     selectOnFocus:false,
11950     /**
11951      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11952      */
11953     queryParam: 'query',
11954     /**
11955      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11956      * when mode = 'remote' (defaults to 'Loading...')
11957      */
11958     loadingText: 'Loading...',
11959     /**
11960      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11961      */
11962     resizable: false,
11963     /**
11964      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11965      */
11966     handleHeight : 8,
11967     /**
11968      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11969      * traditional select (defaults to true)
11970      */
11971     editable: true,
11972     /**
11973      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11974      */
11975     allQuery: '',
11976     /**
11977      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11978      */
11979     mode: 'remote',
11980     /**
11981      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11982      * listWidth has a higher value)
11983      */
11984     minListWidth : 70,
11985     /**
11986      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11987      * allow the user to set arbitrary text into the field (defaults to false)
11988      */
11989     forceSelection:false,
11990     /**
11991      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11992      * if typeAhead = true (defaults to 250)
11993      */
11994     typeAheadDelay : 250,
11995     /**
11996      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11997      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11998      */
11999     valueNotFoundText : undefined,
12000     /**
12001      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12002      */
12003     blockFocus : false,
12004     
12005     /**
12006      * @cfg {Boolean} disableClear Disable showing of clear button.
12007      */
12008     disableClear : false,
12009     /**
12010      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12011      */
12012     alwaysQuery : false,
12013     
12014     /**
12015      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12016      */
12017     multiple : false,
12018     
12019     /**
12020      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12021      */
12022     invalidClass : "has-warning",
12023     
12024     /**
12025      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12026      */
12027     validClass : "has-success",
12028     
12029     /**
12030      * @cfg {Boolean} specialFilter (true|false) special filter default false
12031      */
12032     specialFilter : false,
12033     
12034     /**
12035      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12036      */
12037     mobileTouchView : true,
12038     
12039     //private
12040     addicon : false,
12041     editicon: false,
12042     
12043     page: 0,
12044     hasQuery: false,
12045     append: false,
12046     loadNext: false,
12047     autoFocus : true,
12048     tickable : false,
12049     btnPosition : 'right',
12050     triggerList : true,
12051     showToggleBtn : true,
12052     animate : true,
12053     emptyResultText: 'Empty',
12054     triggerText : 'Select',
12055     
12056     // element that contains real text value.. (when hidden is used..)
12057     
12058     getAutoCreate : function()
12059     {
12060         var cfg = false;
12061         
12062         /*
12063          * Touch Devices
12064          */
12065         
12066         if(Roo.isTouch && this.mobileTouchView){
12067             cfg = this.getAutoCreateTouchView();
12068             return cfg;;
12069         }
12070         
12071         /*
12072          *  Normal ComboBox
12073          */
12074         if(!this.tickable){
12075             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12076             return cfg;
12077         }
12078         
12079         /*
12080          *  ComboBox with tickable selections
12081          */
12082              
12083         var align = this.labelAlign || this.parentLabelAlign();
12084         
12085         cfg = {
12086             cls : 'form-group roo-combobox-tickable' //input-group
12087         };
12088         
12089         var buttons = {
12090             tag : 'div',
12091             cls : 'tickable-buttons',
12092             cn : [
12093                 {
12094                     tag : 'button',
12095                     type : 'button',
12096                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12097                     html : this.triggerText
12098                 },
12099                 {
12100                     tag : 'button',
12101                     type : 'button',
12102                     name : 'ok',
12103                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12104                     html : 'Done'
12105                 },
12106                 {
12107                     tag : 'button',
12108                     type : 'button',
12109                     name : 'cancel',
12110                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12111                     html : 'Cancel'
12112                 }
12113             ]
12114         };
12115         
12116         if(this.editable){
12117             buttons.cn.unshift({
12118                 tag: 'input',
12119                 cls: 'roo-select2-search-field-input'
12120             });
12121         }
12122         
12123         var _this = this;
12124         
12125         Roo.each(buttons.cn, function(c){
12126             if (_this.size) {
12127                 c.cls += ' btn-' + _this.size;
12128             }
12129
12130             if (_this.disabled) {
12131                 c.disabled = true;
12132             }
12133         });
12134         
12135         var box = {
12136             tag: 'div',
12137             cn: [
12138                 {
12139                     tag: 'input',
12140                     type : 'hidden',
12141                     cls: 'form-hidden-field'
12142                 },
12143                 {
12144                     tag: 'ul',
12145                     cls: 'roo-select2-choices',
12146                     cn:[
12147                         {
12148                             tag: 'li',
12149                             cls: 'roo-select2-search-field',
12150                             cn: [
12151
12152                                 buttons
12153                             ]
12154                         }
12155                     ]
12156                 }
12157             ]
12158         };
12159         
12160         var combobox = {
12161             cls: 'roo-select2-container input-group roo-select2-container-multi',
12162             cn: [
12163                 box
12164 //                {
12165 //                    tag: 'ul',
12166 //                    cls: 'typeahead typeahead-long dropdown-menu',
12167 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12168 //                }
12169             ]
12170         };
12171         
12172         if(this.hasFeedback && !this.allowBlank){
12173             
12174             var feedback = {
12175                 tag: 'span',
12176                 cls: 'glyphicon form-control-feedback'
12177             };
12178
12179             combobox.cn.push(feedback);
12180         }
12181         
12182         if (align ==='left' && this.fieldLabel.length) {
12183             
12184 //                Roo.log("left and has label");
12185                 cfg.cn = [
12186                     
12187                     {
12188                         tag: 'label',
12189                         'for' :  id,
12190                         cls : 'control-label col-sm-' + this.labelWidth,
12191                         html : this.fieldLabel
12192                         
12193                     },
12194                     {
12195                         cls : "col-sm-" + (12 - this.labelWidth), 
12196                         cn: [
12197                             combobox
12198                         ]
12199                     }
12200                     
12201                 ];
12202         } else if ( this.fieldLabel.length) {
12203 //                Roo.log(" label");
12204                  cfg.cn = [
12205                    
12206                     {
12207                         tag: 'label',
12208                         //cls : 'input-group-addon',
12209                         html : this.fieldLabel
12210                         
12211                     },
12212                     
12213                     combobox
12214                     
12215                 ];
12216
12217         } else {
12218             
12219 //                Roo.log(" no label && no align");
12220                 cfg = combobox
12221                      
12222                 
12223         }
12224          
12225         var settings=this;
12226         ['xs','sm','md','lg'].map(function(size){
12227             if (settings[size]) {
12228                 cfg.cls += ' col-' + size + '-' + settings[size];
12229             }
12230         });
12231         
12232         return cfg;
12233         
12234     },
12235     
12236     _initEventsCalled : false,
12237     
12238     // private
12239     initEvents: function()
12240     {
12241         
12242         if (this._initEventsCalled) { // as we call render... prevent looping...
12243             return;
12244         }
12245         this._initEventsCalled = true;
12246         
12247         if (!this.store) {
12248             throw "can not find store for combo";
12249         }
12250         
12251         this.store = Roo.factory(this.store, Roo.data);
12252         
12253         // if we are building from html. then this element is so complex, that we can not really
12254         // use the rendered HTML.
12255         // so we have to trash and replace the previous code.
12256         if (Roo.XComponent.build_from_html) {
12257             
12258             // remove this element....
12259             var e = this.el.dom, k=0;
12260             while (e ) { e = e.previousSibling;  ++k;}
12261
12262             this.el.remove();
12263             
12264             this.el=false;
12265             this.rendered = false;
12266             
12267             this.render(this.parent().getChildContainer(true), k);
12268             
12269             
12270             
12271         }
12272         
12273         
12274         /*
12275          * Touch Devices
12276          */
12277         
12278         if(Roo.isTouch && this.mobileTouchView){
12279             this.initTouchView();
12280             return;
12281         }
12282         
12283         if(this.tickable){
12284             this.initTickableEvents();
12285             return;
12286         }
12287         
12288         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12289         
12290         if(this.hiddenName){
12291             
12292             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12293             
12294             this.hiddenField.dom.value =
12295                 this.hiddenValue !== undefined ? this.hiddenValue :
12296                 this.value !== undefined ? this.value : '';
12297
12298             // prevent input submission
12299             this.el.dom.removeAttribute('name');
12300             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12301              
12302              
12303         }
12304         //if(Roo.isGecko){
12305         //    this.el.dom.setAttribute('autocomplete', 'off');
12306         //}
12307         
12308         var cls = 'x-combo-list';
12309         
12310         //this.list = new Roo.Layer({
12311         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12312         //});
12313         
12314         var _this = this;
12315         
12316         (function(){
12317             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12318             _this.list.setWidth(lw);
12319         }).defer(100);
12320         
12321         this.list.on('mouseover', this.onViewOver, this);
12322         this.list.on('mousemove', this.onViewMove, this);
12323         
12324         this.list.on('scroll', this.onViewScroll, this);
12325         
12326         /*
12327         this.list.swallowEvent('mousewheel');
12328         this.assetHeight = 0;
12329
12330         if(this.title){
12331             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12332             this.assetHeight += this.header.getHeight();
12333         }
12334
12335         this.innerList = this.list.createChild({cls:cls+'-inner'});
12336         this.innerList.on('mouseover', this.onViewOver, this);
12337         this.innerList.on('mousemove', this.onViewMove, this);
12338         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12339         
12340         if(this.allowBlank && !this.pageSize && !this.disableClear){
12341             this.footer = this.list.createChild({cls:cls+'-ft'});
12342             this.pageTb = new Roo.Toolbar(this.footer);
12343            
12344         }
12345         if(this.pageSize){
12346             this.footer = this.list.createChild({cls:cls+'-ft'});
12347             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12348                     {pageSize: this.pageSize});
12349             
12350         }
12351         
12352         if (this.pageTb && this.allowBlank && !this.disableClear) {
12353             var _this = this;
12354             this.pageTb.add(new Roo.Toolbar.Fill(), {
12355                 cls: 'x-btn-icon x-btn-clear',
12356                 text: '&#160;',
12357                 handler: function()
12358                 {
12359                     _this.collapse();
12360                     _this.clearValue();
12361                     _this.onSelect(false, -1);
12362                 }
12363             });
12364         }
12365         if (this.footer) {
12366             this.assetHeight += this.footer.getHeight();
12367         }
12368         */
12369             
12370         if(!this.tpl){
12371             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12372         }
12373
12374         this.view = new Roo.View(this.list, this.tpl, {
12375             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12376         });
12377         //this.view.wrapEl.setDisplayed(false);
12378         this.view.on('click', this.onViewClick, this);
12379         
12380         
12381         
12382         this.store.on('beforeload', this.onBeforeLoad, this);
12383         this.store.on('load', this.onLoad, this);
12384         this.store.on('loadexception', this.onLoadException, this);
12385         /*
12386         if(this.resizable){
12387             this.resizer = new Roo.Resizable(this.list,  {
12388                pinned:true, handles:'se'
12389             });
12390             this.resizer.on('resize', function(r, w, h){
12391                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12392                 this.listWidth = w;
12393                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12394                 this.restrictHeight();
12395             }, this);
12396             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12397         }
12398         */
12399         if(!this.editable){
12400             this.editable = true;
12401             this.setEditable(false);
12402         }
12403         
12404         /*
12405         
12406         if (typeof(this.events.add.listeners) != 'undefined') {
12407             
12408             this.addicon = this.wrap.createChild(
12409                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12410        
12411             this.addicon.on('click', function(e) {
12412                 this.fireEvent('add', this);
12413             }, this);
12414         }
12415         if (typeof(this.events.edit.listeners) != 'undefined') {
12416             
12417             this.editicon = this.wrap.createChild(
12418                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12419             if (this.addicon) {
12420                 this.editicon.setStyle('margin-left', '40px');
12421             }
12422             this.editicon.on('click', function(e) {
12423                 
12424                 // we fire even  if inothing is selected..
12425                 this.fireEvent('edit', this, this.lastData );
12426                 
12427             }, this);
12428         }
12429         */
12430         
12431         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12432             "up" : function(e){
12433                 this.inKeyMode = true;
12434                 this.selectPrev();
12435             },
12436
12437             "down" : function(e){
12438                 if(!this.isExpanded()){
12439                     this.onTriggerClick();
12440                 }else{
12441                     this.inKeyMode = true;
12442                     this.selectNext();
12443                 }
12444             },
12445
12446             "enter" : function(e){
12447 //                this.onViewClick();
12448                 //return true;
12449                 this.collapse();
12450                 
12451                 if(this.fireEvent("specialkey", this, e)){
12452                     this.onViewClick(false);
12453                 }
12454                 
12455                 return true;
12456             },
12457
12458             "esc" : function(e){
12459                 this.collapse();
12460             },
12461
12462             "tab" : function(e){
12463                 this.collapse();
12464                 
12465                 if(this.fireEvent("specialkey", this, e)){
12466                     this.onViewClick(false);
12467                 }
12468                 
12469                 return true;
12470             },
12471
12472             scope : this,
12473
12474             doRelay : function(foo, bar, hname){
12475                 if(hname == 'down' || this.scope.isExpanded()){
12476                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12477                 }
12478                 return true;
12479             },
12480
12481             forceKeyDown: true
12482         });
12483         
12484         
12485         this.queryDelay = Math.max(this.queryDelay || 10,
12486                 this.mode == 'local' ? 10 : 250);
12487         
12488         
12489         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12490         
12491         if(this.typeAhead){
12492             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12493         }
12494         if(this.editable !== false){
12495             this.inputEl().on("keyup", this.onKeyUp, this);
12496         }
12497         if(this.forceSelection){
12498             this.inputEl().on('blur', this.doForce, this);
12499         }
12500         
12501         if(this.multiple){
12502             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12503             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12504         }
12505     },
12506     
12507     initTickableEvents: function()
12508     {   
12509         this.createList();
12510         
12511         if(this.hiddenName){
12512             
12513             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12514             
12515             this.hiddenField.dom.value =
12516                 this.hiddenValue !== undefined ? this.hiddenValue :
12517                 this.value !== undefined ? this.value : '';
12518
12519             // prevent input submission
12520             this.el.dom.removeAttribute('name');
12521             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12522              
12523              
12524         }
12525         
12526 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12527         
12528         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12529         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12530         if(this.triggerList){
12531             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12532         }
12533          
12534         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12535         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12536         
12537         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12538         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12539         
12540         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12541         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12542         
12543         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12544         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12545         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12546         
12547         this.okBtn.hide();
12548         this.cancelBtn.hide();
12549         
12550         var _this = this;
12551         
12552         (function(){
12553             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12554             _this.list.setWidth(lw);
12555         }).defer(100);
12556         
12557         this.list.on('mouseover', this.onViewOver, this);
12558         this.list.on('mousemove', this.onViewMove, this);
12559         
12560         this.list.on('scroll', this.onViewScroll, this);
12561         
12562         if(!this.tpl){
12563             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>';
12564         }
12565
12566         this.view = new Roo.View(this.list, this.tpl, {
12567             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12568         });
12569         
12570         //this.view.wrapEl.setDisplayed(false);
12571         this.view.on('click', this.onViewClick, this);
12572         
12573         
12574         
12575         this.store.on('beforeload', this.onBeforeLoad, this);
12576         this.store.on('load', this.onLoad, this);
12577         this.store.on('loadexception', this.onLoadException, this);
12578         
12579         if(this.editable){
12580             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12581                 "up" : function(e){
12582                     this.inKeyMode = true;
12583                     this.selectPrev();
12584                 },
12585
12586                 "down" : function(e){
12587                     this.inKeyMode = true;
12588                     this.selectNext();
12589                 },
12590
12591                 "enter" : function(e){
12592                     if(this.fireEvent("specialkey", this, e)){
12593                         this.onViewClick(false);
12594                     }
12595                     
12596                     return true;
12597                 },
12598
12599                 "esc" : function(e){
12600                     this.onTickableFooterButtonClick(e, false, false);
12601                 },
12602
12603                 "tab" : function(e){
12604                     this.fireEvent("specialkey", this, e);
12605                     
12606                     this.onTickableFooterButtonClick(e, false, false);
12607                     
12608                     return true;
12609                 },
12610
12611                 scope : this,
12612
12613                 doRelay : function(e, fn, key){
12614                     if(this.scope.isExpanded()){
12615                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12616                     }
12617                     return true;
12618                 },
12619
12620                 forceKeyDown: true
12621             });
12622         }
12623         
12624         this.queryDelay = Math.max(this.queryDelay || 10,
12625                 this.mode == 'local' ? 10 : 250);
12626         
12627         
12628         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12629         
12630         if(this.typeAhead){
12631             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12632         }
12633         
12634         if(this.editable !== false){
12635             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12636         }
12637         
12638     },
12639
12640     onDestroy : function(){
12641         if(this.view){
12642             this.view.setStore(null);
12643             this.view.el.removeAllListeners();
12644             this.view.el.remove();
12645             this.view.purgeListeners();
12646         }
12647         if(this.list){
12648             this.list.dom.innerHTML  = '';
12649         }
12650         
12651         if(this.store){
12652             this.store.un('beforeload', this.onBeforeLoad, this);
12653             this.store.un('load', this.onLoad, this);
12654             this.store.un('loadexception', this.onLoadException, this);
12655         }
12656         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12657     },
12658
12659     // private
12660     fireKey : function(e){
12661         if(e.isNavKeyPress() && !this.list.isVisible()){
12662             this.fireEvent("specialkey", this, e);
12663         }
12664     },
12665
12666     // private
12667     onResize: function(w, h){
12668 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12669 //        
12670 //        if(typeof w != 'number'){
12671 //            // we do not handle it!?!?
12672 //            return;
12673 //        }
12674 //        var tw = this.trigger.getWidth();
12675 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12676 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12677 //        var x = w - tw;
12678 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12679 //            
12680 //        //this.trigger.setStyle('left', x+'px');
12681 //        
12682 //        if(this.list && this.listWidth === undefined){
12683 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12684 //            this.list.setWidth(lw);
12685 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12686 //        }
12687         
12688     
12689         
12690     },
12691
12692     /**
12693      * Allow or prevent the user from directly editing the field text.  If false is passed,
12694      * the user will only be able to select from the items defined in the dropdown list.  This method
12695      * is the runtime equivalent of setting the 'editable' config option at config time.
12696      * @param {Boolean} value True to allow the user to directly edit the field text
12697      */
12698     setEditable : function(value){
12699         if(value == this.editable){
12700             return;
12701         }
12702         this.editable = value;
12703         if(!value){
12704             this.inputEl().dom.setAttribute('readOnly', true);
12705             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12706             this.inputEl().addClass('x-combo-noedit');
12707         }else{
12708             this.inputEl().dom.setAttribute('readOnly', false);
12709             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12710             this.inputEl().removeClass('x-combo-noedit');
12711         }
12712     },
12713
12714     // private
12715     
12716     onBeforeLoad : function(combo,opts){
12717         if(!this.hasFocus){
12718             return;
12719         }
12720          if (!opts.add) {
12721             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12722          }
12723         this.restrictHeight();
12724         this.selectedIndex = -1;
12725     },
12726
12727     // private
12728     onLoad : function(){
12729         
12730         this.hasQuery = false;
12731         
12732         if(!this.hasFocus){
12733             return;
12734         }
12735         
12736         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12737             this.loading.hide();
12738         }
12739              
12740         if(this.store.getCount() > 0){
12741             this.expand();
12742             this.restrictHeight();
12743             if(this.lastQuery == this.allQuery){
12744                 if(this.editable && !this.tickable){
12745                     this.inputEl().dom.select();
12746                 }
12747                 
12748                 if(
12749                     !this.selectByValue(this.value, true) &&
12750                     this.autoFocus && 
12751                     (
12752                         !this.store.lastOptions ||
12753                         typeof(this.store.lastOptions.add) == 'undefined' || 
12754                         this.store.lastOptions.add != true
12755                     )
12756                 ){
12757                     this.select(0, true);
12758                 }
12759             }else{
12760                 if(this.autoFocus){
12761                     this.selectNext();
12762                 }
12763                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12764                     this.taTask.delay(this.typeAheadDelay);
12765                 }
12766             }
12767         }else{
12768             this.onEmptyResults();
12769         }
12770         
12771         //this.el.focus();
12772     },
12773     // private
12774     onLoadException : function()
12775     {
12776         this.hasQuery = false;
12777         
12778         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12779             this.loading.hide();
12780         }
12781         
12782         if(this.tickable && this.editable){
12783             return;
12784         }
12785         
12786         this.collapse();
12787         // only causes errors at present
12788         //Roo.log(this.store.reader.jsonData);
12789         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12790             // fixme
12791             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12792         //}
12793         
12794         
12795     },
12796     // private
12797     onTypeAhead : function(){
12798         if(this.store.getCount() > 0){
12799             var r = this.store.getAt(0);
12800             var newValue = r.data[this.displayField];
12801             var len = newValue.length;
12802             var selStart = this.getRawValue().length;
12803             
12804             if(selStart != len){
12805                 this.setRawValue(newValue);
12806                 this.selectText(selStart, newValue.length);
12807             }
12808         }
12809     },
12810
12811     // private
12812     onSelect : function(record, index){
12813         
12814         if(this.fireEvent('beforeselect', this, record, index) !== false){
12815         
12816             this.setFromData(index > -1 ? record.data : false);
12817             
12818             this.collapse();
12819             this.fireEvent('select', this, record, index);
12820         }
12821     },
12822
12823     /**
12824      * Returns the currently selected field value or empty string if no value is set.
12825      * @return {String} value The selected value
12826      */
12827     getValue : function(){
12828         
12829         if(this.multiple){
12830             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12831         }
12832         
12833         if(this.valueField){
12834             return typeof this.value != 'undefined' ? this.value : '';
12835         }else{
12836             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12837         }
12838     },
12839
12840     /**
12841      * Clears any text/value currently set in the field
12842      */
12843     clearValue : function(){
12844         if(this.hiddenField){
12845             this.hiddenField.dom.value = '';
12846         }
12847         this.value = '';
12848         this.setRawValue('');
12849         this.lastSelectionText = '';
12850         this.lastData = false;
12851         
12852         var close = this.closeTriggerEl();
12853         
12854         if(close){
12855             close.hide();
12856         }
12857         
12858     },
12859
12860     /**
12861      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12862      * will be displayed in the field.  If the value does not match the data value of an existing item,
12863      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12864      * Otherwise the field will be blank (although the value will still be set).
12865      * @param {String} value The value to match
12866      */
12867     setValue : function(v){
12868         if(this.multiple){
12869             this.syncValue();
12870             return;
12871         }
12872         
12873         var text = v;
12874         if(this.valueField){
12875             var r = this.findRecord(this.valueField, v);
12876             if(r){
12877                 text = r.data[this.displayField];
12878             }else if(this.valueNotFoundText !== undefined){
12879                 text = this.valueNotFoundText;
12880             }
12881         }
12882         this.lastSelectionText = text;
12883         if(this.hiddenField){
12884             this.hiddenField.dom.value = v;
12885         }
12886         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12887         this.value = v;
12888         
12889         var close = this.closeTriggerEl();
12890         
12891         if(close){
12892             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12893         }
12894     },
12895     /**
12896      * @property {Object} the last set data for the element
12897      */
12898     
12899     lastData : false,
12900     /**
12901      * Sets the value of the field based on a object which is related to the record format for the store.
12902      * @param {Object} value the value to set as. or false on reset?
12903      */
12904     setFromData : function(o){
12905         
12906         if(this.multiple){
12907             this.addItem(o);
12908             return;
12909         }
12910             
12911         var dv = ''; // display value
12912         var vv = ''; // value value..
12913         this.lastData = o;
12914         if (this.displayField) {
12915             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12916         } else {
12917             // this is an error condition!!!
12918             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12919         }
12920         
12921         if(this.valueField){
12922             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12923         }
12924         
12925         var close = this.closeTriggerEl();
12926         
12927         if(close){
12928             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12929         }
12930         
12931         if(this.hiddenField){
12932             this.hiddenField.dom.value = vv;
12933             
12934             this.lastSelectionText = dv;
12935             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12936             this.value = vv;
12937             return;
12938         }
12939         // no hidden field.. - we store the value in 'value', but still display
12940         // display field!!!!
12941         this.lastSelectionText = dv;
12942         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12943         this.value = vv;
12944         
12945         
12946         
12947     },
12948     // private
12949     reset : function(){
12950         // overridden so that last data is reset..
12951         
12952         if(this.multiple){
12953             this.clearItem();
12954             return;
12955         }
12956         
12957         this.setValue(this.originalValue);
12958         this.clearInvalid();
12959         this.lastData = false;
12960         if (this.view) {
12961             this.view.clearSelections();
12962         }
12963     },
12964     // private
12965     findRecord : function(prop, value){
12966         var record;
12967         if(this.store.getCount() > 0){
12968             this.store.each(function(r){
12969                 if(r.data[prop] == value){
12970                     record = r;
12971                     return false;
12972                 }
12973                 return true;
12974             });
12975         }
12976         return record;
12977     },
12978     
12979     getName: function()
12980     {
12981         // returns hidden if it's set..
12982         if (!this.rendered) {return ''};
12983         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12984         
12985     },
12986     // private
12987     onViewMove : function(e, t){
12988         this.inKeyMode = false;
12989     },
12990
12991     // private
12992     onViewOver : function(e, t){
12993         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12994             return;
12995         }
12996         var item = this.view.findItemFromChild(t);
12997         
12998         if(item){
12999             var index = this.view.indexOf(item);
13000             this.select(index, false);
13001         }
13002     },
13003
13004     // private
13005     onViewClick : function(view, doFocus, el, e)
13006     {
13007         var index = this.view.getSelectedIndexes()[0];
13008         
13009         var r = this.store.getAt(index);
13010         
13011         if(this.tickable){
13012             
13013             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13014                 return;
13015             }
13016             
13017             var rm = false;
13018             var _this = this;
13019             
13020             Roo.each(this.tickItems, function(v,k){
13021                 
13022                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13023                     Roo.log(v);
13024                     _this.tickItems.splice(k, 1);
13025                     
13026                     if(typeof(e) == 'undefined' && view == false){
13027                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13028                     }
13029                     
13030                     rm = true;
13031                     return;
13032                 }
13033             });
13034             
13035             if(rm){
13036                 return;
13037             }
13038             
13039             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13040                 this.tickItems.push(r.data);
13041             }
13042             
13043             if(typeof(e) == 'undefined' && view == false){
13044                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13045             }
13046                     
13047             return;
13048         }
13049         
13050         if(r){
13051             this.onSelect(r, index);
13052         }
13053         if(doFocus !== false && !this.blockFocus){
13054             this.inputEl().focus();
13055         }
13056     },
13057
13058     // private
13059     restrictHeight : function(){
13060         //this.innerList.dom.style.height = '';
13061         //var inner = this.innerList.dom;
13062         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13063         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13064         //this.list.beginUpdate();
13065         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13066         this.list.alignTo(this.inputEl(), this.listAlign);
13067         this.list.alignTo(this.inputEl(), this.listAlign);
13068         //this.list.endUpdate();
13069     },
13070
13071     // private
13072     onEmptyResults : function(){
13073         
13074         if(this.tickable && this.editable){
13075             this.restrictHeight();
13076             return;
13077         }
13078         
13079         this.collapse();
13080     },
13081
13082     /**
13083      * Returns true if the dropdown list is expanded, else false.
13084      */
13085     isExpanded : function(){
13086         return this.list.isVisible();
13087     },
13088
13089     /**
13090      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13091      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13092      * @param {String} value The data value of the item to select
13093      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13094      * selected item if it is not currently in view (defaults to true)
13095      * @return {Boolean} True if the value matched an item in the list, else false
13096      */
13097     selectByValue : function(v, scrollIntoView){
13098         if(v !== undefined && v !== null){
13099             var r = this.findRecord(this.valueField || this.displayField, v);
13100             if(r){
13101                 this.select(this.store.indexOf(r), scrollIntoView);
13102                 return true;
13103             }
13104         }
13105         return false;
13106     },
13107
13108     /**
13109      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13110      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13111      * @param {Number} index The zero-based index of the list item to select
13112      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13113      * selected item if it is not currently in view (defaults to true)
13114      */
13115     select : function(index, scrollIntoView){
13116         this.selectedIndex = index;
13117         this.view.select(index);
13118         if(scrollIntoView !== false){
13119             var el = this.view.getNode(index);
13120             /*
13121              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13122              */
13123             if(el){
13124                 this.list.scrollChildIntoView(el, false);
13125             }
13126         }
13127     },
13128
13129     // private
13130     selectNext : function(){
13131         var ct = this.store.getCount();
13132         if(ct > 0){
13133             if(this.selectedIndex == -1){
13134                 this.select(0);
13135             }else if(this.selectedIndex < ct-1){
13136                 this.select(this.selectedIndex+1);
13137             }
13138         }
13139     },
13140
13141     // private
13142     selectPrev : function(){
13143         var ct = this.store.getCount();
13144         if(ct > 0){
13145             if(this.selectedIndex == -1){
13146                 this.select(0);
13147             }else if(this.selectedIndex != 0){
13148                 this.select(this.selectedIndex-1);
13149             }
13150         }
13151     },
13152
13153     // private
13154     onKeyUp : function(e){
13155         if(this.editable !== false && !e.isSpecialKey()){
13156             this.lastKey = e.getKey();
13157             this.dqTask.delay(this.queryDelay);
13158         }
13159     },
13160
13161     // private
13162     validateBlur : function(){
13163         return !this.list || !this.list.isVisible();   
13164     },
13165
13166     // private
13167     initQuery : function(){
13168         
13169         var v = this.getRawValue();
13170         
13171         if(this.tickable && this.editable){
13172             v = this.tickableInputEl().getValue();
13173         }
13174         
13175         this.doQuery(v);
13176     },
13177
13178     // private
13179     doForce : function(){
13180         if(this.inputEl().dom.value.length > 0){
13181             this.inputEl().dom.value =
13182                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13183              
13184         }
13185     },
13186
13187     /**
13188      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13189      * query allowing the query action to be canceled if needed.
13190      * @param {String} query The SQL query to execute
13191      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13192      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13193      * saved in the current store (defaults to false)
13194      */
13195     doQuery : function(q, forceAll){
13196         
13197         if(q === undefined || q === null){
13198             q = '';
13199         }
13200         var qe = {
13201             query: q,
13202             forceAll: forceAll,
13203             combo: this,
13204             cancel:false
13205         };
13206         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13207             return false;
13208         }
13209         q = qe.query;
13210         
13211         forceAll = qe.forceAll;
13212         if(forceAll === true || (q.length >= this.minChars)){
13213             
13214             this.hasQuery = true;
13215             
13216             if(this.lastQuery != q || this.alwaysQuery){
13217                 this.lastQuery = q;
13218                 if(this.mode == 'local'){
13219                     this.selectedIndex = -1;
13220                     if(forceAll){
13221                         this.store.clearFilter();
13222                     }else{
13223                         
13224                         if(this.specialFilter){
13225                             this.fireEvent('specialfilter', this);
13226                             this.onLoad();
13227                             return;
13228                         }
13229                         
13230                         this.store.filter(this.displayField, q);
13231                     }
13232                     
13233                     this.store.fireEvent("datachanged", this.store);
13234                     
13235                     this.onLoad();
13236                     
13237                     
13238                 }else{
13239                     
13240                     this.store.baseParams[this.queryParam] = q;
13241                     
13242                     var options = {params : this.getParams(q)};
13243                     
13244                     if(this.loadNext){
13245                         options.add = true;
13246                         options.params.start = this.page * this.pageSize;
13247                     }
13248                     
13249                     this.store.load(options);
13250                     
13251                     /*
13252                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13253                      *  we should expand the list on onLoad
13254                      *  so command out it
13255                      */
13256 //                    this.expand();
13257                 }
13258             }else{
13259                 this.selectedIndex = -1;
13260                 this.onLoad();   
13261             }
13262         }
13263         
13264         this.loadNext = false;
13265     },
13266     
13267     // private
13268     getParams : function(q){
13269         var p = {};
13270         //p[this.queryParam] = q;
13271         
13272         if(this.pageSize){
13273             p.start = 0;
13274             p.limit = this.pageSize;
13275         }
13276         return p;
13277     },
13278
13279     /**
13280      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13281      */
13282     collapse : function(){
13283         if(!this.isExpanded()){
13284             return;
13285         }
13286         
13287         this.list.hide();
13288         
13289         if(this.tickable){
13290             this.hasFocus = false;
13291             this.okBtn.hide();
13292             this.cancelBtn.hide();
13293             this.trigger.show();
13294             
13295             if(this.editable){
13296                 this.tickableInputEl().dom.value = '';
13297                 this.tickableInputEl().blur();
13298             }
13299             
13300         }
13301         
13302         Roo.get(document).un('mousedown', this.collapseIf, this);
13303         Roo.get(document).un('mousewheel', this.collapseIf, this);
13304         if (!this.editable) {
13305             Roo.get(document).un('keydown', this.listKeyPress, this);
13306         }
13307         this.fireEvent('collapse', this);
13308     },
13309
13310     // private
13311     collapseIf : function(e){
13312         var in_combo  = e.within(this.el);
13313         var in_list =  e.within(this.list);
13314         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13315         
13316         if (in_combo || in_list || is_list) {
13317             //e.stopPropagation();
13318             return;
13319         }
13320         
13321         if(this.tickable){
13322             this.onTickableFooterButtonClick(e, false, false);
13323         }
13324
13325         this.collapse();
13326         
13327     },
13328
13329     /**
13330      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13331      */
13332     expand : function(){
13333        
13334         if(this.isExpanded() || !this.hasFocus){
13335             return;
13336         }
13337         
13338         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13339         this.list.setWidth(lw);
13340         
13341         
13342          Roo.log('expand');
13343         
13344         this.list.show();
13345         
13346         this.restrictHeight();
13347         
13348         if(this.tickable){
13349             
13350             this.tickItems = Roo.apply([], this.item);
13351             
13352             this.okBtn.show();
13353             this.cancelBtn.show();
13354             this.trigger.hide();
13355             
13356             if(this.editable){
13357                 this.tickableInputEl().focus();
13358             }
13359             
13360         }
13361         
13362         Roo.get(document).on('mousedown', this.collapseIf, this);
13363         Roo.get(document).on('mousewheel', this.collapseIf, this);
13364         if (!this.editable) {
13365             Roo.get(document).on('keydown', this.listKeyPress, this);
13366         }
13367         
13368         this.fireEvent('expand', this);
13369     },
13370
13371     // private
13372     // Implements the default empty TriggerField.onTriggerClick function
13373     onTriggerClick : function(e)
13374     {
13375         Roo.log('trigger click');
13376         
13377         if(this.disabled || !this.triggerList){
13378             return;
13379         }
13380         
13381         this.page = 0;
13382         this.loadNext = false;
13383         
13384         if(this.isExpanded()){
13385             this.collapse();
13386             if (!this.blockFocus) {
13387                 this.inputEl().focus();
13388             }
13389             
13390         }else {
13391             this.hasFocus = true;
13392             if(this.triggerAction == 'all') {
13393                 this.doQuery(this.allQuery, true);
13394             } else {
13395                 this.doQuery(this.getRawValue());
13396             }
13397             if (!this.blockFocus) {
13398                 this.inputEl().focus();
13399             }
13400         }
13401     },
13402     
13403     onTickableTriggerClick : function(e)
13404     {
13405         if(this.disabled){
13406             return;
13407         }
13408         
13409         this.page = 0;
13410         this.loadNext = false;
13411         this.hasFocus = true;
13412         
13413         if(this.triggerAction == 'all') {
13414             this.doQuery(this.allQuery, true);
13415         } else {
13416             this.doQuery(this.getRawValue());
13417         }
13418     },
13419     
13420     onSearchFieldClick : function(e)
13421     {
13422         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13423             this.onTickableFooterButtonClick(e, false, false);
13424             return;
13425         }
13426         
13427         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13428             return;
13429         }
13430         
13431         this.page = 0;
13432         this.loadNext = false;
13433         this.hasFocus = true;
13434         
13435         if(this.triggerAction == 'all') {
13436             this.doQuery(this.allQuery, true);
13437         } else {
13438             this.doQuery(this.getRawValue());
13439         }
13440     },
13441     
13442     listKeyPress : function(e)
13443     {
13444         //Roo.log('listkeypress');
13445         // scroll to first matching element based on key pres..
13446         if (e.isSpecialKey()) {
13447             return false;
13448         }
13449         var k = String.fromCharCode(e.getKey()).toUpperCase();
13450         //Roo.log(k);
13451         var match  = false;
13452         var csel = this.view.getSelectedNodes();
13453         var cselitem = false;
13454         if (csel.length) {
13455             var ix = this.view.indexOf(csel[0]);
13456             cselitem  = this.store.getAt(ix);
13457             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13458                 cselitem = false;
13459             }
13460             
13461         }
13462         
13463         this.store.each(function(v) { 
13464             if (cselitem) {
13465                 // start at existing selection.
13466                 if (cselitem.id == v.id) {
13467                     cselitem = false;
13468                 }
13469                 return true;
13470             }
13471                 
13472             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13473                 match = this.store.indexOf(v);
13474                 return false;
13475             }
13476             return true;
13477         }, this);
13478         
13479         if (match === false) {
13480             return true; // no more action?
13481         }
13482         // scroll to?
13483         this.view.select(match);
13484         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13485         sn.scrollIntoView(sn.dom.parentNode, false);
13486     },
13487     
13488     onViewScroll : function(e, t){
13489         
13490         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){
13491             return;
13492         }
13493         
13494         this.hasQuery = true;
13495         
13496         this.loading = this.list.select('.loading', true).first();
13497         
13498         if(this.loading === null){
13499             this.list.createChild({
13500                 tag: 'div',
13501                 cls: 'loading roo-select2-more-results roo-select2-active',
13502                 html: 'Loading more results...'
13503             });
13504             
13505             this.loading = this.list.select('.loading', true).first();
13506             
13507             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13508             
13509             this.loading.hide();
13510         }
13511         
13512         this.loading.show();
13513         
13514         var _combo = this;
13515         
13516         this.page++;
13517         this.loadNext = true;
13518         
13519         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13520         
13521         return;
13522     },
13523     
13524     addItem : function(o)
13525     {   
13526         var dv = ''; // display value
13527         
13528         if (this.displayField) {
13529             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13530         } else {
13531             // this is an error condition!!!
13532             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13533         }
13534         
13535         if(!dv.length){
13536             return;
13537         }
13538         
13539         var choice = this.choices.createChild({
13540             tag: 'li',
13541             cls: 'roo-select2-search-choice',
13542             cn: [
13543                 {
13544                     tag: 'div',
13545                     html: dv
13546                 },
13547                 {
13548                     tag: 'a',
13549                     href: '#',
13550                     cls: 'roo-select2-search-choice-close',
13551                     tabindex: '-1'
13552                 }
13553             ]
13554             
13555         }, this.searchField);
13556         
13557         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13558         
13559         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13560         
13561         this.item.push(o);
13562         
13563         this.lastData = o;
13564         
13565         this.syncValue();
13566         
13567         this.inputEl().dom.value = '';
13568         
13569         this.validate();
13570     },
13571     
13572     onRemoveItem : function(e, _self, o)
13573     {
13574         e.preventDefault();
13575         
13576         this.lastItem = Roo.apply([], this.item);
13577         
13578         var index = this.item.indexOf(o.data) * 1;
13579         
13580         if( index < 0){
13581             Roo.log('not this item?!');
13582             return;
13583         }
13584         
13585         this.item.splice(index, 1);
13586         o.item.remove();
13587         
13588         this.syncValue();
13589         
13590         this.fireEvent('remove', this, e);
13591         
13592         this.validate();
13593         
13594     },
13595     
13596     syncValue : function()
13597     {
13598         if(!this.item.length){
13599             this.clearValue();
13600             return;
13601         }
13602             
13603         var value = [];
13604         var _this = this;
13605         Roo.each(this.item, function(i){
13606             if(_this.valueField){
13607                 value.push(i[_this.valueField]);
13608                 return;
13609             }
13610
13611             value.push(i);
13612         });
13613
13614         this.value = value.join(',');
13615
13616         if(this.hiddenField){
13617             this.hiddenField.dom.value = this.value;
13618         }
13619         
13620         this.store.fireEvent("datachanged", this.store);
13621     },
13622     
13623     clearItem : function()
13624     {
13625         if(!this.multiple){
13626             return;
13627         }
13628         
13629         this.item = [];
13630         
13631         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13632            c.remove();
13633         });
13634         
13635         this.syncValue();
13636         
13637         this.validate();
13638         
13639         if(this.tickable && !Roo.isTouch){
13640             this.view.refresh();
13641         }
13642     },
13643     
13644     inputEl: function ()
13645     {
13646         if(Roo.isTouch && this.mobileTouchView){
13647             return this.el.select('input.form-control',true).first();
13648         }
13649         
13650         if(this.tickable){
13651             return this.searchField;
13652         }
13653         
13654         return this.el.select('input.form-control',true).first();
13655     },
13656     
13657     
13658     onTickableFooterButtonClick : function(e, btn, el)
13659     {
13660         e.preventDefault();
13661         
13662         this.lastItem = Roo.apply([], this.item);
13663         
13664         if(btn && btn.name == 'cancel'){
13665             this.tickItems = Roo.apply([], this.item);
13666             this.collapse();
13667             return;
13668         }
13669         
13670         this.clearItem();
13671         
13672         var _this = this;
13673         
13674         Roo.each(this.tickItems, function(o){
13675             _this.addItem(o);
13676         });
13677         
13678         this.collapse();
13679         
13680     },
13681     
13682     validate : function()
13683     {
13684         var v = this.getRawValue();
13685         
13686         if(this.multiple){
13687             v = this.getValue();
13688         }
13689         
13690         if(this.disabled || this.allowBlank || v.length){
13691             this.markValid();
13692             return true;
13693         }
13694         
13695         this.markInvalid();
13696         return false;
13697     },
13698     
13699     tickableInputEl : function()
13700     {
13701         if(!this.tickable || !this.editable){
13702             return this.inputEl();
13703         }
13704         
13705         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13706     },
13707     
13708     
13709     getAutoCreateTouchView : function()
13710     {
13711         var id = Roo.id();
13712         
13713         var cfg = {
13714             cls: 'form-group' //input-group
13715         };
13716         
13717         var input =  {
13718             tag: 'input',
13719             id : id,
13720             type : this.inputType,
13721             cls : 'form-control x-combo-noedit',
13722             autocomplete: 'new-password',
13723             placeholder : this.placeholder || '',
13724             readonly : true
13725         };
13726         
13727         if (this.name) {
13728             input.name = this.name;
13729         }
13730         
13731         if (this.size) {
13732             input.cls += ' input-' + this.size;
13733         }
13734         
13735         if (this.disabled) {
13736             input.disabled = true;
13737         }
13738         
13739         var inputblock = {
13740             cls : '',
13741             cn : [
13742                 input
13743             ]
13744         };
13745         
13746         if(this.before){
13747             inputblock.cls += ' input-group';
13748             
13749             inputblock.cn.unshift({
13750                 tag :'span',
13751                 cls : 'input-group-addon',
13752                 html : this.before
13753             });
13754         }
13755         
13756         if(this.removable && !this.multiple){
13757             inputblock.cls += ' roo-removable';
13758             
13759             inputblock.cn.push({
13760                 tag: 'button',
13761                 html : 'x',
13762                 cls : 'roo-combo-removable-btn close'
13763             });
13764         }
13765
13766         if(this.hasFeedback && !this.allowBlank){
13767             
13768             inputblock.cls += ' has-feedback';
13769             
13770             inputblock.cn.push({
13771                 tag: 'span',
13772                 cls: 'glyphicon form-control-feedback'
13773             });
13774             
13775         }
13776         
13777         if (this.after) {
13778             
13779             inputblock.cls += (this.before) ? '' : ' input-group';
13780             
13781             inputblock.cn.push({
13782                 tag :'span',
13783                 cls : 'input-group-addon',
13784                 html : this.after
13785             });
13786         }
13787
13788         var box = {
13789             tag: 'div',
13790             cn: [
13791                 {
13792                     tag: 'input',
13793                     type : 'hidden',
13794                     cls: 'form-hidden-field'
13795                 },
13796                 inputblock
13797             ]
13798             
13799         };
13800         
13801         if(this.multiple){
13802             box = {
13803                 tag: 'div',
13804                 cn: [
13805                     {
13806                         tag: 'input',
13807                         type : 'hidden',
13808                         cls: 'form-hidden-field'
13809                     },
13810                     {
13811                         tag: 'ul',
13812                         cls: 'roo-select2-choices',
13813                         cn:[
13814                             {
13815                                 tag: 'li',
13816                                 cls: 'roo-select2-search-field',
13817                                 cn: [
13818
13819                                     inputblock
13820                                 ]
13821                             }
13822                         ]
13823                     }
13824                 ]
13825             }
13826         };
13827         
13828         var combobox = {
13829             cls: 'roo-select2-container input-group',
13830             cn: [
13831                 box
13832             ]
13833         };
13834         
13835         if(this.multiple){
13836             combobox.cls += ' roo-select2-container-multi';
13837         }
13838         
13839         var align = this.labelAlign || this.parentLabelAlign();
13840         
13841         cfg.cn = combobox;
13842         
13843         if(this.fieldLabel.length){
13844             
13845             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13846             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13847             
13848             cfg.cn = [
13849                 {
13850                     tag: 'label',
13851                     cls : 'control-label ' + lw,
13852                     html : this.fieldLabel
13853
13854                 },
13855                 {
13856                     cls : cw, 
13857                     cn: [
13858                         combobox
13859                     ]
13860                 }
13861             ];
13862         }
13863         
13864         var settings = this;
13865         
13866         ['xs','sm','md','lg'].map(function(size){
13867             if (settings[size]) {
13868                 cfg.cls += ' col-' + size + '-' + settings[size];
13869             }
13870         });
13871         
13872         return cfg;
13873     },
13874     
13875     initTouchView : function()
13876     {
13877         this.renderTouchView();
13878         
13879         this.touchViewEl.on('scroll', function(){
13880             this.el.dom.scrollTop = 0;
13881         }, this);
13882         
13883         this.originalValue = this.getValue();
13884         
13885         this.inputEl().on("click", this.showTouchView, this);
13886         
13887         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13888         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13889         
13890         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13891         
13892         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13893         this.store.on('load', this.onTouchViewLoad, this);
13894         this.store.on('loadexception', this.onTouchViewLoadException, this);
13895         
13896         if(this.hiddenName){
13897             
13898             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13899             
13900             this.hiddenField.dom.value =
13901                 this.hiddenValue !== undefined ? this.hiddenValue :
13902                 this.value !== undefined ? this.value : '';
13903         
13904             this.el.dom.removeAttribute('name');
13905             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13906         }
13907         
13908         if(this.multiple){
13909             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13910             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13911         }
13912         
13913         if(this.removable && !this.multiple){
13914             var close = this.closeTriggerEl();
13915             if(close){
13916                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13917                 close.on('click', this.removeBtnClick, this, close);
13918             }
13919         }
13920         /*
13921          * fix the bug in Safari iOS8
13922          */
13923         this.inputEl().on("focus", function(e){
13924             document.activeElement.blur();
13925         }, this);
13926         
13927         return;
13928         
13929         
13930     },
13931     
13932     renderTouchView : function()
13933     {
13934         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13935         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13936         
13937         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13938         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13939         
13940         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13941         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13942         this.touchViewBodyEl.setStyle('overflow', 'auto');
13943         
13944         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13945         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13946         
13947         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13948         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13949         
13950     },
13951     
13952     showTouchView : function()
13953     {
13954         if(this.disabled){
13955             return;
13956         }
13957         
13958         this.touchViewHeaderEl.hide();
13959
13960         if(this.fieldLabel.length){
13961             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13962             this.touchViewHeaderEl.show();
13963         }
13964
13965         this.touchViewEl.show();
13966
13967         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13968         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13969
13970         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13971
13972         if(this.fieldLabel.length){
13973             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13974         }
13975         
13976         this.touchViewBodyEl.setHeight(bodyHeight);
13977
13978         if(this.animate){
13979             var _this = this;
13980             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13981         }else{
13982             this.touchViewEl.addClass('in');
13983         }
13984
13985         this.doTouchViewQuery();
13986         
13987     },
13988     
13989     hideTouchView : function()
13990     {
13991         this.touchViewEl.removeClass('in');
13992
13993         if(this.animate){
13994             var _this = this;
13995             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13996         }else{
13997             this.touchViewEl.setStyle('display', 'none');
13998         }
13999         
14000     },
14001     
14002     setTouchViewValue : function()
14003     {
14004         if(this.multiple){
14005             this.clearItem();
14006         
14007             var _this = this;
14008
14009             Roo.each(this.tickItems, function(o){
14010                 this.addItem(o);
14011             }, this);
14012         }
14013         
14014         this.hideTouchView();
14015     },
14016     
14017     doTouchViewQuery : function()
14018     {
14019         var qe = {
14020             query: '',
14021             forceAll: true,
14022             combo: this,
14023             cancel:false
14024         };
14025         
14026         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14027             return false;
14028         }
14029         
14030         if(!this.alwaysQuery || this.mode == 'local'){
14031             this.onTouchViewLoad();
14032             return;
14033         }
14034         
14035         this.store.load();
14036     },
14037     
14038     onTouchViewBeforeLoad : function(combo,opts)
14039     {
14040         return;
14041     },
14042
14043     // private
14044     onTouchViewLoad : function()
14045     {
14046         if(this.store.getCount() < 1){
14047             this.onTouchViewEmptyResults();
14048             return;
14049         }
14050         
14051         this.clearTouchView();
14052         
14053         var rawValue = this.getRawValue();
14054         
14055         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14056         
14057         this.tickItems = [];
14058         
14059         this.store.data.each(function(d, rowIndex){
14060             var row = this.touchViewListGroup.createChild(template);
14061             
14062             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14063                 row.addClass(d.data.cls);
14064             }
14065             
14066             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14067                 var cfg = {
14068                     data : d.data,
14069                     html : d.data[this.displayField]
14070                 };
14071                 
14072                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14073                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14074                 }
14075             }
14076             
14077             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
14078                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14079             }
14080             
14081             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
14082                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14083                 this.tickItems.push(d.data);
14084             }
14085             
14086             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14087             
14088         }, this);
14089         
14090         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14091         
14092         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14093
14094         if(this.fieldLabel.length){
14095             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14096         }
14097
14098         var listHeight = this.touchViewListGroup.getHeight();
14099         
14100         var _this = this;
14101         
14102         if(firstChecked && listHeight > bodyHeight){
14103             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14104         }
14105         
14106     },
14107     
14108     onTouchViewLoadException : function()
14109     {
14110         this.hideTouchView();
14111     },
14112     
14113     onTouchViewEmptyResults : function()
14114     {
14115         this.clearTouchView();
14116         
14117         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14118         
14119         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14120         
14121     },
14122     
14123     clearTouchView : function()
14124     {
14125         this.touchViewListGroup.dom.innerHTML = '';
14126     },
14127     
14128     onTouchViewClick : function(e, el, o)
14129     {
14130         e.preventDefault();
14131         
14132         var row = o.row;
14133         var rowIndex = o.rowIndex;
14134         
14135         var r = this.store.getAt(rowIndex);
14136         
14137         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14138             
14139             if(!this.multiple){
14140                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14141                     c.dom.removeAttribute('checked');
14142                 }, this);
14143
14144                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14145
14146                 this.setFromData(r.data);
14147
14148                 var close = this.closeTriggerEl();
14149
14150                 if(close){
14151                     close.show();
14152                 }
14153
14154                 this.hideTouchView();
14155
14156                 this.fireEvent('select', this, r, rowIndex);
14157
14158                 return;
14159             }
14160
14161             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14162                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14163                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14164                 return;
14165             }
14166
14167             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14168             this.addItem(r.data);
14169             this.tickItems.push(r.data);
14170         }
14171     }
14172     
14173
14174     /** 
14175     * @cfg {Boolean} grow 
14176     * @hide 
14177     */
14178     /** 
14179     * @cfg {Number} growMin 
14180     * @hide 
14181     */
14182     /** 
14183     * @cfg {Number} growMax 
14184     * @hide 
14185     */
14186     /**
14187      * @hide
14188      * @method autoSize
14189      */
14190 });
14191
14192 Roo.apply(Roo.bootstrap.ComboBox,  {
14193     
14194     header : {
14195         tag: 'div',
14196         cls: 'modal-header',
14197         cn: [
14198             {
14199                 tag: 'h4',
14200                 cls: 'modal-title'
14201             }
14202         ]
14203     },
14204     
14205     body : {
14206         tag: 'div',
14207         cls: 'modal-body',
14208         cn: [
14209             {
14210                 tag: 'ul',
14211                 cls: 'list-group'
14212             }
14213         ]
14214     },
14215     
14216     listItemRadio : {
14217         tag: 'li',
14218         cls: 'list-group-item',
14219         cn: [
14220             {
14221                 tag: 'span',
14222                 cls: 'roo-combobox-list-group-item-value'
14223             },
14224             {
14225                 tag: 'div',
14226                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14227                 cn: [
14228                     {
14229                         tag: 'input',
14230                         type: 'radio'
14231                     },
14232                     {
14233                         tag: 'label'
14234                     }
14235                 ]
14236             }
14237         ]
14238     },
14239     
14240     listItemCheckbox : {
14241         tag: 'li',
14242         cls: 'list-group-item',
14243         cn: [
14244             {
14245                 tag: 'span',
14246                 cls: 'roo-combobox-list-group-item-value'
14247             },
14248             {
14249                 tag: 'div',
14250                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14251                 cn: [
14252                     {
14253                         tag: 'input',
14254                         type: 'checkbox'
14255                     },
14256                     {
14257                         tag: 'label'
14258                     }
14259                 ]
14260             }
14261         ]
14262     },
14263     
14264     emptyResult : {
14265         tag: 'div',
14266         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14267     },
14268     
14269     footer : {
14270         tag: 'div',
14271         cls: 'modal-footer',
14272         cn: [
14273             {
14274                 tag: 'div',
14275                 cls: 'row',
14276                 cn: [
14277                     {
14278                         tag: 'div',
14279                         cls: 'col-xs-6 text-left',
14280                         cn: {
14281                             tag: 'button',
14282                             cls: 'btn btn-danger roo-touch-view-cancel',
14283                             html: 'Cancel'
14284                         }
14285                     },
14286                     {
14287                         tag: 'div',
14288                         cls: 'col-xs-6 text-right',
14289                         cn: {
14290                             tag: 'button',
14291                             cls: 'btn btn-success roo-touch-view-ok',
14292                             html: 'OK'
14293                         }
14294                     }
14295                 ]
14296             }
14297         ]
14298         
14299     }
14300 });
14301
14302 Roo.apply(Roo.bootstrap.ComboBox,  {
14303     
14304     touchViewTemplate : {
14305         tag: 'div',
14306         cls: 'modal fade roo-combobox-touch-view',
14307         cn: [
14308             {
14309                 tag: 'div',
14310                 cls: 'modal-dialog',
14311                 style : 'position:fixed', // we have to fix position....
14312                 cn: [
14313                     {
14314                         tag: 'div',
14315                         cls: 'modal-content',
14316                         cn: [
14317                             Roo.bootstrap.ComboBox.header,
14318                             Roo.bootstrap.ComboBox.body,
14319                             Roo.bootstrap.ComboBox.footer
14320                         ]
14321                     }
14322                 ]
14323             }
14324         ]
14325     }
14326 });/*
14327  * Based on:
14328  * Ext JS Library 1.1.1
14329  * Copyright(c) 2006-2007, Ext JS, LLC.
14330  *
14331  * Originally Released Under LGPL - original licence link has changed is not relivant.
14332  *
14333  * Fork - LGPL
14334  * <script type="text/javascript">
14335  */
14336
14337 /**
14338  * @class Roo.View
14339  * @extends Roo.util.Observable
14340  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14341  * This class also supports single and multi selection modes. <br>
14342  * Create a data model bound view:
14343  <pre><code>
14344  var store = new Roo.data.Store(...);
14345
14346  var view = new Roo.View({
14347     el : "my-element",
14348     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14349  
14350     singleSelect: true,
14351     selectedClass: "ydataview-selected",
14352     store: store
14353  });
14354
14355  // listen for node click?
14356  view.on("click", function(vw, index, node, e){
14357  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14358  });
14359
14360  // load XML data
14361  dataModel.load("foobar.xml");
14362  </code></pre>
14363  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14364  * <br><br>
14365  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14366  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14367  * 
14368  * Note: old style constructor is still suported (container, template, config)
14369  * 
14370  * @constructor
14371  * Create a new View
14372  * @param {Object} config The config object
14373  * 
14374  */
14375 Roo.View = function(config, depreciated_tpl, depreciated_config){
14376     
14377     this.parent = false;
14378     
14379     if (typeof(depreciated_tpl) == 'undefined') {
14380         // new way.. - universal constructor.
14381         Roo.apply(this, config);
14382         this.el  = Roo.get(this.el);
14383     } else {
14384         // old format..
14385         this.el  = Roo.get(config);
14386         this.tpl = depreciated_tpl;
14387         Roo.apply(this, depreciated_config);
14388     }
14389     this.wrapEl  = this.el.wrap().wrap();
14390     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14391     
14392     
14393     if(typeof(this.tpl) == "string"){
14394         this.tpl = new Roo.Template(this.tpl);
14395     } else {
14396         // support xtype ctors..
14397         this.tpl = new Roo.factory(this.tpl, Roo);
14398     }
14399     
14400     
14401     this.tpl.compile();
14402     
14403     /** @private */
14404     this.addEvents({
14405         /**
14406          * @event beforeclick
14407          * Fires before a click is processed. Returns false to cancel the default action.
14408          * @param {Roo.View} this
14409          * @param {Number} index The index of the target node
14410          * @param {HTMLElement} node The target node
14411          * @param {Roo.EventObject} e The raw event object
14412          */
14413             "beforeclick" : true,
14414         /**
14415          * @event click
14416          * Fires when a template node is clicked.
14417          * @param {Roo.View} this
14418          * @param {Number} index The index of the target node
14419          * @param {HTMLElement} node The target node
14420          * @param {Roo.EventObject} e The raw event object
14421          */
14422             "click" : true,
14423         /**
14424          * @event dblclick
14425          * Fires when a template node is double clicked.
14426          * @param {Roo.View} this
14427          * @param {Number} index The index of the target node
14428          * @param {HTMLElement} node The target node
14429          * @param {Roo.EventObject} e The raw event object
14430          */
14431             "dblclick" : true,
14432         /**
14433          * @event contextmenu
14434          * Fires when a template node is right clicked.
14435          * @param {Roo.View} this
14436          * @param {Number} index The index of the target node
14437          * @param {HTMLElement} node The target node
14438          * @param {Roo.EventObject} e The raw event object
14439          */
14440             "contextmenu" : true,
14441         /**
14442          * @event selectionchange
14443          * Fires when the selected nodes change.
14444          * @param {Roo.View} this
14445          * @param {Array} selections Array of the selected nodes
14446          */
14447             "selectionchange" : true,
14448     
14449         /**
14450          * @event beforeselect
14451          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14452          * @param {Roo.View} this
14453          * @param {HTMLElement} node The node to be selected
14454          * @param {Array} selections Array of currently selected nodes
14455          */
14456             "beforeselect" : true,
14457         /**
14458          * @event preparedata
14459          * Fires on every row to render, to allow you to change the data.
14460          * @param {Roo.View} this
14461          * @param {Object} data to be rendered (change this)
14462          */
14463           "preparedata" : true
14464           
14465           
14466         });
14467
14468
14469
14470     this.el.on({
14471         "click": this.onClick,
14472         "dblclick": this.onDblClick,
14473         "contextmenu": this.onContextMenu,
14474         scope:this
14475     });
14476
14477     this.selections = [];
14478     this.nodes = [];
14479     this.cmp = new Roo.CompositeElementLite([]);
14480     if(this.store){
14481         this.store = Roo.factory(this.store, Roo.data);
14482         this.setStore(this.store, true);
14483     }
14484     
14485     if ( this.footer && this.footer.xtype) {
14486            
14487          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14488         
14489         this.footer.dataSource = this.store;
14490         this.footer.container = fctr;
14491         this.footer = Roo.factory(this.footer, Roo);
14492         fctr.insertFirst(this.el);
14493         
14494         // this is a bit insane - as the paging toolbar seems to detach the el..
14495 //        dom.parentNode.parentNode.parentNode
14496          // they get detached?
14497     }
14498     
14499     
14500     Roo.View.superclass.constructor.call(this);
14501     
14502     
14503 };
14504
14505 Roo.extend(Roo.View, Roo.util.Observable, {
14506     
14507      /**
14508      * @cfg {Roo.data.Store} store Data store to load data from.
14509      */
14510     store : false,
14511     
14512     /**
14513      * @cfg {String|Roo.Element} el The container element.
14514      */
14515     el : '',
14516     
14517     /**
14518      * @cfg {String|Roo.Template} tpl The template used by this View 
14519      */
14520     tpl : false,
14521     /**
14522      * @cfg {String} dataName the named area of the template to use as the data area
14523      *                          Works with domtemplates roo-name="name"
14524      */
14525     dataName: false,
14526     /**
14527      * @cfg {String} selectedClass The css class to add to selected nodes
14528      */
14529     selectedClass : "x-view-selected",
14530      /**
14531      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14532      */
14533     emptyText : "",
14534     
14535     /**
14536      * @cfg {String} text to display on mask (default Loading)
14537      */
14538     mask : false,
14539     /**
14540      * @cfg {Boolean} multiSelect Allow multiple selection
14541      */
14542     multiSelect : false,
14543     /**
14544      * @cfg {Boolean} singleSelect Allow single selection
14545      */
14546     singleSelect:  false,
14547     
14548     /**
14549      * @cfg {Boolean} toggleSelect - selecting 
14550      */
14551     toggleSelect : false,
14552     
14553     /**
14554      * @cfg {Boolean} tickable - selecting 
14555      */
14556     tickable : false,
14557     
14558     /**
14559      * Returns the element this view is bound to.
14560      * @return {Roo.Element}
14561      */
14562     getEl : function(){
14563         return this.wrapEl;
14564     },
14565     
14566     
14567
14568     /**
14569      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14570      */
14571     refresh : function(){
14572         //Roo.log('refresh');
14573         var t = this.tpl;
14574         
14575         // if we are using something like 'domtemplate', then
14576         // the what gets used is:
14577         // t.applySubtemplate(NAME, data, wrapping data..)
14578         // the outer template then get' applied with
14579         //     the store 'extra data'
14580         // and the body get's added to the
14581         //      roo-name="data" node?
14582         //      <span class='roo-tpl-{name}'></span> ?????
14583         
14584         
14585         
14586         this.clearSelections();
14587         this.el.update("");
14588         var html = [];
14589         var records = this.store.getRange();
14590         if(records.length < 1) {
14591             
14592             // is this valid??  = should it render a template??
14593             
14594             this.el.update(this.emptyText);
14595             return;
14596         }
14597         var el = this.el;
14598         if (this.dataName) {
14599             this.el.update(t.apply(this.store.meta)); //????
14600             el = this.el.child('.roo-tpl-' + this.dataName);
14601         }
14602         
14603         for(var i = 0, len = records.length; i < len; i++){
14604             var data = this.prepareData(records[i].data, i, records[i]);
14605             this.fireEvent("preparedata", this, data, i, records[i]);
14606             
14607             var d = Roo.apply({}, data);
14608             
14609             if(this.tickable){
14610                 Roo.apply(d, {'roo-id' : Roo.id()});
14611                 
14612                 var _this = this;
14613             
14614                 Roo.each(this.parent.item, function(item){
14615                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14616                         return;
14617                     }
14618                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14619                 });
14620             }
14621             
14622             html[html.length] = Roo.util.Format.trim(
14623                 this.dataName ?
14624                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14625                     t.apply(d)
14626             );
14627         }
14628         
14629         
14630         
14631         el.update(html.join(""));
14632         this.nodes = el.dom.childNodes;
14633         this.updateIndexes(0);
14634     },
14635     
14636
14637     /**
14638      * Function to override to reformat the data that is sent to
14639      * the template for each node.
14640      * DEPRICATED - use the preparedata event handler.
14641      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14642      * a JSON object for an UpdateManager bound view).
14643      */
14644     prepareData : function(data, index, record)
14645     {
14646         this.fireEvent("preparedata", this, data, index, record);
14647         return data;
14648     },
14649
14650     onUpdate : function(ds, record){
14651         // Roo.log('on update');   
14652         this.clearSelections();
14653         var index = this.store.indexOf(record);
14654         var n = this.nodes[index];
14655         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14656         n.parentNode.removeChild(n);
14657         this.updateIndexes(index, index);
14658     },
14659
14660     
14661     
14662 // --------- FIXME     
14663     onAdd : function(ds, records, index)
14664     {
14665         //Roo.log(['on Add', ds, records, index] );        
14666         this.clearSelections();
14667         if(this.nodes.length == 0){
14668             this.refresh();
14669             return;
14670         }
14671         var n = this.nodes[index];
14672         for(var i = 0, len = records.length; i < len; i++){
14673             var d = this.prepareData(records[i].data, i, records[i]);
14674             if(n){
14675                 this.tpl.insertBefore(n, d);
14676             }else{
14677                 
14678                 this.tpl.append(this.el, d);
14679             }
14680         }
14681         this.updateIndexes(index);
14682     },
14683
14684     onRemove : function(ds, record, index){
14685        // Roo.log('onRemove');
14686         this.clearSelections();
14687         var el = this.dataName  ?
14688             this.el.child('.roo-tpl-' + this.dataName) :
14689             this.el; 
14690         
14691         el.dom.removeChild(this.nodes[index]);
14692         this.updateIndexes(index);
14693     },
14694
14695     /**
14696      * Refresh an individual node.
14697      * @param {Number} index
14698      */
14699     refreshNode : function(index){
14700         this.onUpdate(this.store, this.store.getAt(index));
14701     },
14702
14703     updateIndexes : function(startIndex, endIndex){
14704         var ns = this.nodes;
14705         startIndex = startIndex || 0;
14706         endIndex = endIndex || ns.length - 1;
14707         for(var i = startIndex; i <= endIndex; i++){
14708             ns[i].nodeIndex = i;
14709         }
14710     },
14711
14712     /**
14713      * Changes the data store this view uses and refresh the view.
14714      * @param {Store} store
14715      */
14716     setStore : function(store, initial){
14717         if(!initial && this.store){
14718             this.store.un("datachanged", this.refresh);
14719             this.store.un("add", this.onAdd);
14720             this.store.un("remove", this.onRemove);
14721             this.store.un("update", this.onUpdate);
14722             this.store.un("clear", this.refresh);
14723             this.store.un("beforeload", this.onBeforeLoad);
14724             this.store.un("load", this.onLoad);
14725             this.store.un("loadexception", this.onLoad);
14726         }
14727         if(store){
14728           
14729             store.on("datachanged", this.refresh, this);
14730             store.on("add", this.onAdd, this);
14731             store.on("remove", this.onRemove, this);
14732             store.on("update", this.onUpdate, this);
14733             store.on("clear", this.refresh, this);
14734             store.on("beforeload", this.onBeforeLoad, this);
14735             store.on("load", this.onLoad, this);
14736             store.on("loadexception", this.onLoad, this);
14737         }
14738         
14739         if(store){
14740             this.refresh();
14741         }
14742     },
14743     /**
14744      * onbeforeLoad - masks the loading area.
14745      *
14746      */
14747     onBeforeLoad : function(store,opts)
14748     {
14749          //Roo.log('onBeforeLoad');   
14750         if (!opts.add) {
14751             this.el.update("");
14752         }
14753         this.el.mask(this.mask ? this.mask : "Loading" ); 
14754     },
14755     onLoad : function ()
14756     {
14757         this.el.unmask();
14758     },
14759     
14760
14761     /**
14762      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14763      * @param {HTMLElement} node
14764      * @return {HTMLElement} The template node
14765      */
14766     findItemFromChild : function(node){
14767         var el = this.dataName  ?
14768             this.el.child('.roo-tpl-' + this.dataName,true) :
14769             this.el.dom; 
14770         
14771         if(!node || node.parentNode == el){
14772                     return node;
14773             }
14774             var p = node.parentNode;
14775             while(p && p != el){
14776             if(p.parentNode == el){
14777                 return p;
14778             }
14779             p = p.parentNode;
14780         }
14781             return null;
14782     },
14783
14784     /** @ignore */
14785     onClick : function(e){
14786         var item = this.findItemFromChild(e.getTarget());
14787         if(item){
14788             var index = this.indexOf(item);
14789             if(this.onItemClick(item, index, e) !== false){
14790                 this.fireEvent("click", this, index, item, e);
14791             }
14792         }else{
14793             this.clearSelections();
14794         }
14795     },
14796
14797     /** @ignore */
14798     onContextMenu : function(e){
14799         var item = this.findItemFromChild(e.getTarget());
14800         if(item){
14801             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14802         }
14803     },
14804
14805     /** @ignore */
14806     onDblClick : function(e){
14807         var item = this.findItemFromChild(e.getTarget());
14808         if(item){
14809             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14810         }
14811     },
14812
14813     onItemClick : function(item, index, e)
14814     {
14815         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14816             return false;
14817         }
14818         if (this.toggleSelect) {
14819             var m = this.isSelected(item) ? 'unselect' : 'select';
14820             //Roo.log(m);
14821             var _t = this;
14822             _t[m](item, true, false);
14823             return true;
14824         }
14825         if(this.multiSelect || this.singleSelect){
14826             if(this.multiSelect && e.shiftKey && this.lastSelection){
14827                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14828             }else{
14829                 this.select(item, this.multiSelect && e.ctrlKey);
14830                 this.lastSelection = item;
14831             }
14832             
14833             if(!this.tickable){
14834                 e.preventDefault();
14835             }
14836             
14837         }
14838         return true;
14839     },
14840
14841     /**
14842      * Get the number of selected nodes.
14843      * @return {Number}
14844      */
14845     getSelectionCount : function(){
14846         return this.selections.length;
14847     },
14848
14849     /**
14850      * Get the currently selected nodes.
14851      * @return {Array} An array of HTMLElements
14852      */
14853     getSelectedNodes : function(){
14854         return this.selections;
14855     },
14856
14857     /**
14858      * Get the indexes of the selected nodes.
14859      * @return {Array}
14860      */
14861     getSelectedIndexes : function(){
14862         var indexes = [], s = this.selections;
14863         for(var i = 0, len = s.length; i < len; i++){
14864             indexes.push(s[i].nodeIndex);
14865         }
14866         return indexes;
14867     },
14868
14869     /**
14870      * Clear all selections
14871      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14872      */
14873     clearSelections : function(suppressEvent){
14874         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14875             this.cmp.elements = this.selections;
14876             this.cmp.removeClass(this.selectedClass);
14877             this.selections = [];
14878             if(!suppressEvent){
14879                 this.fireEvent("selectionchange", this, this.selections);
14880             }
14881         }
14882     },
14883
14884     /**
14885      * Returns true if the passed node is selected
14886      * @param {HTMLElement/Number} node The node or node index
14887      * @return {Boolean}
14888      */
14889     isSelected : function(node){
14890         var s = this.selections;
14891         if(s.length < 1){
14892             return false;
14893         }
14894         node = this.getNode(node);
14895         return s.indexOf(node) !== -1;
14896     },
14897
14898     /**
14899      * Selects nodes.
14900      * @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
14901      * @param {Boolean} keepExisting (optional) true to keep existing selections
14902      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14903      */
14904     select : function(nodeInfo, keepExisting, suppressEvent){
14905         if(nodeInfo instanceof Array){
14906             if(!keepExisting){
14907                 this.clearSelections(true);
14908             }
14909             for(var i = 0, len = nodeInfo.length; i < len; i++){
14910                 this.select(nodeInfo[i], true, true);
14911             }
14912             return;
14913         } 
14914         var node = this.getNode(nodeInfo);
14915         if(!node || this.isSelected(node)){
14916             return; // already selected.
14917         }
14918         if(!keepExisting){
14919             this.clearSelections(true);
14920         }
14921         
14922         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14923             Roo.fly(node).addClass(this.selectedClass);
14924             this.selections.push(node);
14925             if(!suppressEvent){
14926                 this.fireEvent("selectionchange", this, this.selections);
14927             }
14928         }
14929         
14930         
14931     },
14932       /**
14933      * Unselects nodes.
14934      * @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
14935      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14936      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14937      */
14938     unselect : function(nodeInfo, keepExisting, suppressEvent)
14939     {
14940         if(nodeInfo instanceof Array){
14941             Roo.each(this.selections, function(s) {
14942                 this.unselect(s, nodeInfo);
14943             }, this);
14944             return;
14945         }
14946         var node = this.getNode(nodeInfo);
14947         if(!node || !this.isSelected(node)){
14948             //Roo.log("not selected");
14949             return; // not selected.
14950         }
14951         // fireevent???
14952         var ns = [];
14953         Roo.each(this.selections, function(s) {
14954             if (s == node ) {
14955                 Roo.fly(node).removeClass(this.selectedClass);
14956
14957                 return;
14958             }
14959             ns.push(s);
14960         },this);
14961         
14962         this.selections= ns;
14963         this.fireEvent("selectionchange", this, this.selections);
14964     },
14965
14966     /**
14967      * Gets a template node.
14968      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14969      * @return {HTMLElement} The node or null if it wasn't found
14970      */
14971     getNode : function(nodeInfo){
14972         if(typeof nodeInfo == "string"){
14973             return document.getElementById(nodeInfo);
14974         }else if(typeof nodeInfo == "number"){
14975             return this.nodes[nodeInfo];
14976         }
14977         return nodeInfo;
14978     },
14979
14980     /**
14981      * Gets a range template nodes.
14982      * @param {Number} startIndex
14983      * @param {Number} endIndex
14984      * @return {Array} An array of nodes
14985      */
14986     getNodes : function(start, end){
14987         var ns = this.nodes;
14988         start = start || 0;
14989         end = typeof end == "undefined" ? ns.length - 1 : end;
14990         var nodes = [];
14991         if(start <= end){
14992             for(var i = start; i <= end; i++){
14993                 nodes.push(ns[i]);
14994             }
14995         } else{
14996             for(var i = start; i >= end; i--){
14997                 nodes.push(ns[i]);
14998             }
14999         }
15000         return nodes;
15001     },
15002
15003     /**
15004      * Finds the index of the passed node
15005      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15006      * @return {Number} The index of the node or -1
15007      */
15008     indexOf : function(node){
15009         node = this.getNode(node);
15010         if(typeof node.nodeIndex == "number"){
15011             return node.nodeIndex;
15012         }
15013         var ns = this.nodes;
15014         for(var i = 0, len = ns.length; i < len; i++){
15015             if(ns[i] == node){
15016                 return i;
15017             }
15018         }
15019         return -1;
15020     }
15021 });
15022 /*
15023  * - LGPL
15024  *
15025  * based on jquery fullcalendar
15026  * 
15027  */
15028
15029 Roo.bootstrap = Roo.bootstrap || {};
15030 /**
15031  * @class Roo.bootstrap.Calendar
15032  * @extends Roo.bootstrap.Component
15033  * Bootstrap Calendar class
15034  * @cfg {Boolean} loadMask (true|false) default false
15035  * @cfg {Object} header generate the user specific header of the calendar, default false
15036
15037  * @constructor
15038  * Create a new Container
15039  * @param {Object} config The config object
15040  */
15041
15042
15043
15044 Roo.bootstrap.Calendar = function(config){
15045     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15046      this.addEvents({
15047         /**
15048              * @event select
15049              * Fires when a date is selected
15050              * @param {DatePicker} this
15051              * @param {Date} date The selected date
15052              */
15053         'select': true,
15054         /**
15055              * @event monthchange
15056              * Fires when the displayed month changes 
15057              * @param {DatePicker} this
15058              * @param {Date} date The selected month
15059              */
15060         'monthchange': true,
15061         /**
15062              * @event evententer
15063              * Fires when mouse over an event
15064              * @param {Calendar} this
15065              * @param {event} Event
15066              */
15067         'evententer': true,
15068         /**
15069              * @event eventleave
15070              * Fires when the mouse leaves an
15071              * @param {Calendar} this
15072              * @param {event}
15073              */
15074         'eventleave': true,
15075         /**
15076              * @event eventclick
15077              * Fires when the mouse click an
15078              * @param {Calendar} this
15079              * @param {event}
15080              */
15081         'eventclick': true
15082         
15083     });
15084
15085 };
15086
15087 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15088     
15089      /**
15090      * @cfg {Number} startDay
15091      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15092      */
15093     startDay : 0,
15094     
15095     loadMask : false,
15096     
15097     header : false,
15098       
15099     getAutoCreate : function(){
15100         
15101         
15102         var fc_button = function(name, corner, style, content ) {
15103             return Roo.apply({},{
15104                 tag : 'span',
15105                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15106                          (corner.length ?
15107                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15108                             ''
15109                         ),
15110                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15111                 unselectable: 'on'
15112             });
15113         };
15114         
15115         var header = {};
15116         
15117         if(!this.header){
15118             header = {
15119                 tag : 'table',
15120                 cls : 'fc-header',
15121                 style : 'width:100%',
15122                 cn : [
15123                     {
15124                         tag: 'tr',
15125                         cn : [
15126                             {
15127                                 tag : 'td',
15128                                 cls : 'fc-header-left',
15129                                 cn : [
15130                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15131                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15132                                     { tag: 'span', cls: 'fc-header-space' },
15133                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15134
15135
15136                                 ]
15137                             },
15138
15139                             {
15140                                 tag : 'td',
15141                                 cls : 'fc-header-center',
15142                                 cn : [
15143                                     {
15144                                         tag: 'span',
15145                                         cls: 'fc-header-title',
15146                                         cn : {
15147                                             tag: 'H2',
15148                                             html : 'month / year'
15149                                         }
15150                                     }
15151
15152                                 ]
15153                             },
15154                             {
15155                                 tag : 'td',
15156                                 cls : 'fc-header-right',
15157                                 cn : [
15158                               /*      fc_button('month', 'left', '', 'month' ),
15159                                     fc_button('week', '', '', 'week' ),
15160                                     fc_button('day', 'right', '', 'day' )
15161                                 */    
15162
15163                                 ]
15164                             }
15165
15166                         ]
15167                     }
15168                 ]
15169             };
15170         }
15171         
15172         header = this.header;
15173         
15174        
15175         var cal_heads = function() {
15176             var ret = [];
15177             // fixme - handle this.
15178             
15179             for (var i =0; i < Date.dayNames.length; i++) {
15180                 var d = Date.dayNames[i];
15181                 ret.push({
15182                     tag: 'th',
15183                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15184                     html : d.substring(0,3)
15185                 });
15186                 
15187             }
15188             ret[0].cls += ' fc-first';
15189             ret[6].cls += ' fc-last';
15190             return ret;
15191         };
15192         var cal_cell = function(n) {
15193             return  {
15194                 tag: 'td',
15195                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15196                 cn : [
15197                     {
15198                         cn : [
15199                             {
15200                                 cls: 'fc-day-number',
15201                                 html: 'D'
15202                             },
15203                             {
15204                                 cls: 'fc-day-content',
15205                              
15206                                 cn : [
15207                                      {
15208                                         style: 'position: relative;' // height: 17px;
15209                                     }
15210                                 ]
15211                             }
15212                             
15213                             
15214                         ]
15215                     }
15216                 ]
15217                 
15218             }
15219         };
15220         var cal_rows = function() {
15221             
15222             var ret = [];
15223             for (var r = 0; r < 6; r++) {
15224                 var row= {
15225                     tag : 'tr',
15226                     cls : 'fc-week',
15227                     cn : []
15228                 };
15229                 
15230                 for (var i =0; i < Date.dayNames.length; i++) {
15231                     var d = Date.dayNames[i];
15232                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15233
15234                 }
15235                 row.cn[0].cls+=' fc-first';
15236                 row.cn[0].cn[0].style = 'min-height:90px';
15237                 row.cn[6].cls+=' fc-last';
15238                 ret.push(row);
15239                 
15240             }
15241             ret[0].cls += ' fc-first';
15242             ret[4].cls += ' fc-prev-last';
15243             ret[5].cls += ' fc-last';
15244             return ret;
15245             
15246         };
15247         
15248         var cal_table = {
15249             tag: 'table',
15250             cls: 'fc-border-separate',
15251             style : 'width:100%',
15252             cellspacing  : 0,
15253             cn : [
15254                 { 
15255                     tag: 'thead',
15256                     cn : [
15257                         { 
15258                             tag: 'tr',
15259                             cls : 'fc-first fc-last',
15260                             cn : cal_heads()
15261                         }
15262                     ]
15263                 },
15264                 { 
15265                     tag: 'tbody',
15266                     cn : cal_rows()
15267                 }
15268                   
15269             ]
15270         };
15271          
15272          var cfg = {
15273             cls : 'fc fc-ltr',
15274             cn : [
15275                 header,
15276                 {
15277                     cls : 'fc-content',
15278                     style : "position: relative;",
15279                     cn : [
15280                         {
15281                             cls : 'fc-view fc-view-month fc-grid',
15282                             style : 'position: relative',
15283                             unselectable : 'on',
15284                             cn : [
15285                                 {
15286                                     cls : 'fc-event-container',
15287                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15288                                 },
15289                                 cal_table
15290                             ]
15291                         }
15292                     ]
15293     
15294                 }
15295            ] 
15296             
15297         };
15298         
15299          
15300         
15301         return cfg;
15302     },
15303     
15304     
15305     initEvents : function()
15306     {
15307         if(!this.store){
15308             throw "can not find store for calendar";
15309         }
15310         
15311         var mark = {
15312             tag: "div",
15313             cls:"x-dlg-mask",
15314             style: "text-align:center",
15315             cn: [
15316                 {
15317                     tag: "div",
15318                     style: "background-color:white;width:50%;margin:250 auto",
15319                     cn: [
15320                         {
15321                             tag: "img",
15322                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15323                         },
15324                         {
15325                             tag: "span",
15326                             html: "Loading"
15327                         }
15328                         
15329                     ]
15330                 }
15331             ]
15332         };
15333         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15334         
15335         var size = this.el.select('.fc-content', true).first().getSize();
15336         this.maskEl.setSize(size.width, size.height);
15337         this.maskEl.enableDisplayMode("block");
15338         if(!this.loadMask){
15339             this.maskEl.hide();
15340         }
15341         
15342         this.store = Roo.factory(this.store, Roo.data);
15343         this.store.on('load', this.onLoad, this);
15344         this.store.on('beforeload', this.onBeforeLoad, this);
15345         
15346         this.resize();
15347         
15348         this.cells = this.el.select('.fc-day',true);
15349         //Roo.log(this.cells);
15350         this.textNodes = this.el.query('.fc-day-number');
15351         this.cells.addClassOnOver('fc-state-hover');
15352         
15353         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15354         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15355         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15356         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15357         
15358         this.on('monthchange', this.onMonthChange, this);
15359         
15360         this.update(new Date().clearTime());
15361     },
15362     
15363     resize : function() {
15364         var sz  = this.el.getSize();
15365         
15366         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15367         this.el.select('.fc-day-content div',true).setHeight(34);
15368     },
15369     
15370     
15371     // private
15372     showPrevMonth : function(e){
15373         this.update(this.activeDate.add("mo", -1));
15374     },
15375     showToday : function(e){
15376         this.update(new Date().clearTime());
15377     },
15378     // private
15379     showNextMonth : function(e){
15380         this.update(this.activeDate.add("mo", 1));
15381     },
15382
15383     // private
15384     showPrevYear : function(){
15385         this.update(this.activeDate.add("y", -1));
15386     },
15387
15388     // private
15389     showNextYear : function(){
15390         this.update(this.activeDate.add("y", 1));
15391     },
15392
15393     
15394    // private
15395     update : function(date)
15396     {
15397         var vd = this.activeDate;
15398         this.activeDate = date;
15399 //        if(vd && this.el){
15400 //            var t = date.getTime();
15401 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15402 //                Roo.log('using add remove');
15403 //                
15404 //                this.fireEvent('monthchange', this, date);
15405 //                
15406 //                this.cells.removeClass("fc-state-highlight");
15407 //                this.cells.each(function(c){
15408 //                   if(c.dateValue == t){
15409 //                       c.addClass("fc-state-highlight");
15410 //                       setTimeout(function(){
15411 //                            try{c.dom.firstChild.focus();}catch(e){}
15412 //                       }, 50);
15413 //                       return false;
15414 //                   }
15415 //                   return true;
15416 //                });
15417 //                return;
15418 //            }
15419 //        }
15420         
15421         var days = date.getDaysInMonth();
15422         
15423         var firstOfMonth = date.getFirstDateOfMonth();
15424         var startingPos = firstOfMonth.getDay()-this.startDay;
15425         
15426         if(startingPos < this.startDay){
15427             startingPos += 7;
15428         }
15429         
15430         var pm = date.add(Date.MONTH, -1);
15431         var prevStart = pm.getDaysInMonth()-startingPos;
15432 //        
15433         this.cells = this.el.select('.fc-day',true);
15434         this.textNodes = this.el.query('.fc-day-number');
15435         this.cells.addClassOnOver('fc-state-hover');
15436         
15437         var cells = this.cells.elements;
15438         var textEls = this.textNodes;
15439         
15440         Roo.each(cells, function(cell){
15441             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15442         });
15443         
15444         days += startingPos;
15445
15446         // convert everything to numbers so it's fast
15447         var day = 86400000;
15448         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15449         //Roo.log(d);
15450         //Roo.log(pm);
15451         //Roo.log(prevStart);
15452         
15453         var today = new Date().clearTime().getTime();
15454         var sel = date.clearTime().getTime();
15455         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15456         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15457         var ddMatch = this.disabledDatesRE;
15458         var ddText = this.disabledDatesText;
15459         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15460         var ddaysText = this.disabledDaysText;
15461         var format = this.format;
15462         
15463         var setCellClass = function(cal, cell){
15464             cell.row = 0;
15465             cell.events = [];
15466             cell.more = [];
15467             //Roo.log('set Cell Class');
15468             cell.title = "";
15469             var t = d.getTime();
15470             
15471             //Roo.log(d);
15472             
15473             cell.dateValue = t;
15474             if(t == today){
15475                 cell.className += " fc-today";
15476                 cell.className += " fc-state-highlight";
15477                 cell.title = cal.todayText;
15478             }
15479             if(t == sel){
15480                 // disable highlight in other month..
15481                 //cell.className += " fc-state-highlight";
15482                 
15483             }
15484             // disabling
15485             if(t < min) {
15486                 cell.className = " fc-state-disabled";
15487                 cell.title = cal.minText;
15488                 return;
15489             }
15490             if(t > max) {
15491                 cell.className = " fc-state-disabled";
15492                 cell.title = cal.maxText;
15493                 return;
15494             }
15495             if(ddays){
15496                 if(ddays.indexOf(d.getDay()) != -1){
15497                     cell.title = ddaysText;
15498                     cell.className = " fc-state-disabled";
15499                 }
15500             }
15501             if(ddMatch && format){
15502                 var fvalue = d.dateFormat(format);
15503                 if(ddMatch.test(fvalue)){
15504                     cell.title = ddText.replace("%0", fvalue);
15505                     cell.className = " fc-state-disabled";
15506                 }
15507             }
15508             
15509             if (!cell.initialClassName) {
15510                 cell.initialClassName = cell.dom.className;
15511             }
15512             
15513             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15514         };
15515
15516         var i = 0;
15517         
15518         for(; i < startingPos; i++) {
15519             textEls[i].innerHTML = (++prevStart);
15520             d.setDate(d.getDate()+1);
15521             
15522             cells[i].className = "fc-past fc-other-month";
15523             setCellClass(this, cells[i]);
15524         }
15525         
15526         var intDay = 0;
15527         
15528         for(; i < days; i++){
15529             intDay = i - startingPos + 1;
15530             textEls[i].innerHTML = (intDay);
15531             d.setDate(d.getDate()+1);
15532             
15533             cells[i].className = ''; // "x-date-active";
15534             setCellClass(this, cells[i]);
15535         }
15536         var extraDays = 0;
15537         
15538         for(; i < 42; i++) {
15539             textEls[i].innerHTML = (++extraDays);
15540             d.setDate(d.getDate()+1);
15541             
15542             cells[i].className = "fc-future fc-other-month";
15543             setCellClass(this, cells[i]);
15544         }
15545         
15546         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15547         
15548         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15549         
15550         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15551         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15552         
15553         if(totalRows != 6){
15554             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15555             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15556         }
15557         
15558         this.fireEvent('monthchange', this, date);
15559         
15560         
15561         /*
15562         if(!this.internalRender){
15563             var main = this.el.dom.firstChild;
15564             var w = main.offsetWidth;
15565             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15566             Roo.fly(main).setWidth(w);
15567             this.internalRender = true;
15568             // opera does not respect the auto grow header center column
15569             // then, after it gets a width opera refuses to recalculate
15570             // without a second pass
15571             if(Roo.isOpera && !this.secondPass){
15572                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15573                 this.secondPass = true;
15574                 this.update.defer(10, this, [date]);
15575             }
15576         }
15577         */
15578         
15579     },
15580     
15581     findCell : function(dt) {
15582         dt = dt.clearTime().getTime();
15583         var ret = false;
15584         this.cells.each(function(c){
15585             //Roo.log("check " +c.dateValue + '?=' + dt);
15586             if(c.dateValue == dt){
15587                 ret = c;
15588                 return false;
15589             }
15590             return true;
15591         });
15592         
15593         return ret;
15594     },
15595     
15596     findCells : function(ev) {
15597         var s = ev.start.clone().clearTime().getTime();
15598        // Roo.log(s);
15599         var e= ev.end.clone().clearTime().getTime();
15600        // Roo.log(e);
15601         var ret = [];
15602         this.cells.each(function(c){
15603              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15604             
15605             if(c.dateValue > e){
15606                 return ;
15607             }
15608             if(c.dateValue < s){
15609                 return ;
15610             }
15611             ret.push(c);
15612         });
15613         
15614         return ret;    
15615     },
15616     
15617 //    findBestRow: function(cells)
15618 //    {
15619 //        var ret = 0;
15620 //        
15621 //        for (var i =0 ; i < cells.length;i++) {
15622 //            ret  = Math.max(cells[i].rows || 0,ret);
15623 //        }
15624 //        return ret;
15625 //        
15626 //    },
15627     
15628     
15629     addItem : function(ev)
15630     {
15631         // look for vertical location slot in
15632         var cells = this.findCells(ev);
15633         
15634 //        ev.row = this.findBestRow(cells);
15635         
15636         // work out the location.
15637         
15638         var crow = false;
15639         var rows = [];
15640         for(var i =0; i < cells.length; i++) {
15641             
15642             cells[i].row = cells[0].row;
15643             
15644             if(i == 0){
15645                 cells[i].row = cells[i].row + 1;
15646             }
15647             
15648             if (!crow) {
15649                 crow = {
15650                     start : cells[i],
15651                     end :  cells[i]
15652                 };
15653                 continue;
15654             }
15655             if (crow.start.getY() == cells[i].getY()) {
15656                 // on same row.
15657                 crow.end = cells[i];
15658                 continue;
15659             }
15660             // different row.
15661             rows.push(crow);
15662             crow = {
15663                 start: cells[i],
15664                 end : cells[i]
15665             };
15666             
15667         }
15668         
15669         rows.push(crow);
15670         ev.els = [];
15671         ev.rows = rows;
15672         ev.cells = cells;
15673         
15674         cells[0].events.push(ev);
15675         
15676         this.calevents.push(ev);
15677     },
15678     
15679     clearEvents: function() {
15680         
15681         if(!this.calevents){
15682             return;
15683         }
15684         
15685         Roo.each(this.cells.elements, function(c){
15686             c.row = 0;
15687             c.events = [];
15688             c.more = [];
15689         });
15690         
15691         Roo.each(this.calevents, function(e) {
15692             Roo.each(e.els, function(el) {
15693                 el.un('mouseenter' ,this.onEventEnter, this);
15694                 el.un('mouseleave' ,this.onEventLeave, this);
15695                 el.remove();
15696             },this);
15697         },this);
15698         
15699         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15700             e.remove();
15701         });
15702         
15703     },
15704     
15705     renderEvents: function()
15706     {   
15707         var _this = this;
15708         
15709         this.cells.each(function(c) {
15710             
15711             if(c.row < 5){
15712                 return;
15713             }
15714             
15715             var ev = c.events;
15716             
15717             var r = 4;
15718             if(c.row != c.events.length){
15719                 r = 4 - (4 - (c.row - c.events.length));
15720             }
15721             
15722             c.events = ev.slice(0, r);
15723             c.more = ev.slice(r);
15724             
15725             if(c.more.length && c.more.length == 1){
15726                 c.events.push(c.more.pop());
15727             }
15728             
15729             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15730             
15731         });
15732             
15733         this.cells.each(function(c) {
15734             
15735             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15736             
15737             
15738             for (var e = 0; e < c.events.length; e++){
15739                 var ev = c.events[e];
15740                 var rows = ev.rows;
15741                 
15742                 for(var i = 0; i < rows.length; i++) {
15743                 
15744                     // how many rows should it span..
15745
15746                     var  cfg = {
15747                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15748                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15749
15750                         unselectable : "on",
15751                         cn : [
15752                             {
15753                                 cls: 'fc-event-inner',
15754                                 cn : [
15755     //                                {
15756     //                                  tag:'span',
15757     //                                  cls: 'fc-event-time',
15758     //                                  html : cells.length > 1 ? '' : ev.time
15759     //                                },
15760                                     {
15761                                       tag:'span',
15762                                       cls: 'fc-event-title',
15763                                       html : String.format('{0}', ev.title)
15764                                     }
15765
15766
15767                                 ]
15768                             },
15769                             {
15770                                 cls: 'ui-resizable-handle ui-resizable-e',
15771                                 html : '&nbsp;&nbsp;&nbsp'
15772                             }
15773
15774                         ]
15775                     };
15776
15777                     if (i == 0) {
15778                         cfg.cls += ' fc-event-start';
15779                     }
15780                     if ((i+1) == rows.length) {
15781                         cfg.cls += ' fc-event-end';
15782                     }
15783
15784                     var ctr = _this.el.select('.fc-event-container',true).first();
15785                     var cg = ctr.createChild(cfg);
15786
15787                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15788                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15789
15790                     var r = (c.more.length) ? 1 : 0;
15791                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15792                     cg.setWidth(ebox.right - sbox.x -2);
15793
15794                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15795                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15796                     cg.on('click', _this.onEventClick, _this, ev);
15797
15798                     ev.els.push(cg);
15799                     
15800                 }
15801                 
15802             }
15803             
15804             
15805             if(c.more.length){
15806                 var  cfg = {
15807                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15808                     style : 'position: absolute',
15809                     unselectable : "on",
15810                     cn : [
15811                         {
15812                             cls: 'fc-event-inner',
15813                             cn : [
15814                                 {
15815                                   tag:'span',
15816                                   cls: 'fc-event-title',
15817                                   html : 'More'
15818                                 }
15819
15820
15821                             ]
15822                         },
15823                         {
15824                             cls: 'ui-resizable-handle ui-resizable-e',
15825                             html : '&nbsp;&nbsp;&nbsp'
15826                         }
15827
15828                     ]
15829                 };
15830
15831                 var ctr = _this.el.select('.fc-event-container',true).first();
15832                 var cg = ctr.createChild(cfg);
15833
15834                 var sbox = c.select('.fc-day-content',true).first().getBox();
15835                 var ebox = c.select('.fc-day-content',true).first().getBox();
15836                 //Roo.log(cg);
15837                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15838                 cg.setWidth(ebox.right - sbox.x -2);
15839
15840                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15841                 
15842             }
15843             
15844         });
15845         
15846         
15847         
15848     },
15849     
15850     onEventEnter: function (e, el,event,d) {
15851         this.fireEvent('evententer', this, el, event);
15852     },
15853     
15854     onEventLeave: function (e, el,event,d) {
15855         this.fireEvent('eventleave', this, el, event);
15856     },
15857     
15858     onEventClick: function (e, el,event,d) {
15859         this.fireEvent('eventclick', this, el, event);
15860     },
15861     
15862     onMonthChange: function () {
15863         this.store.load();
15864     },
15865     
15866     onMoreEventClick: function(e, el, more)
15867     {
15868         var _this = this;
15869         
15870         this.calpopover.placement = 'right';
15871         this.calpopover.setTitle('More');
15872         
15873         this.calpopover.setContent('');
15874         
15875         var ctr = this.calpopover.el.select('.popover-content', true).first();
15876         
15877         Roo.each(more, function(m){
15878             var cfg = {
15879                 cls : 'fc-event-hori fc-event-draggable',
15880                 html : m.title
15881             };
15882             var cg = ctr.createChild(cfg);
15883             
15884             cg.on('click', _this.onEventClick, _this, m);
15885         });
15886         
15887         this.calpopover.show(el);
15888         
15889         
15890     },
15891     
15892     onLoad: function () 
15893     {   
15894         this.calevents = [];
15895         var cal = this;
15896         
15897         if(this.store.getCount() > 0){
15898             this.store.data.each(function(d){
15899                cal.addItem({
15900                     id : d.data.id,
15901                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15902                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15903                     time : d.data.start_time,
15904                     title : d.data.title,
15905                     description : d.data.description,
15906                     venue : d.data.venue
15907                 });
15908             });
15909         }
15910         
15911         this.renderEvents();
15912         
15913         if(this.calevents.length && this.loadMask){
15914             this.maskEl.hide();
15915         }
15916     },
15917     
15918     onBeforeLoad: function()
15919     {
15920         this.clearEvents();
15921         if(this.loadMask){
15922             this.maskEl.show();
15923         }
15924     }
15925 });
15926
15927  
15928  /*
15929  * - LGPL
15930  *
15931  * element
15932  * 
15933  */
15934
15935 /**
15936  * @class Roo.bootstrap.Popover
15937  * @extends Roo.bootstrap.Component
15938  * Bootstrap Popover class
15939  * @cfg {String} html contents of the popover   (or false to use children..)
15940  * @cfg {String} title of popover (or false to hide)
15941  * @cfg {String} placement how it is placed
15942  * @cfg {String} trigger click || hover (or false to trigger manually)
15943  * @cfg {String} over what (parent or false to trigger manually.)
15944  * @cfg {Number} delay - delay before showing
15945  
15946  * @constructor
15947  * Create a new Popover
15948  * @param {Object} config The config object
15949  */
15950
15951 Roo.bootstrap.Popover = function(config){
15952     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15953     
15954     this.addEvents({
15955         // raw events
15956          /**
15957          * @event show
15958          * After the popover show
15959          * 
15960          * @param {Roo.bootstrap.Popover} this
15961          */
15962         "show" : true,
15963         /**
15964          * @event hide
15965          * After the popover hide
15966          * 
15967          * @param {Roo.bootstrap.Popover} this
15968          */
15969         "hide" : true
15970     });
15971 };
15972
15973 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15974     
15975     title: 'Fill in a title',
15976     html: false,
15977     
15978     placement : 'right',
15979     trigger : 'hover', // hover
15980     
15981     delay : 0,
15982     
15983     over: 'parent',
15984     
15985     can_build_overlaid : false,
15986     
15987     getChildContainer : function()
15988     {
15989         return this.el.select('.popover-content',true).first();
15990     },
15991     
15992     getAutoCreate : function(){
15993          
15994         var cfg = {
15995            cls : 'popover roo-dynamic',
15996            style: 'display:block',
15997            cn : [
15998                 {
15999                     cls : 'arrow'
16000                 },
16001                 {
16002                     cls : 'popover-inner',
16003                     cn : [
16004                         {
16005                             tag: 'h3',
16006                             cls: 'popover-title',
16007                             html : this.title
16008                         },
16009                         {
16010                             cls : 'popover-content',
16011                             html : this.html
16012                         }
16013                     ]
16014                     
16015                 }
16016            ]
16017         };
16018         
16019         return cfg;
16020     },
16021     setTitle: function(str)
16022     {
16023         this.title = str;
16024         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16025     },
16026     setContent: function(str)
16027     {
16028         this.html = str;
16029         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16030     },
16031     // as it get's added to the bottom of the page.
16032     onRender : function(ct, position)
16033     {
16034         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16035         if(!this.el){
16036             var cfg = Roo.apply({},  this.getAutoCreate());
16037             cfg.id = Roo.id();
16038             
16039             if (this.cls) {
16040                 cfg.cls += ' ' + this.cls;
16041             }
16042             if (this.style) {
16043                 cfg.style = this.style;
16044             }
16045             //Roo.log("adding to ");
16046             this.el = Roo.get(document.body).createChild(cfg, position);
16047 //            Roo.log(this.el);
16048         }
16049         this.initEvents();
16050     },
16051     
16052     initEvents : function()
16053     {
16054         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16055         this.el.enableDisplayMode('block');
16056         this.el.hide();
16057         if (this.over === false) {
16058             return; 
16059         }
16060         if (this.triggers === false) {
16061             return;
16062         }
16063         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16064         var triggers = this.trigger ? this.trigger.split(' ') : [];
16065         Roo.each(triggers, function(trigger) {
16066         
16067             if (trigger == 'click') {
16068                 on_el.on('click', this.toggle, this);
16069             } else if (trigger != 'manual') {
16070                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16071                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16072       
16073                 on_el.on(eventIn  ,this.enter, this);
16074                 on_el.on(eventOut, this.leave, this);
16075             }
16076         }, this);
16077         
16078     },
16079     
16080     
16081     // private
16082     timeout : null,
16083     hoverState : null,
16084     
16085     toggle : function () {
16086         this.hoverState == 'in' ? this.leave() : this.enter();
16087     },
16088     
16089     enter : function () {
16090         
16091         clearTimeout(this.timeout);
16092     
16093         this.hoverState = 'in';
16094     
16095         if (!this.delay || !this.delay.show) {
16096             this.show();
16097             return;
16098         }
16099         var _t = this;
16100         this.timeout = setTimeout(function () {
16101             if (_t.hoverState == 'in') {
16102                 _t.show();
16103             }
16104         }, this.delay.show)
16105     },
16106     
16107     leave : function() {
16108         clearTimeout(this.timeout);
16109     
16110         this.hoverState = 'out';
16111     
16112         if (!this.delay || !this.delay.hide) {
16113             this.hide();
16114             return;
16115         }
16116         var _t = this;
16117         this.timeout = setTimeout(function () {
16118             if (_t.hoverState == 'out') {
16119                 _t.hide();
16120             }
16121         }, this.delay.hide)
16122     },
16123     
16124     show : function (on_el)
16125     {
16126         if (!on_el) {
16127             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16128         }
16129         
16130         // set content.
16131         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16132         if (this.html !== false) {
16133             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16134         }
16135         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16136         if (!this.title.length) {
16137             this.el.select('.popover-title',true).hide();
16138         }
16139         
16140         var placement = typeof this.placement == 'function' ?
16141             this.placement.call(this, this.el, on_el) :
16142             this.placement;
16143             
16144         var autoToken = /\s?auto?\s?/i;
16145         var autoPlace = autoToken.test(placement);
16146         if (autoPlace) {
16147             placement = placement.replace(autoToken, '') || 'top';
16148         }
16149         
16150         //this.el.detach()
16151         //this.el.setXY([0,0]);
16152         this.el.show();
16153         this.el.dom.style.display='block';
16154         this.el.addClass(placement);
16155         
16156         //this.el.appendTo(on_el);
16157         
16158         var p = this.getPosition();
16159         var box = this.el.getBox();
16160         
16161         if (autoPlace) {
16162             // fixme..
16163         }
16164         var align = Roo.bootstrap.Popover.alignment[placement];
16165         this.el.alignTo(on_el, align[0],align[1]);
16166         //var arrow = this.el.select('.arrow',true).first();
16167         //arrow.set(align[2], 
16168         
16169         this.el.addClass('in');
16170         
16171         
16172         if (this.el.hasClass('fade')) {
16173             // fade it?
16174         }
16175         
16176         this.hoverState = 'in';
16177         
16178         this.fireEvent('show', this);
16179         
16180     },
16181     hide : function()
16182     {
16183         this.el.setXY([0,0]);
16184         this.el.removeClass('in');
16185         this.el.hide();
16186         this.hoverState = null;
16187         
16188         this.fireEvent('hide', this);
16189     }
16190     
16191 });
16192
16193 Roo.bootstrap.Popover.alignment = {
16194     'left' : ['r-l', [-10,0], 'right'],
16195     'right' : ['l-r', [10,0], 'left'],
16196     'bottom' : ['t-b', [0,10], 'top'],
16197     'top' : [ 'b-t', [0,-10], 'bottom']
16198 };
16199
16200  /*
16201  * - LGPL
16202  *
16203  * Progress
16204  * 
16205  */
16206
16207 /**
16208  * @class Roo.bootstrap.Progress
16209  * @extends Roo.bootstrap.Component
16210  * Bootstrap Progress class
16211  * @cfg {Boolean} striped striped of the progress bar
16212  * @cfg {Boolean} active animated of the progress bar
16213  * 
16214  * 
16215  * @constructor
16216  * Create a new Progress
16217  * @param {Object} config The config object
16218  */
16219
16220 Roo.bootstrap.Progress = function(config){
16221     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16222 };
16223
16224 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16225     
16226     striped : false,
16227     active: false,
16228     
16229     getAutoCreate : function(){
16230         var cfg = {
16231             tag: 'div',
16232             cls: 'progress'
16233         };
16234         
16235         
16236         if(this.striped){
16237             cfg.cls += ' progress-striped';
16238         }
16239       
16240         if(this.active){
16241             cfg.cls += ' active';
16242         }
16243         
16244         
16245         return cfg;
16246     }
16247    
16248 });
16249
16250  
16251
16252  /*
16253  * - LGPL
16254  *
16255  * ProgressBar
16256  * 
16257  */
16258
16259 /**
16260  * @class Roo.bootstrap.ProgressBar
16261  * @extends Roo.bootstrap.Component
16262  * Bootstrap ProgressBar class
16263  * @cfg {Number} aria_valuenow aria-value now
16264  * @cfg {Number} aria_valuemin aria-value min
16265  * @cfg {Number} aria_valuemax aria-value max
16266  * @cfg {String} label label for the progress bar
16267  * @cfg {String} panel (success | info | warning | danger )
16268  * @cfg {String} role role of the progress bar
16269  * @cfg {String} sr_only text
16270  * 
16271  * 
16272  * @constructor
16273  * Create a new ProgressBar
16274  * @param {Object} config The config object
16275  */
16276
16277 Roo.bootstrap.ProgressBar = function(config){
16278     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16279 };
16280
16281 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16282     
16283     aria_valuenow : 0,
16284     aria_valuemin : 0,
16285     aria_valuemax : 100,
16286     label : false,
16287     panel : false,
16288     role : false,
16289     sr_only: false,
16290     
16291     getAutoCreate : function()
16292     {
16293         
16294         var cfg = {
16295             tag: 'div',
16296             cls: 'progress-bar',
16297             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16298         };
16299         
16300         if(this.sr_only){
16301             cfg.cn = {
16302                 tag: 'span',
16303                 cls: 'sr-only',
16304                 html: this.sr_only
16305             }
16306         }
16307         
16308         if(this.role){
16309             cfg.role = this.role;
16310         }
16311         
16312         if(this.aria_valuenow){
16313             cfg['aria-valuenow'] = this.aria_valuenow;
16314         }
16315         
16316         if(this.aria_valuemin){
16317             cfg['aria-valuemin'] = this.aria_valuemin;
16318         }
16319         
16320         if(this.aria_valuemax){
16321             cfg['aria-valuemax'] = this.aria_valuemax;
16322         }
16323         
16324         if(this.label && !this.sr_only){
16325             cfg.html = this.label;
16326         }
16327         
16328         if(this.panel){
16329             cfg.cls += ' progress-bar-' + this.panel;
16330         }
16331         
16332         return cfg;
16333     },
16334     
16335     update : function(aria_valuenow)
16336     {
16337         this.aria_valuenow = aria_valuenow;
16338         
16339         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16340     }
16341    
16342 });
16343
16344  
16345
16346  /*
16347  * - LGPL
16348  *
16349  * column
16350  * 
16351  */
16352
16353 /**
16354  * @class Roo.bootstrap.TabGroup
16355  * @extends Roo.bootstrap.Column
16356  * Bootstrap Column class
16357  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16358  * @cfg {Boolean} carousel true to make the group behave like a carousel
16359  * @cfg {Boolean} bullets show bullets for the panels
16360  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16361  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16362  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16363  * 
16364  * @constructor
16365  * Create a new TabGroup
16366  * @param {Object} config The config object
16367  */
16368
16369 Roo.bootstrap.TabGroup = function(config){
16370     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16371     if (!this.navId) {
16372         this.navId = Roo.id();
16373     }
16374     this.tabs = [];
16375     Roo.bootstrap.TabGroup.register(this);
16376     
16377 };
16378
16379 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16380     
16381     carousel : false,
16382     transition : false,
16383     bullets : 0,
16384     timer : 0,
16385     autoslide : false,
16386     slideFn : false,
16387     slideOnTouch : false,
16388     
16389     getAutoCreate : function()
16390     {
16391         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16392         
16393         cfg.cls += ' tab-content';
16394         
16395         if (this.carousel) {
16396             cfg.cls += ' carousel slide';
16397             
16398             cfg.cn = [{
16399                cls : 'carousel-inner'
16400             }];
16401         
16402             if(this.bullets  && !Roo.isTouch){
16403                 
16404                 var bullets = {
16405                     cls : 'carousel-bullets',
16406                     cn : []
16407                 };
16408                
16409                 if(this.bullets_cls){
16410                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16411                 }
16412                  /*
16413                 for (var i = 0; i < this.bullets; i++){
16414                     bullets.cn.push({
16415                         cls : 'bullet bullet-' + i
16416                     });
16417                 }
16418                 */
16419                 bullets.cn.push({
16420                     cls : 'clear'
16421                 });
16422                 
16423                 cfg.cn[0].cn = bullets;
16424             }
16425         }
16426         
16427         return cfg;
16428     },
16429     
16430     initEvents:  function()
16431     {
16432         if(Roo.isTouch && this.slideOnTouch){
16433             this.el.on("touchstart", this.onTouchStart, this);
16434         }
16435         
16436         if(this.autoslide){
16437             var _this = this;
16438             
16439             this.slideFn = window.setInterval(function() {
16440                 _this.showPanelNext();
16441             }, this.timer);
16442         }
16443         
16444     },
16445     
16446     onTouchStart : function(e, el, o)
16447     {
16448         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16449             return;
16450         }
16451         
16452         this.showPanelNext();
16453     },
16454     
16455     getChildContainer : function()
16456     {
16457         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16458     },
16459     
16460     /**
16461     * register a Navigation item
16462     * @param {Roo.bootstrap.NavItem} the navitem to add
16463     */
16464     register : function(item)
16465     {
16466         this.tabs.push( item);
16467         item.navId = this.navId; // not really needed..
16468         this.addBullet();
16469     
16470     },
16471     
16472     getActivePanel : function()
16473     {
16474         var r = false;
16475         Roo.each(this.tabs, function(t) {
16476             if (t.active) {
16477                 r = t;
16478                 return false;
16479             }
16480             return null;
16481         });
16482         return r;
16483         
16484     },
16485     getPanelByName : function(n)
16486     {
16487         var r = false;
16488         Roo.each(this.tabs, function(t) {
16489             if (t.tabId == n) {
16490                 r = t;
16491                 return false;
16492             }
16493             return null;
16494         });
16495         return r;
16496     },
16497     indexOfPanel : function(p)
16498     {
16499         var r = false;
16500         Roo.each(this.tabs, function(t,i) {
16501             if (t.tabId == p.tabId) {
16502                 r = i;
16503                 return false;
16504             }
16505             return null;
16506         });
16507         return r;
16508     },
16509     /**
16510      * show a specific panel
16511      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16512      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16513      */
16514     showPanel : function (pan)
16515     {
16516         if(this.transition || typeof(pan) == 'undefined'){
16517             Roo.log("waiting for the transitionend");
16518             return;
16519         }
16520         
16521         if (typeof(pan) == 'number') {
16522             pan = this.tabs[pan];
16523         }
16524         
16525         if (typeof(pan) == 'string') {
16526             pan = this.getPanelByName(pan);
16527         }
16528         
16529         var cur = this.getActivePanel();
16530         
16531         if(!pan || !cur){
16532             Roo.log('pan or acitve pan is undefined');
16533             return false;
16534         }
16535         
16536         if (pan.tabId == this.getActivePanel().tabId) {
16537             return true;
16538         }
16539         
16540         if (false === cur.fireEvent('beforedeactivate')) {
16541             return false;
16542         }
16543         
16544         if(this.bullets > 0 && !Roo.isTouch){
16545             this.setActiveBullet(this.indexOfPanel(pan));
16546         }
16547         
16548         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16549             
16550             this.transition = true;
16551             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16552             var lr = dir == 'next' ? 'left' : 'right';
16553             pan.el.addClass(dir); // or prev
16554             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16555             cur.el.addClass(lr); // or right
16556             pan.el.addClass(lr);
16557             
16558             var _this = this;
16559             cur.el.on('transitionend', function() {
16560                 Roo.log("trans end?");
16561                 
16562                 pan.el.removeClass([lr,dir]);
16563                 pan.setActive(true);
16564                 
16565                 cur.el.removeClass([lr]);
16566                 cur.setActive(false);
16567                 
16568                 _this.transition = false;
16569                 
16570             }, this, { single:  true } );
16571             
16572             return true;
16573         }
16574         
16575         cur.setActive(false);
16576         pan.setActive(true);
16577         
16578         return true;
16579         
16580     },
16581     showPanelNext : function()
16582     {
16583         var i = this.indexOfPanel(this.getActivePanel());
16584         
16585         if (i >= this.tabs.length - 1 && !this.autoslide) {
16586             return;
16587         }
16588         
16589         if (i >= this.tabs.length - 1 && this.autoslide) {
16590             i = -1;
16591         }
16592         
16593         this.showPanel(this.tabs[i+1]);
16594     },
16595     
16596     showPanelPrev : function()
16597     {
16598         var i = this.indexOfPanel(this.getActivePanel());
16599         
16600         if (i  < 1 && !this.autoslide) {
16601             return;
16602         }
16603         
16604         if (i < 1 && this.autoslide) {
16605             i = this.tabs.length;
16606         }
16607         
16608         this.showPanel(this.tabs[i-1]);
16609     },
16610     
16611     
16612     addBullet: function()
16613     {
16614         if(!this.bullets || Roo.isTouch){
16615             return;
16616         }
16617         var ctr = this.el.select('.carousel-bullets',true).first();
16618         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16619         var bullet = ctr.createChild({
16620             cls : 'bullet bullet-' + i
16621         },ctr.dom.lastChild);
16622         
16623         
16624         var _this = this;
16625         
16626         bullet.on('click', (function(e, el, o, ii, t){
16627
16628             e.preventDefault();
16629
16630             this.showPanel(ii);
16631
16632             if(this.autoslide && this.slideFn){
16633                 clearInterval(this.slideFn);
16634                 this.slideFn = window.setInterval(function() {
16635                     _this.showPanelNext();
16636                 }, this.timer);
16637             }
16638
16639         }).createDelegate(this, [i, bullet], true));
16640                 
16641         
16642     },
16643      
16644     setActiveBullet : function(i)
16645     {
16646         if(Roo.isTouch){
16647             return;
16648         }
16649         
16650         Roo.each(this.el.select('.bullet', true).elements, function(el){
16651             el.removeClass('selected');
16652         });
16653
16654         var bullet = this.el.select('.bullet-' + i, true).first();
16655         
16656         if(!bullet){
16657             return;
16658         }
16659         
16660         bullet.addClass('selected');
16661     }
16662     
16663     
16664   
16665 });
16666
16667  
16668
16669  
16670  
16671 Roo.apply(Roo.bootstrap.TabGroup, {
16672     
16673     groups: {},
16674      /**
16675     * register a Navigation Group
16676     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16677     */
16678     register : function(navgrp)
16679     {
16680         this.groups[navgrp.navId] = navgrp;
16681         
16682     },
16683     /**
16684     * fetch a Navigation Group based on the navigation ID
16685     * if one does not exist , it will get created.
16686     * @param {string} the navgroup to add
16687     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16688     */
16689     get: function(navId) {
16690         if (typeof(this.groups[navId]) == 'undefined') {
16691             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16692         }
16693         return this.groups[navId] ;
16694     }
16695     
16696     
16697     
16698 });
16699
16700  /*
16701  * - LGPL
16702  *
16703  * TabPanel
16704  * 
16705  */
16706
16707 /**
16708  * @class Roo.bootstrap.TabPanel
16709  * @extends Roo.bootstrap.Component
16710  * Bootstrap TabPanel class
16711  * @cfg {Boolean} active panel active
16712  * @cfg {String} html panel content
16713  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16714  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16715  * 
16716  * 
16717  * @constructor
16718  * Create a new TabPanel
16719  * @param {Object} config The config object
16720  */
16721
16722 Roo.bootstrap.TabPanel = function(config){
16723     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16724     this.addEvents({
16725         /**
16726              * @event changed
16727              * Fires when the active status changes
16728              * @param {Roo.bootstrap.TabPanel} this
16729              * @param {Boolean} state the new state
16730             
16731          */
16732         'changed': true,
16733         /**
16734              * @event beforedeactivate
16735              * Fires before a tab is de-activated - can be used to do validation on a form.
16736              * @param {Roo.bootstrap.TabPanel} this
16737              * @return {Boolean} false if there is an error
16738             
16739          */
16740         'beforedeactivate': true
16741      });
16742     
16743     this.tabId = this.tabId || Roo.id();
16744   
16745 };
16746
16747 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16748     
16749     active: false,
16750     html: false,
16751     tabId: false,
16752     navId : false,
16753     
16754     getAutoCreate : function(){
16755         var cfg = {
16756             tag: 'div',
16757             // item is needed for carousel - not sure if it has any effect otherwise
16758             cls: 'tab-pane item',
16759             html: this.html || ''
16760         };
16761         
16762         if(this.active){
16763             cfg.cls += ' active';
16764         }
16765         
16766         if(this.tabId){
16767             cfg.tabId = this.tabId;
16768         }
16769         
16770         
16771         return cfg;
16772     },
16773     
16774     initEvents:  function()
16775     {
16776         var p = this.parent();
16777         this.navId = this.navId || p.navId;
16778         
16779         if (typeof(this.navId) != 'undefined') {
16780             // not really needed.. but just in case.. parent should be a NavGroup.
16781             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16782             
16783             tg.register(this);
16784             
16785             var i = tg.tabs.length - 1;
16786             
16787             if(this.active && tg.bullets > 0 && i < tg.bullets){
16788                 tg.setActiveBullet(i);
16789             }
16790         }
16791         
16792     },
16793     
16794     
16795     onRender : function(ct, position)
16796     {
16797        // Roo.log("Call onRender: " + this.xtype);
16798         
16799         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16800         
16801         
16802         
16803         
16804         
16805     },
16806     
16807     setActive: function(state)
16808     {
16809         Roo.log("panel - set active " + this.tabId + "=" + state);
16810         
16811         this.active = state;
16812         if (!state) {
16813             this.el.removeClass('active');
16814             
16815         } else  if (!this.el.hasClass('active')) {
16816             this.el.addClass('active');
16817         }
16818         
16819         this.fireEvent('changed', this, state);
16820     }
16821     
16822     
16823 });
16824  
16825
16826  
16827
16828  /*
16829  * - LGPL
16830  *
16831  * DateField
16832  * 
16833  */
16834
16835 /**
16836  * @class Roo.bootstrap.DateField
16837  * @extends Roo.bootstrap.Input
16838  * Bootstrap DateField class
16839  * @cfg {Number} weekStart default 0
16840  * @cfg {String} viewMode default empty, (months|years)
16841  * @cfg {String} minViewMode default empty, (months|years)
16842  * @cfg {Number} startDate default -Infinity
16843  * @cfg {Number} endDate default Infinity
16844  * @cfg {Boolean} todayHighlight default false
16845  * @cfg {Boolean} todayBtn default false
16846  * @cfg {Boolean} calendarWeeks default false
16847  * @cfg {Object} daysOfWeekDisabled default empty
16848  * @cfg {Boolean} singleMode default false (true | false)
16849  * 
16850  * @cfg {Boolean} keyboardNavigation default true
16851  * @cfg {String} language default en
16852  * 
16853  * @constructor
16854  * Create a new DateField
16855  * @param {Object} config The config object
16856  */
16857
16858 Roo.bootstrap.DateField = function(config){
16859     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16860      this.addEvents({
16861             /**
16862              * @event show
16863              * Fires when this field show.
16864              * @param {Roo.bootstrap.DateField} this
16865              * @param {Mixed} date The date value
16866              */
16867             show : true,
16868             /**
16869              * @event show
16870              * Fires when this field hide.
16871              * @param {Roo.bootstrap.DateField} this
16872              * @param {Mixed} date The date value
16873              */
16874             hide : true,
16875             /**
16876              * @event select
16877              * Fires when select a date.
16878              * @param {Roo.bootstrap.DateField} this
16879              * @param {Mixed} date The date value
16880              */
16881             select : true,
16882             /**
16883              * @event beforeselect
16884              * Fires when before select a date.
16885              * @param {Roo.bootstrap.DateField} this
16886              * @param {Mixed} date The date value
16887              */
16888             beforeselect : true
16889         });
16890 };
16891
16892 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16893     
16894     /**
16895      * @cfg {String} format
16896      * The default date format string which can be overriden for localization support.  The format must be
16897      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16898      */
16899     format : "m/d/y",
16900     /**
16901      * @cfg {String} altFormats
16902      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16903      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16904      */
16905     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16906     
16907     weekStart : 0,
16908     
16909     viewMode : '',
16910     
16911     minViewMode : '',
16912     
16913     todayHighlight : false,
16914     
16915     todayBtn: false,
16916     
16917     language: 'en',
16918     
16919     keyboardNavigation: true,
16920     
16921     calendarWeeks: false,
16922     
16923     startDate: -Infinity,
16924     
16925     endDate: Infinity,
16926     
16927     daysOfWeekDisabled: [],
16928     
16929     _events: [],
16930     
16931     singleMode : false,
16932     
16933     UTCDate: function()
16934     {
16935         return new Date(Date.UTC.apply(Date, arguments));
16936     },
16937     
16938     UTCToday: function()
16939     {
16940         var today = new Date();
16941         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16942     },
16943     
16944     getDate: function() {
16945             var d = this.getUTCDate();
16946             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16947     },
16948     
16949     getUTCDate: function() {
16950             return this.date;
16951     },
16952     
16953     setDate: function(d) {
16954             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16955     },
16956     
16957     setUTCDate: function(d) {
16958             this.date = d;
16959             this.setValue(this.formatDate(this.date));
16960     },
16961         
16962     onRender: function(ct, position)
16963     {
16964         
16965         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16966         
16967         this.language = this.language || 'en';
16968         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16969         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16970         
16971         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16972         this.format = this.format || 'm/d/y';
16973         this.isInline = false;
16974         this.isInput = true;
16975         this.component = this.el.select('.add-on', true).first() || false;
16976         this.component = (this.component && this.component.length === 0) ? false : this.component;
16977         this.hasInput = this.component && this.inputEL().length;
16978         
16979         if (typeof(this.minViewMode === 'string')) {
16980             switch (this.minViewMode) {
16981                 case 'months':
16982                     this.minViewMode = 1;
16983                     break;
16984                 case 'years':
16985                     this.minViewMode = 2;
16986                     break;
16987                 default:
16988                     this.minViewMode = 0;
16989                     break;
16990             }
16991         }
16992         
16993         if (typeof(this.viewMode === 'string')) {
16994             switch (this.viewMode) {
16995                 case 'months':
16996                     this.viewMode = 1;
16997                     break;
16998                 case 'years':
16999                     this.viewMode = 2;
17000                     break;
17001                 default:
17002                     this.viewMode = 0;
17003                     break;
17004             }
17005         }
17006                 
17007         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17008         
17009 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17010         
17011         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17012         
17013         this.picker().on('mousedown', this.onMousedown, this);
17014         this.picker().on('click', this.onClick, this);
17015         
17016         this.picker().addClass('datepicker-dropdown');
17017         
17018         this.startViewMode = this.viewMode;
17019         
17020         if(this.singleMode){
17021             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17022                 v.setVisibilityMode(Roo.Element.DISPLAY);
17023                 v.hide();
17024             });
17025             
17026             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17027                 v.setStyle('width', '189px');
17028             });
17029         }
17030         
17031         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17032             if(!this.calendarWeeks){
17033                 v.remove();
17034                 return;
17035             }
17036             
17037             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17038             v.attr('colspan', function(i, val){
17039                 return parseInt(val) + 1;
17040             });
17041         });
17042                         
17043         
17044         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17045         
17046         this.setStartDate(this.startDate);
17047         this.setEndDate(this.endDate);
17048         
17049         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17050         
17051         this.fillDow();
17052         this.fillMonths();
17053         this.update();
17054         this.showMode();
17055         
17056         if(this.isInline) {
17057             this.show();
17058         }
17059     },
17060     
17061     picker : function()
17062     {
17063         return this.pickerEl;
17064 //        return this.el.select('.datepicker', true).first();
17065     },
17066     
17067     fillDow: function()
17068     {
17069         var dowCnt = this.weekStart;
17070         
17071         var dow = {
17072             tag: 'tr',
17073             cn: [
17074                 
17075             ]
17076         };
17077         
17078         if(this.calendarWeeks){
17079             dow.cn.push({
17080                 tag: 'th',
17081                 cls: 'cw',
17082                 html: '&nbsp;'
17083             })
17084         }
17085         
17086         while (dowCnt < this.weekStart + 7) {
17087             dow.cn.push({
17088                 tag: 'th',
17089                 cls: 'dow',
17090                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17091             });
17092         }
17093         
17094         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17095     },
17096     
17097     fillMonths: function()
17098     {    
17099         var i = 0;
17100         var months = this.picker().select('>.datepicker-months td', true).first();
17101         
17102         months.dom.innerHTML = '';
17103         
17104         while (i < 12) {
17105             var month = {
17106                 tag: 'span',
17107                 cls: 'month',
17108                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17109             };
17110             
17111             months.createChild(month);
17112         }
17113         
17114     },
17115     
17116     update: function()
17117     {
17118         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;
17119         
17120         if (this.date < this.startDate) {
17121             this.viewDate = new Date(this.startDate);
17122         } else if (this.date > this.endDate) {
17123             this.viewDate = new Date(this.endDate);
17124         } else {
17125             this.viewDate = new Date(this.date);
17126         }
17127         
17128         this.fill();
17129     },
17130     
17131     fill: function() 
17132     {
17133         var d = new Date(this.viewDate),
17134                 year = d.getUTCFullYear(),
17135                 month = d.getUTCMonth(),
17136                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17137                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17138                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17139                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17140                 currentDate = this.date && this.date.valueOf(),
17141                 today = this.UTCToday();
17142         
17143         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17144         
17145 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17146         
17147 //        this.picker.select('>tfoot th.today').
17148 //                                              .text(dates[this.language].today)
17149 //                                              .toggle(this.todayBtn !== false);
17150     
17151         this.updateNavArrows();
17152         this.fillMonths();
17153                                                 
17154         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17155         
17156         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17157          
17158         prevMonth.setUTCDate(day);
17159         
17160         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17161         
17162         var nextMonth = new Date(prevMonth);
17163         
17164         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17165         
17166         nextMonth = nextMonth.valueOf();
17167         
17168         var fillMonths = false;
17169         
17170         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17171         
17172         while(prevMonth.valueOf() < nextMonth) {
17173             var clsName = '';
17174             
17175             if (prevMonth.getUTCDay() === this.weekStart) {
17176                 if(fillMonths){
17177                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17178                 }
17179                     
17180                 fillMonths = {
17181                     tag: 'tr',
17182                     cn: []
17183                 };
17184                 
17185                 if(this.calendarWeeks){
17186                     // ISO 8601: First week contains first thursday.
17187                     // ISO also states week starts on Monday, but we can be more abstract here.
17188                     var
17189                     // Start of current week: based on weekstart/current date
17190                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17191                     // Thursday of this week
17192                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17193                     // First Thursday of year, year from thursday
17194                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17195                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17196                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17197                     
17198                     fillMonths.cn.push({
17199                         tag: 'td',
17200                         cls: 'cw',
17201                         html: calWeek
17202                     });
17203                 }
17204             }
17205             
17206             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17207                 clsName += ' old';
17208             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17209                 clsName += ' new';
17210             }
17211             if (this.todayHighlight &&
17212                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17213                 prevMonth.getUTCMonth() == today.getMonth() &&
17214                 prevMonth.getUTCDate() == today.getDate()) {
17215                 clsName += ' today';
17216             }
17217             
17218             if (currentDate && prevMonth.valueOf() === currentDate) {
17219                 clsName += ' active';
17220             }
17221             
17222             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17223                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17224                     clsName += ' disabled';
17225             }
17226             
17227             fillMonths.cn.push({
17228                 tag: 'td',
17229                 cls: 'day ' + clsName,
17230                 html: prevMonth.getDate()
17231             });
17232             
17233             prevMonth.setDate(prevMonth.getDate()+1);
17234         }
17235           
17236         var currentYear = this.date && this.date.getUTCFullYear();
17237         var currentMonth = this.date && this.date.getUTCMonth();
17238         
17239         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17240         
17241         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17242             v.removeClass('active');
17243             
17244             if(currentYear === year && k === currentMonth){
17245                 v.addClass('active');
17246             }
17247             
17248             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17249                 v.addClass('disabled');
17250             }
17251             
17252         });
17253         
17254         
17255         year = parseInt(year/10, 10) * 10;
17256         
17257         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17258         
17259         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17260         
17261         year -= 1;
17262         for (var i = -1; i < 11; i++) {
17263             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17264                 tag: 'span',
17265                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17266                 html: year
17267             });
17268             
17269             year += 1;
17270         }
17271     },
17272     
17273     showMode: function(dir) 
17274     {
17275         if (dir) {
17276             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17277         }
17278         
17279         Roo.each(this.picker().select('>div',true).elements, function(v){
17280             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17281             v.hide();
17282         });
17283         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17284     },
17285     
17286     place: function()
17287     {
17288         if(this.isInline) {
17289             return;
17290         }
17291         
17292         this.picker().removeClass(['bottom', 'top']);
17293         
17294         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17295             /*
17296              * place to the top of element!
17297              *
17298              */
17299             
17300             this.picker().addClass('top');
17301             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17302             
17303             return;
17304         }
17305         
17306         this.picker().addClass('bottom');
17307         
17308         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17309     },
17310     
17311     parseDate : function(value)
17312     {
17313         if(!value || value instanceof Date){
17314             return value;
17315         }
17316         var v = Date.parseDate(value, this.format);
17317         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17318             v = Date.parseDate(value, 'Y-m-d');
17319         }
17320         if(!v && this.altFormats){
17321             if(!this.altFormatsArray){
17322                 this.altFormatsArray = this.altFormats.split("|");
17323             }
17324             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17325                 v = Date.parseDate(value, this.altFormatsArray[i]);
17326             }
17327         }
17328         return v;
17329     },
17330     
17331     formatDate : function(date, fmt)
17332     {   
17333         return (!date || !(date instanceof Date)) ?
17334         date : date.dateFormat(fmt || this.format);
17335     },
17336     
17337     onFocus : function()
17338     {
17339         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17340         this.show();
17341     },
17342     
17343     onBlur : function()
17344     {
17345         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17346         
17347         var d = this.inputEl().getValue();
17348         
17349         this.setValue(d);
17350                 
17351         this.hide();
17352     },
17353     
17354     show : function()
17355     {
17356         this.picker().show();
17357         this.update();
17358         this.place();
17359         
17360         this.fireEvent('show', this, this.date);
17361     },
17362     
17363     hide : function()
17364     {
17365         if(this.isInline) {
17366             return;
17367         }
17368         this.picker().hide();
17369         this.viewMode = this.startViewMode;
17370         this.showMode();
17371         
17372         this.fireEvent('hide', this, this.date);
17373         
17374     },
17375     
17376     onMousedown: function(e)
17377     {
17378         e.stopPropagation();
17379         e.preventDefault();
17380     },
17381     
17382     keyup: function(e)
17383     {
17384         Roo.bootstrap.DateField.superclass.keyup.call(this);
17385         this.update();
17386     },
17387
17388     setValue: function(v)
17389     {
17390         if(this.fireEvent('beforeselect', this, v) !== false){
17391             var d = new Date(this.parseDate(v) ).clearTime();
17392         
17393             if(isNaN(d.getTime())){
17394                 this.date = this.viewDate = '';
17395                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17396                 return;
17397             }
17398
17399             v = this.formatDate(d);
17400
17401             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17402
17403             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17404
17405             this.update();
17406
17407             this.fireEvent('select', this, this.date);
17408         }
17409     },
17410     
17411     getValue: function()
17412     {
17413         return this.formatDate(this.date);
17414     },
17415     
17416     fireKey: function(e)
17417     {
17418         if (!this.picker().isVisible()){
17419             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17420                 this.show();
17421             }
17422             return;
17423         }
17424         
17425         var dateChanged = false,
17426         dir, day, month,
17427         newDate, newViewDate;
17428         
17429         switch(e.keyCode){
17430             case 27: // escape
17431                 this.hide();
17432                 e.preventDefault();
17433                 break;
17434             case 37: // left
17435             case 39: // right
17436                 if (!this.keyboardNavigation) {
17437                     break;
17438                 }
17439                 dir = e.keyCode == 37 ? -1 : 1;
17440                 
17441                 if (e.ctrlKey){
17442                     newDate = this.moveYear(this.date, dir);
17443                     newViewDate = this.moveYear(this.viewDate, dir);
17444                 } else if (e.shiftKey){
17445                     newDate = this.moveMonth(this.date, dir);
17446                     newViewDate = this.moveMonth(this.viewDate, dir);
17447                 } else {
17448                     newDate = new Date(this.date);
17449                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17450                     newViewDate = new Date(this.viewDate);
17451                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17452                 }
17453                 if (this.dateWithinRange(newDate)){
17454                     this.date = newDate;
17455                     this.viewDate = newViewDate;
17456                     this.setValue(this.formatDate(this.date));
17457 //                    this.update();
17458                     e.preventDefault();
17459                     dateChanged = true;
17460                 }
17461                 break;
17462             case 38: // up
17463             case 40: // down
17464                 if (!this.keyboardNavigation) {
17465                     break;
17466                 }
17467                 dir = e.keyCode == 38 ? -1 : 1;
17468                 if (e.ctrlKey){
17469                     newDate = this.moveYear(this.date, dir);
17470                     newViewDate = this.moveYear(this.viewDate, dir);
17471                 } else if (e.shiftKey){
17472                     newDate = this.moveMonth(this.date, dir);
17473                     newViewDate = this.moveMonth(this.viewDate, dir);
17474                 } else {
17475                     newDate = new Date(this.date);
17476                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17477                     newViewDate = new Date(this.viewDate);
17478                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17479                 }
17480                 if (this.dateWithinRange(newDate)){
17481                     this.date = newDate;
17482                     this.viewDate = newViewDate;
17483                     this.setValue(this.formatDate(this.date));
17484 //                    this.update();
17485                     e.preventDefault();
17486                     dateChanged = true;
17487                 }
17488                 break;
17489             case 13: // enter
17490                 this.setValue(this.formatDate(this.date));
17491                 this.hide();
17492                 e.preventDefault();
17493                 break;
17494             case 9: // tab
17495                 this.setValue(this.formatDate(this.date));
17496                 this.hide();
17497                 break;
17498             case 16: // shift
17499             case 17: // ctrl
17500             case 18: // alt
17501                 break;
17502             default :
17503                 this.hide();
17504                 
17505         }
17506     },
17507     
17508     
17509     onClick: function(e) 
17510     {
17511         e.stopPropagation();
17512         e.preventDefault();
17513         
17514         var target = e.getTarget();
17515         
17516         if(target.nodeName.toLowerCase() === 'i'){
17517             target = Roo.get(target).dom.parentNode;
17518         }
17519         
17520         var nodeName = target.nodeName;
17521         var className = target.className;
17522         var html = target.innerHTML;
17523         //Roo.log(nodeName);
17524         
17525         switch(nodeName.toLowerCase()) {
17526             case 'th':
17527                 switch(className) {
17528                     case 'switch':
17529                         this.showMode(1);
17530                         break;
17531                     case 'prev':
17532                     case 'next':
17533                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17534                         switch(this.viewMode){
17535                                 case 0:
17536                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17537                                         break;
17538                                 case 1:
17539                                 case 2:
17540                                         this.viewDate = this.moveYear(this.viewDate, dir);
17541                                         break;
17542                         }
17543                         this.fill();
17544                         break;
17545                     case 'today':
17546                         var date = new Date();
17547                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17548 //                        this.fill()
17549                         this.setValue(this.formatDate(this.date));
17550                         
17551                         this.hide();
17552                         break;
17553                 }
17554                 break;
17555             case 'span':
17556                 if (className.indexOf('disabled') < 0) {
17557                     this.viewDate.setUTCDate(1);
17558                     if (className.indexOf('month') > -1) {
17559                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17560                     } else {
17561                         var year = parseInt(html, 10) || 0;
17562                         this.viewDate.setUTCFullYear(year);
17563                         
17564                     }
17565                     
17566                     if(this.singleMode){
17567                         this.setValue(this.formatDate(this.viewDate));
17568                         this.hide();
17569                         return;
17570                     }
17571                     
17572                     this.showMode(-1);
17573                     this.fill();
17574                 }
17575                 break;
17576                 
17577             case 'td':
17578                 //Roo.log(className);
17579                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17580                     var day = parseInt(html, 10) || 1;
17581                     var year = this.viewDate.getUTCFullYear(),
17582                         month = this.viewDate.getUTCMonth();
17583
17584                     if (className.indexOf('old') > -1) {
17585                         if(month === 0 ){
17586                             month = 11;
17587                             year -= 1;
17588                         }else{
17589                             month -= 1;
17590                         }
17591                     } else if (className.indexOf('new') > -1) {
17592                         if (month == 11) {
17593                             month = 0;
17594                             year += 1;
17595                         } else {
17596                             month += 1;
17597                         }
17598                     }
17599                     //Roo.log([year,month,day]);
17600                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17601                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17602 //                    this.fill();
17603                     //Roo.log(this.formatDate(this.date));
17604                     this.setValue(this.formatDate(this.date));
17605                     this.hide();
17606                 }
17607                 break;
17608         }
17609     },
17610     
17611     setStartDate: function(startDate)
17612     {
17613         this.startDate = startDate || -Infinity;
17614         if (this.startDate !== -Infinity) {
17615             this.startDate = this.parseDate(this.startDate);
17616         }
17617         this.update();
17618         this.updateNavArrows();
17619     },
17620
17621     setEndDate: function(endDate)
17622     {
17623         this.endDate = endDate || Infinity;
17624         if (this.endDate !== Infinity) {
17625             this.endDate = this.parseDate(this.endDate);
17626         }
17627         this.update();
17628         this.updateNavArrows();
17629     },
17630     
17631     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17632     {
17633         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17634         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17635             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17636         }
17637         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17638             return parseInt(d, 10);
17639         });
17640         this.update();
17641         this.updateNavArrows();
17642     },
17643     
17644     updateNavArrows: function() 
17645     {
17646         if(this.singleMode){
17647             return;
17648         }
17649         
17650         var d = new Date(this.viewDate),
17651         year = d.getUTCFullYear(),
17652         month = d.getUTCMonth();
17653         
17654         Roo.each(this.picker().select('.prev', true).elements, function(v){
17655             v.show();
17656             switch (this.viewMode) {
17657                 case 0:
17658
17659                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17660                         v.hide();
17661                     }
17662                     break;
17663                 case 1:
17664                 case 2:
17665                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17666                         v.hide();
17667                     }
17668                     break;
17669             }
17670         });
17671         
17672         Roo.each(this.picker().select('.next', true).elements, function(v){
17673             v.show();
17674             switch (this.viewMode) {
17675                 case 0:
17676
17677                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17678                         v.hide();
17679                     }
17680                     break;
17681                 case 1:
17682                 case 2:
17683                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17684                         v.hide();
17685                     }
17686                     break;
17687             }
17688         })
17689     },
17690     
17691     moveMonth: function(date, dir)
17692     {
17693         if (!dir) {
17694             return date;
17695         }
17696         var new_date = new Date(date.valueOf()),
17697         day = new_date.getUTCDate(),
17698         month = new_date.getUTCMonth(),
17699         mag = Math.abs(dir),
17700         new_month, test;
17701         dir = dir > 0 ? 1 : -1;
17702         if (mag == 1){
17703             test = dir == -1
17704             // If going back one month, make sure month is not current month
17705             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17706             ? function(){
17707                 return new_date.getUTCMonth() == month;
17708             }
17709             // If going forward one month, make sure month is as expected
17710             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17711             : function(){
17712                 return new_date.getUTCMonth() != new_month;
17713             };
17714             new_month = month + dir;
17715             new_date.setUTCMonth(new_month);
17716             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17717             if (new_month < 0 || new_month > 11) {
17718                 new_month = (new_month + 12) % 12;
17719             }
17720         } else {
17721             // For magnitudes >1, move one month at a time...
17722             for (var i=0; i<mag; i++) {
17723                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17724                 new_date = this.moveMonth(new_date, dir);
17725             }
17726             // ...then reset the day, keeping it in the new month
17727             new_month = new_date.getUTCMonth();
17728             new_date.setUTCDate(day);
17729             test = function(){
17730                 return new_month != new_date.getUTCMonth();
17731             };
17732         }
17733         // Common date-resetting loop -- if date is beyond end of month, make it
17734         // end of month
17735         while (test()){
17736             new_date.setUTCDate(--day);
17737             new_date.setUTCMonth(new_month);
17738         }
17739         return new_date;
17740     },
17741
17742     moveYear: function(date, dir)
17743     {
17744         return this.moveMonth(date, dir*12);
17745     },
17746
17747     dateWithinRange: function(date)
17748     {
17749         return date >= this.startDate && date <= this.endDate;
17750     },
17751
17752     
17753     remove: function() 
17754     {
17755         this.picker().remove();
17756     }
17757    
17758 });
17759
17760 Roo.apply(Roo.bootstrap.DateField,  {
17761     
17762     head : {
17763         tag: 'thead',
17764         cn: [
17765         {
17766             tag: 'tr',
17767             cn: [
17768             {
17769                 tag: 'th',
17770                 cls: 'prev',
17771                 html: '<i class="fa fa-arrow-left"/>'
17772             },
17773             {
17774                 tag: 'th',
17775                 cls: 'switch',
17776                 colspan: '5'
17777             },
17778             {
17779                 tag: 'th',
17780                 cls: 'next',
17781                 html: '<i class="fa fa-arrow-right"/>'
17782             }
17783
17784             ]
17785         }
17786         ]
17787     },
17788     
17789     content : {
17790         tag: 'tbody',
17791         cn: [
17792         {
17793             tag: 'tr',
17794             cn: [
17795             {
17796                 tag: 'td',
17797                 colspan: '7'
17798             }
17799             ]
17800         }
17801         ]
17802     },
17803     
17804     footer : {
17805         tag: 'tfoot',
17806         cn: [
17807         {
17808             tag: 'tr',
17809             cn: [
17810             {
17811                 tag: 'th',
17812                 colspan: '7',
17813                 cls: 'today'
17814             }
17815                     
17816             ]
17817         }
17818         ]
17819     },
17820     
17821     dates:{
17822         en: {
17823             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17824             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17825             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17826             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17827             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17828             today: "Today"
17829         }
17830     },
17831     
17832     modes: [
17833     {
17834         clsName: 'days',
17835         navFnc: 'Month',
17836         navStep: 1
17837     },
17838     {
17839         clsName: 'months',
17840         navFnc: 'FullYear',
17841         navStep: 1
17842     },
17843     {
17844         clsName: 'years',
17845         navFnc: 'FullYear',
17846         navStep: 10
17847     }]
17848 });
17849
17850 Roo.apply(Roo.bootstrap.DateField,  {
17851   
17852     template : {
17853         tag: 'div',
17854         cls: 'datepicker dropdown-menu roo-dynamic',
17855         cn: [
17856         {
17857             tag: 'div',
17858             cls: 'datepicker-days',
17859             cn: [
17860             {
17861                 tag: 'table',
17862                 cls: 'table-condensed',
17863                 cn:[
17864                 Roo.bootstrap.DateField.head,
17865                 {
17866                     tag: 'tbody'
17867                 },
17868                 Roo.bootstrap.DateField.footer
17869                 ]
17870             }
17871             ]
17872         },
17873         {
17874             tag: 'div',
17875             cls: 'datepicker-months',
17876             cn: [
17877             {
17878                 tag: 'table',
17879                 cls: 'table-condensed',
17880                 cn:[
17881                 Roo.bootstrap.DateField.head,
17882                 Roo.bootstrap.DateField.content,
17883                 Roo.bootstrap.DateField.footer
17884                 ]
17885             }
17886             ]
17887         },
17888         {
17889             tag: 'div',
17890             cls: 'datepicker-years',
17891             cn: [
17892             {
17893                 tag: 'table',
17894                 cls: 'table-condensed',
17895                 cn:[
17896                 Roo.bootstrap.DateField.head,
17897                 Roo.bootstrap.DateField.content,
17898                 Roo.bootstrap.DateField.footer
17899                 ]
17900             }
17901             ]
17902         }
17903         ]
17904     }
17905 });
17906
17907  
17908
17909  /*
17910  * - LGPL
17911  *
17912  * TimeField
17913  * 
17914  */
17915
17916 /**
17917  * @class Roo.bootstrap.TimeField
17918  * @extends Roo.bootstrap.Input
17919  * Bootstrap DateField class
17920  * 
17921  * 
17922  * @constructor
17923  * Create a new TimeField
17924  * @param {Object} config The config object
17925  */
17926
17927 Roo.bootstrap.TimeField = function(config){
17928     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17929     this.addEvents({
17930             /**
17931              * @event show
17932              * Fires when this field show.
17933              * @param {Roo.bootstrap.DateField} thisthis
17934              * @param {Mixed} date The date value
17935              */
17936             show : true,
17937             /**
17938              * @event show
17939              * Fires when this field hide.
17940              * @param {Roo.bootstrap.DateField} this
17941              * @param {Mixed} date The date value
17942              */
17943             hide : true,
17944             /**
17945              * @event select
17946              * Fires when select a date.
17947              * @param {Roo.bootstrap.DateField} this
17948              * @param {Mixed} date The date value
17949              */
17950             select : true
17951         });
17952 };
17953
17954 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17955     
17956     /**
17957      * @cfg {String} format
17958      * The default time format string which can be overriden for localization support.  The format must be
17959      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17960      */
17961     format : "H:i",
17962        
17963     onRender: function(ct, position)
17964     {
17965         
17966         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17967                 
17968         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17969         
17970         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17971         
17972         this.pop = this.picker().select('>.datepicker-time',true).first();
17973         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17974         
17975         this.picker().on('mousedown', this.onMousedown, this);
17976         this.picker().on('click', this.onClick, this);
17977         
17978         this.picker().addClass('datepicker-dropdown');
17979     
17980         this.fillTime();
17981         this.update();
17982             
17983         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17984         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17985         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17986         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17987         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17988         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17989
17990     },
17991     
17992     fireKey: function(e){
17993         if (!this.picker().isVisible()){
17994             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17995                 this.show();
17996             }
17997             return;
17998         }
17999
18000         e.preventDefault();
18001         
18002         switch(e.keyCode){
18003             case 27: // escape
18004                 this.hide();
18005                 break;
18006             case 37: // left
18007             case 39: // right
18008                 this.onTogglePeriod();
18009                 break;
18010             case 38: // up
18011                 this.onIncrementMinutes();
18012                 break;
18013             case 40: // down
18014                 this.onDecrementMinutes();
18015                 break;
18016             case 13: // enter
18017             case 9: // tab
18018                 this.setTime();
18019                 break;
18020         }
18021     },
18022     
18023     onClick: function(e) {
18024         e.stopPropagation();
18025         e.preventDefault();
18026     },
18027     
18028     picker : function()
18029     {
18030         return this.el.select('.datepicker', true).first();
18031     },
18032     
18033     fillTime: function()
18034     {    
18035         var time = this.pop.select('tbody', true).first();
18036         
18037         time.dom.innerHTML = '';
18038         
18039         time.createChild({
18040             tag: 'tr',
18041             cn: [
18042                 {
18043                     tag: 'td',
18044                     cn: [
18045                         {
18046                             tag: 'a',
18047                             href: '#',
18048                             cls: 'btn',
18049                             cn: [
18050                                 {
18051                                     tag: 'span',
18052                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18053                                 }
18054                             ]
18055                         } 
18056                     ]
18057                 },
18058                 {
18059                     tag: 'td',
18060                     cls: 'separator'
18061                 },
18062                 {
18063                     tag: 'td',
18064                     cn: [
18065                         {
18066                             tag: 'a',
18067                             href: '#',
18068                             cls: 'btn',
18069                             cn: [
18070                                 {
18071                                     tag: 'span',
18072                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18073                                 }
18074                             ]
18075                         }
18076                     ]
18077                 },
18078                 {
18079                     tag: 'td',
18080                     cls: 'separator'
18081                 }
18082             ]
18083         });
18084         
18085         time.createChild({
18086             tag: 'tr',
18087             cn: [
18088                 {
18089                     tag: 'td',
18090                     cn: [
18091                         {
18092                             tag: 'span',
18093                             cls: 'timepicker-hour',
18094                             html: '00'
18095                         }  
18096                     ]
18097                 },
18098                 {
18099                     tag: 'td',
18100                     cls: 'separator',
18101                     html: ':'
18102                 },
18103                 {
18104                     tag: 'td',
18105                     cn: [
18106                         {
18107                             tag: 'span',
18108                             cls: 'timepicker-minute',
18109                             html: '00'
18110                         }  
18111                     ]
18112                 },
18113                 {
18114                     tag: 'td',
18115                     cls: 'separator'
18116                 },
18117                 {
18118                     tag: 'td',
18119                     cn: [
18120                         {
18121                             tag: 'button',
18122                             type: 'button',
18123                             cls: 'btn btn-primary period',
18124                             html: 'AM'
18125                             
18126                         }
18127                     ]
18128                 }
18129             ]
18130         });
18131         
18132         time.createChild({
18133             tag: 'tr',
18134             cn: [
18135                 {
18136                     tag: 'td',
18137                     cn: [
18138                         {
18139                             tag: 'a',
18140                             href: '#',
18141                             cls: 'btn',
18142                             cn: [
18143                                 {
18144                                     tag: 'span',
18145                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18146                                 }
18147                             ]
18148                         }
18149                     ]
18150                 },
18151                 {
18152                     tag: 'td',
18153                     cls: 'separator'
18154                 },
18155                 {
18156                     tag: 'td',
18157                     cn: [
18158                         {
18159                             tag: 'a',
18160                             href: '#',
18161                             cls: 'btn',
18162                             cn: [
18163                                 {
18164                                     tag: 'span',
18165                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18166                                 }
18167                             ]
18168                         }
18169                     ]
18170                 },
18171                 {
18172                     tag: 'td',
18173                     cls: 'separator'
18174                 }
18175             ]
18176         });
18177         
18178     },
18179     
18180     update: function()
18181     {
18182         
18183         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18184         
18185         this.fill();
18186     },
18187     
18188     fill: function() 
18189     {
18190         var hours = this.time.getHours();
18191         var minutes = this.time.getMinutes();
18192         var period = 'AM';
18193         
18194         if(hours > 11){
18195             period = 'PM';
18196         }
18197         
18198         if(hours == 0){
18199             hours = 12;
18200         }
18201         
18202         
18203         if(hours > 12){
18204             hours = hours - 12;
18205         }
18206         
18207         if(hours < 10){
18208             hours = '0' + hours;
18209         }
18210         
18211         if(minutes < 10){
18212             minutes = '0' + minutes;
18213         }
18214         
18215         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18216         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18217         this.pop.select('button', true).first().dom.innerHTML = period;
18218         
18219     },
18220     
18221     place: function()
18222     {   
18223         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18224         
18225         var cls = ['bottom'];
18226         
18227         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18228             cls.pop();
18229             cls.push('top');
18230         }
18231         
18232         cls.push('right');
18233         
18234         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18235             cls.pop();
18236             cls.push('left');
18237         }
18238         
18239         this.picker().addClass(cls.join('-'));
18240         
18241         var _this = this;
18242         
18243         Roo.each(cls, function(c){
18244             if(c == 'bottom'){
18245                 _this.picker().setTop(_this.inputEl().getHeight());
18246                 return;
18247             }
18248             if(c == 'top'){
18249                 _this.picker().setTop(0 - _this.picker().getHeight());
18250                 return;
18251             }
18252             
18253             if(c == 'left'){
18254                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18255                 return;
18256             }
18257             if(c == 'right'){
18258                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18259                 return;
18260             }
18261         });
18262         
18263     },
18264   
18265     onFocus : function()
18266     {
18267         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18268         this.show();
18269     },
18270     
18271     onBlur : function()
18272     {
18273         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18274         this.hide();
18275     },
18276     
18277     show : function()
18278     {
18279         this.picker().show();
18280         this.pop.show();
18281         this.update();
18282         this.place();
18283         
18284         this.fireEvent('show', this, this.date);
18285     },
18286     
18287     hide : function()
18288     {
18289         this.picker().hide();
18290         this.pop.hide();
18291         
18292         this.fireEvent('hide', this, this.date);
18293     },
18294     
18295     setTime : function()
18296     {
18297         this.hide();
18298         this.setValue(this.time.format(this.format));
18299         
18300         this.fireEvent('select', this, this.date);
18301         
18302         
18303     },
18304     
18305     onMousedown: function(e){
18306         e.stopPropagation();
18307         e.preventDefault();
18308     },
18309     
18310     onIncrementHours: function()
18311     {
18312         Roo.log('onIncrementHours');
18313         this.time = this.time.add(Date.HOUR, 1);
18314         this.update();
18315         
18316     },
18317     
18318     onDecrementHours: function()
18319     {
18320         Roo.log('onDecrementHours');
18321         this.time = this.time.add(Date.HOUR, -1);
18322         this.update();
18323     },
18324     
18325     onIncrementMinutes: function()
18326     {
18327         Roo.log('onIncrementMinutes');
18328         this.time = this.time.add(Date.MINUTE, 1);
18329         this.update();
18330     },
18331     
18332     onDecrementMinutes: function()
18333     {
18334         Roo.log('onDecrementMinutes');
18335         this.time = this.time.add(Date.MINUTE, -1);
18336         this.update();
18337     },
18338     
18339     onTogglePeriod: function()
18340     {
18341         Roo.log('onTogglePeriod');
18342         this.time = this.time.add(Date.HOUR, 12);
18343         this.update();
18344     }
18345     
18346    
18347 });
18348
18349 Roo.apply(Roo.bootstrap.TimeField,  {
18350     
18351     content : {
18352         tag: 'tbody',
18353         cn: [
18354             {
18355                 tag: 'tr',
18356                 cn: [
18357                 {
18358                     tag: 'td',
18359                     colspan: '7'
18360                 }
18361                 ]
18362             }
18363         ]
18364     },
18365     
18366     footer : {
18367         tag: 'tfoot',
18368         cn: [
18369             {
18370                 tag: 'tr',
18371                 cn: [
18372                 {
18373                     tag: 'th',
18374                     colspan: '7',
18375                     cls: '',
18376                     cn: [
18377                         {
18378                             tag: 'button',
18379                             cls: 'btn btn-info ok',
18380                             html: 'OK'
18381                         }
18382                     ]
18383                 }
18384
18385                 ]
18386             }
18387         ]
18388     }
18389 });
18390
18391 Roo.apply(Roo.bootstrap.TimeField,  {
18392   
18393     template : {
18394         tag: 'div',
18395         cls: 'datepicker dropdown-menu',
18396         cn: [
18397             {
18398                 tag: 'div',
18399                 cls: 'datepicker-time',
18400                 cn: [
18401                 {
18402                     tag: 'table',
18403                     cls: 'table-condensed',
18404                     cn:[
18405                     Roo.bootstrap.TimeField.content,
18406                     Roo.bootstrap.TimeField.footer
18407                     ]
18408                 }
18409                 ]
18410             }
18411         ]
18412     }
18413 });
18414
18415  
18416
18417  /*
18418  * - LGPL
18419  *
18420  * MonthField
18421  * 
18422  */
18423
18424 /**
18425  * @class Roo.bootstrap.MonthField
18426  * @extends Roo.bootstrap.Input
18427  * Bootstrap MonthField class
18428  * 
18429  * @cfg {String} language default en
18430  * 
18431  * @constructor
18432  * Create a new MonthField
18433  * @param {Object} config The config object
18434  */
18435
18436 Roo.bootstrap.MonthField = function(config){
18437     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18438     
18439     this.addEvents({
18440         /**
18441          * @event show
18442          * Fires when this field show.
18443          * @param {Roo.bootstrap.MonthField} this
18444          * @param {Mixed} date The date value
18445          */
18446         show : true,
18447         /**
18448          * @event show
18449          * Fires when this field hide.
18450          * @param {Roo.bootstrap.MonthField} this
18451          * @param {Mixed} date The date value
18452          */
18453         hide : true,
18454         /**
18455          * @event select
18456          * Fires when select a date.
18457          * @param {Roo.bootstrap.MonthField} this
18458          * @param {String} oldvalue The old value
18459          * @param {String} newvalue The new value
18460          */
18461         select : true
18462     });
18463 };
18464
18465 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18466     
18467     onRender: function(ct, position)
18468     {
18469         
18470         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18471         
18472         this.language = this.language || 'en';
18473         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18474         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18475         
18476         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18477         this.isInline = false;
18478         this.isInput = true;
18479         this.component = this.el.select('.add-on', true).first() || false;
18480         this.component = (this.component && this.component.length === 0) ? false : this.component;
18481         this.hasInput = this.component && this.inputEL().length;
18482         
18483         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18484         
18485         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18486         
18487         this.picker().on('mousedown', this.onMousedown, this);
18488         this.picker().on('click', this.onClick, this);
18489         
18490         this.picker().addClass('datepicker-dropdown');
18491         
18492         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18493             v.setStyle('width', '189px');
18494         });
18495         
18496         this.fillMonths();
18497         
18498         this.update();
18499         
18500         if(this.isInline) {
18501             this.show();
18502         }
18503         
18504     },
18505     
18506     setValue: function(v, suppressEvent)
18507     {   
18508         var o = this.getValue();
18509         
18510         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18511         
18512         this.update();
18513
18514         if(suppressEvent !== true){
18515             this.fireEvent('select', this, o, v);
18516         }
18517         
18518     },
18519     
18520     getValue: function()
18521     {
18522         return this.value;
18523     },
18524     
18525     onClick: function(e) 
18526     {
18527         e.stopPropagation();
18528         e.preventDefault();
18529         
18530         var target = e.getTarget();
18531         
18532         if(target.nodeName.toLowerCase() === 'i'){
18533             target = Roo.get(target).dom.parentNode;
18534         }
18535         
18536         var nodeName = target.nodeName;
18537         var className = target.className;
18538         var html = target.innerHTML;
18539         
18540         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18541             return;
18542         }
18543         
18544         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18545         
18546         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18547         
18548         this.hide();
18549                         
18550     },
18551     
18552     picker : function()
18553     {
18554         return this.pickerEl;
18555     },
18556     
18557     fillMonths: function()
18558     {    
18559         var i = 0;
18560         var months = this.picker().select('>.datepicker-months td', true).first();
18561         
18562         months.dom.innerHTML = '';
18563         
18564         while (i < 12) {
18565             var month = {
18566                 tag: 'span',
18567                 cls: 'month',
18568                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18569             };
18570             
18571             months.createChild(month);
18572         }
18573         
18574     },
18575     
18576     update: function()
18577     {
18578         var _this = this;
18579         
18580         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18581             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18582         }
18583         
18584         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18585             e.removeClass('active');
18586             
18587             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18588                 e.addClass('active');
18589             }
18590         })
18591     },
18592     
18593     place: function()
18594     {
18595         if(this.isInline) {
18596             return;
18597         }
18598         
18599         this.picker().removeClass(['bottom', 'top']);
18600         
18601         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18602             /*
18603              * place to the top of element!
18604              *
18605              */
18606             
18607             this.picker().addClass('top');
18608             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18609             
18610             return;
18611         }
18612         
18613         this.picker().addClass('bottom');
18614         
18615         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18616     },
18617     
18618     onFocus : function()
18619     {
18620         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18621         this.show();
18622     },
18623     
18624     onBlur : function()
18625     {
18626         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18627         
18628         var d = this.inputEl().getValue();
18629         
18630         this.setValue(d);
18631                 
18632         this.hide();
18633     },
18634     
18635     show : function()
18636     {
18637         this.picker().show();
18638         this.picker().select('>.datepicker-months', true).first().show();
18639         this.update();
18640         this.place();
18641         
18642         this.fireEvent('show', this, this.date);
18643     },
18644     
18645     hide : function()
18646     {
18647         if(this.isInline) {
18648             return;
18649         }
18650         this.picker().hide();
18651         this.fireEvent('hide', this, this.date);
18652         
18653     },
18654     
18655     onMousedown: function(e)
18656     {
18657         e.stopPropagation();
18658         e.preventDefault();
18659     },
18660     
18661     keyup: function(e)
18662     {
18663         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18664         this.update();
18665     },
18666
18667     fireKey: function(e)
18668     {
18669         if (!this.picker().isVisible()){
18670             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18671                 this.show();
18672             }
18673             return;
18674         }
18675         
18676         var dir;
18677         
18678         switch(e.keyCode){
18679             case 27: // escape
18680                 this.hide();
18681                 e.preventDefault();
18682                 break;
18683             case 37: // left
18684             case 39: // right
18685                 dir = e.keyCode == 37 ? -1 : 1;
18686                 
18687                 this.vIndex = this.vIndex + dir;
18688                 
18689                 if(this.vIndex < 0){
18690                     this.vIndex = 0;
18691                 }
18692                 
18693                 if(this.vIndex > 11){
18694                     this.vIndex = 11;
18695                 }
18696                 
18697                 if(isNaN(this.vIndex)){
18698                     this.vIndex = 0;
18699                 }
18700                 
18701                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18702                 
18703                 break;
18704             case 38: // up
18705             case 40: // down
18706                 
18707                 dir = e.keyCode == 38 ? -1 : 1;
18708                 
18709                 this.vIndex = this.vIndex + dir * 4;
18710                 
18711                 if(this.vIndex < 0){
18712                     this.vIndex = 0;
18713                 }
18714                 
18715                 if(this.vIndex > 11){
18716                     this.vIndex = 11;
18717                 }
18718                 
18719                 if(isNaN(this.vIndex)){
18720                     this.vIndex = 0;
18721                 }
18722                 
18723                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18724                 break;
18725                 
18726             case 13: // enter
18727                 
18728                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18729                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18730                 }
18731                 
18732                 this.hide();
18733                 e.preventDefault();
18734                 break;
18735             case 9: // tab
18736                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18737                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18738                 }
18739                 this.hide();
18740                 break;
18741             case 16: // shift
18742             case 17: // ctrl
18743             case 18: // alt
18744                 break;
18745             default :
18746                 this.hide();
18747                 
18748         }
18749     },
18750     
18751     remove: function() 
18752     {
18753         this.picker().remove();
18754     }
18755    
18756 });
18757
18758 Roo.apply(Roo.bootstrap.MonthField,  {
18759     
18760     content : {
18761         tag: 'tbody',
18762         cn: [
18763         {
18764             tag: 'tr',
18765             cn: [
18766             {
18767                 tag: 'td',
18768                 colspan: '7'
18769             }
18770             ]
18771         }
18772         ]
18773     },
18774     
18775     dates:{
18776         en: {
18777             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18778             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18779         }
18780     }
18781 });
18782
18783 Roo.apply(Roo.bootstrap.MonthField,  {
18784   
18785     template : {
18786         tag: 'div',
18787         cls: 'datepicker dropdown-menu roo-dynamic',
18788         cn: [
18789             {
18790                 tag: 'div',
18791                 cls: 'datepicker-months',
18792                 cn: [
18793                 {
18794                     tag: 'table',
18795                     cls: 'table-condensed',
18796                     cn:[
18797                         Roo.bootstrap.DateField.content
18798                     ]
18799                 }
18800                 ]
18801             }
18802         ]
18803     }
18804 });
18805
18806  
18807
18808  
18809  /*
18810  * - LGPL
18811  *
18812  * CheckBox
18813  * 
18814  */
18815
18816 /**
18817  * @class Roo.bootstrap.CheckBox
18818  * @extends Roo.bootstrap.Input
18819  * Bootstrap CheckBox class
18820  * 
18821  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18822  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18823  * @cfg {String} boxLabel The text that appears beside the checkbox
18824  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18825  * @cfg {Boolean} checked initnal the element
18826  * @cfg {Boolean} inline inline the element (default false)
18827  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18828  * 
18829  * @constructor
18830  * Create a new CheckBox
18831  * @param {Object} config The config object
18832  */
18833
18834 Roo.bootstrap.CheckBox = function(config){
18835     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18836    
18837     this.addEvents({
18838         /**
18839         * @event check
18840         * Fires when the element is checked or unchecked.
18841         * @param {Roo.bootstrap.CheckBox} this This input
18842         * @param {Boolean} checked The new checked value
18843         */
18844        check : true
18845     });
18846     
18847 };
18848
18849 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18850   
18851     inputType: 'checkbox',
18852     inputValue: 1,
18853     valueOff: 0,
18854     boxLabel: false,
18855     checked: false,
18856     weight : false,
18857     inline: false,
18858     
18859     getAutoCreate : function()
18860     {
18861         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18862         
18863         var id = Roo.id();
18864         
18865         var cfg = {};
18866         
18867         cfg.cls = 'form-group ' + this.inputType; //input-group
18868         
18869         if(this.inline){
18870             cfg.cls += ' ' + this.inputType + '-inline';
18871         }
18872         
18873         var input =  {
18874             tag: 'input',
18875             id : id,
18876             type : this.inputType,
18877             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18878             cls : 'roo-' + this.inputType, //'form-box',
18879             placeholder : this.placeholder || ''
18880             
18881         };
18882         
18883         if (this.weight) { // Validity check?
18884             cfg.cls += " " + this.inputType + "-" + this.weight;
18885         }
18886         
18887         if (this.disabled) {
18888             input.disabled=true;
18889         }
18890         
18891         if(this.checked){
18892             input.checked = this.checked;
18893         }
18894         
18895         if (this.name) {
18896             input.name = this.name;
18897         }
18898         
18899         if (this.size) {
18900             input.cls += ' input-' + this.size;
18901         }
18902         
18903         var settings=this;
18904         
18905         ['xs','sm','md','lg'].map(function(size){
18906             if (settings[size]) {
18907                 cfg.cls += ' col-' + size + '-' + settings[size];
18908             }
18909         });
18910         
18911         var inputblock = input;
18912          
18913         if (this.before || this.after) {
18914             
18915             inputblock = {
18916                 cls : 'input-group',
18917                 cn :  [] 
18918             };
18919             
18920             if (this.before) {
18921                 inputblock.cn.push({
18922                     tag :'span',
18923                     cls : 'input-group-addon',
18924                     html : this.before
18925                 });
18926             }
18927             
18928             inputblock.cn.push(input);
18929             
18930             if (this.after) {
18931                 inputblock.cn.push({
18932                     tag :'span',
18933                     cls : 'input-group-addon',
18934                     html : this.after
18935                 });
18936             }
18937             
18938         }
18939         
18940         if (align ==='left' && this.fieldLabel.length) {
18941 //                Roo.log("left and has label");
18942                 cfg.cn = [
18943                     
18944                     {
18945                         tag: 'label',
18946                         'for' :  id,
18947                         cls : 'control-label col-md-' + this.labelWidth,
18948                         html : this.fieldLabel
18949                         
18950                     },
18951                     {
18952                         cls : "col-md-" + (12 - this.labelWidth), 
18953                         cn: [
18954                             inputblock
18955                         ]
18956                     }
18957                     
18958                 ];
18959         } else if ( this.fieldLabel.length) {
18960 //                Roo.log(" label");
18961                 cfg.cn = [
18962                    
18963                     {
18964                         tag: this.boxLabel ? 'span' : 'label',
18965                         'for': id,
18966                         cls: 'control-label box-input-label',
18967                         //cls : 'input-group-addon',
18968                         html : this.fieldLabel
18969                         
18970                     },
18971                     
18972                     inputblock
18973                     
18974                 ];
18975
18976         } else {
18977             
18978 //                Roo.log(" no label && no align");
18979                 cfg.cn = [  inputblock ] ;
18980                 
18981                 
18982         }
18983         
18984         if(this.boxLabel){
18985              var boxLabelCfg = {
18986                 tag: 'label',
18987                 //'for': id, // box label is handled by onclick - so no for...
18988                 cls: 'box-label',
18989                 html: this.boxLabel
18990             };
18991             
18992             if(this.tooltip){
18993                 boxLabelCfg.tooltip = this.tooltip;
18994             }
18995              
18996             cfg.cn.push(boxLabelCfg);
18997         }
18998         
18999         
19000        
19001         return cfg;
19002         
19003     },
19004     
19005     /**
19006      * return the real input element.
19007      */
19008     inputEl: function ()
19009     {
19010         return this.el.select('input.roo-' + this.inputType,true).first();
19011     },
19012     
19013     labelEl: function()
19014     {
19015         return this.el.select('label.control-label',true).first();
19016     },
19017     /* depricated... */
19018     
19019     label: function()
19020     {
19021         return this.labelEl();
19022     },
19023     
19024     boxLabelEl: function()
19025     {
19026         return this.el.select('label.box-label',true).first();
19027     },
19028     
19029     initEvents : function()
19030     {
19031 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19032         
19033         this.inputEl().on('click', this.onClick,  this);
19034         
19035         if (this.boxLabel) { 
19036             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19037         }
19038         
19039         this.startValue = this.getValue();
19040         
19041         if(this.groupId){
19042             Roo.bootstrap.CheckBox.register(this);
19043         }
19044     },
19045     
19046     onClick : function()
19047     {   
19048         this.setChecked(!this.checked);
19049     },
19050     
19051     setChecked : function(state,suppressEvent)
19052     {
19053         this.startValue = this.getValue();
19054         
19055         if(this.inputType == 'radio'){
19056             
19057             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19058                 e.dom.checked = false;
19059             });
19060             
19061             this.inputEl().dom.checked = true;
19062             
19063             this.inputEl().dom.value = this.inputValue;
19064             
19065             if(suppressEvent !== true){
19066                 this.fireEvent('check', this, true);
19067             }
19068             
19069             this.validate();
19070             
19071             return;
19072         }
19073         
19074         this.checked = state;
19075         
19076         this.inputEl().dom.checked = state;
19077         
19078         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19079         
19080         if(suppressEvent !== true){
19081             this.fireEvent('check', this, state);
19082         }
19083         
19084         this.validate();
19085     },
19086     
19087     getValue : function()
19088     {
19089         if(this.inputType == 'radio'){
19090             return this.getGroupValue();
19091         }
19092         
19093         return this.inputEl().getValue();
19094         
19095     },
19096     
19097     getGroupValue : function()
19098     {
19099         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19100             return '';
19101         }
19102         
19103         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19104     },
19105     
19106     setValue : function(v,suppressEvent)
19107     {
19108         if(this.inputType == 'radio'){
19109             this.setGroupValue(v, suppressEvent);
19110             return;
19111         }
19112         
19113         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19114         
19115         this.validate();
19116     },
19117     
19118     setGroupValue : function(v, suppressEvent)
19119     {
19120         this.startValue = this.getValue();
19121         
19122         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19123             e.dom.checked = false;
19124             
19125             if(e.dom.value == v){
19126                 e.dom.checked = true;
19127             }
19128         });
19129         
19130         if(suppressEvent !== true){
19131             this.fireEvent('check', this, true);
19132         }
19133
19134         this.validate();
19135         
19136         return;
19137     },
19138     
19139     validate : function()
19140     {
19141         if(
19142                 this.disabled || 
19143                 (this.inputType == 'radio' && this.validateRadio()) ||
19144                 (this.inputType == 'checkbox' && this.validateCheckbox())
19145         ){
19146             this.markValid();
19147             return true;
19148         }
19149         
19150         this.markInvalid();
19151         return false;
19152     },
19153     
19154     validateRadio : function()
19155     {
19156         var valid = false;
19157         
19158         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19159             if(!e.dom.checked){
19160                 return;
19161             }
19162             
19163             valid = true;
19164             
19165             return false;
19166         });
19167         
19168         return valid;
19169     },
19170     
19171     validateCheckbox : function()
19172     {
19173         if(!this.groupId){
19174             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19175         }
19176         
19177         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19178         
19179         if(!group){
19180             return false;
19181         }
19182         
19183         var r = false;
19184         
19185         for(var i in group){
19186             if(r){
19187                 break;
19188             }
19189             
19190             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19191         }
19192         
19193         return r;
19194     },
19195     
19196     /**
19197      * Mark this field as valid
19198      */
19199     markValid : function()
19200     {
19201         if(this.allowBlank){
19202             return;
19203         }
19204         
19205         var _this = this;
19206         
19207         this.fireEvent('valid', this);
19208         
19209         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19210         
19211         if(this.groupId){
19212             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19213         }
19214         
19215         if(label){
19216             label.markValid();
19217         }
19218         
19219         if(this.inputType == 'radio'){
19220             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19221                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19222                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19223             });
19224             
19225             return;
19226         }
19227         
19228         if(!this.groupId){
19229             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19230             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19231             return;
19232         }
19233         
19234         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19235             
19236         if(!group){
19237             return;
19238         }
19239         
19240         for(var i in group){
19241             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19242             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19243         }
19244     },
19245     
19246      /**
19247      * Mark this field as invalid
19248      * @param {String} msg The validation message
19249      */
19250     markInvalid : function(msg)
19251     {
19252         if(this.allowBlank){
19253             return;
19254         }
19255         
19256         var _this = this;
19257         
19258         this.fireEvent('invalid', this, msg);
19259         
19260         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19261         
19262         if(this.groupId){
19263             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19264         }
19265         
19266         if(label){
19267             label.markInvalid();
19268         }
19269             
19270         if(this.inputType == 'radio'){
19271             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19272                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19273                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19274             });
19275             
19276             return;
19277         }
19278         
19279         if(!this.groupId){
19280             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19281             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19282             return;
19283         }
19284         
19285         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19286         
19287         if(!group){
19288             return;
19289         }
19290         
19291         for(var i in group){
19292             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19293             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19294         }
19295         
19296     }
19297     
19298 });
19299
19300 Roo.apply(Roo.bootstrap.CheckBox, {
19301     
19302     groups: {},
19303     
19304      /**
19305     * register a CheckBox Group
19306     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19307     */
19308     register : function(checkbox)
19309     {
19310         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19311             this.groups[checkbox.groupId] = {};
19312         }
19313         
19314         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19315             return;
19316         }
19317         
19318         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19319         
19320     },
19321     /**
19322     * fetch a CheckBox Group based on the group ID
19323     * @param {string} the group ID
19324     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19325     */
19326     get: function(groupId) {
19327         if (typeof(this.groups[groupId]) == 'undefined') {
19328             return false;
19329         }
19330         
19331         return this.groups[groupId] ;
19332     }
19333     
19334     
19335 });
19336 /*
19337  * - LGPL
19338  *
19339  * Radio
19340  *
19341  *
19342  * not inline
19343  *<div class="radio">
19344   <label>
19345     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19346     Option one is this and that&mdash;be sure to include why it's great
19347   </label>
19348 </div>
19349  *
19350  *
19351  *inline
19352  *<span>
19353  *<label class="radio-inline">fieldLabel</label>
19354  *<label class="radio-inline">
19355   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19356 </label>
19357 <span>
19358  * 
19359  * 
19360  */
19361
19362 /**
19363  * @class Roo.bootstrap.Radio
19364  * @extends Roo.bootstrap.CheckBox
19365  * Bootstrap Radio class
19366
19367  * @constructor
19368  * Create a new Radio
19369  * @param {Object} config The config object
19370  */
19371
19372 Roo.bootstrap.Radio = function(config){
19373     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19374    
19375 };
19376
19377 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19378     
19379     inputType: 'radio',
19380     inputValue: '',
19381     valueOff: '',
19382     
19383     getAutoCreate : function()
19384     {
19385         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19386         align = align || 'left'; // default...
19387         
19388         
19389         
19390         var id = Roo.id();
19391         
19392         var cfg = {
19393                 tag : this.inline ? 'span' : 'div',
19394                 cls : '',
19395                 cn : []
19396         };
19397         
19398         var inline = this.inline ? ' radio-inline' : '';
19399         
19400         var lbl = {
19401                 tag: 'label' ,
19402                 // does not need for, as we wrap the input with it..
19403                 'for' : id,
19404                 cls : 'control-label box-label' + inline,
19405                 cn : []
19406         };
19407         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19408         
19409         var fieldLabel = {
19410             tag: 'label' ,
19411             //cls : 'control-label' + inline,
19412             html : this.fieldLabel,
19413             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19414         };
19415         
19416  
19417         
19418         
19419         var input =  {
19420             tag: 'input',
19421             id : id,
19422             type : this.inputType,
19423             //value : (!this.checked) ? this.valueOff : this.inputValue,
19424             value : this.inputValue,
19425             cls : 'roo-radio',
19426             placeholder : this.placeholder || '' // ?? needed????
19427             
19428         };
19429         if (this.weight) { // Validity check?
19430             input.cls += " radio-" + this.weight;
19431         }
19432         if (this.disabled) {
19433             input.disabled=true;
19434         }
19435         
19436         if(this.checked){
19437             input.checked = this.checked;
19438         }
19439         
19440         if (this.name) {
19441             input.name = this.name;
19442         }
19443         
19444         if (this.size) {
19445             input.cls += ' input-' + this.size;
19446         }
19447         
19448         //?? can span's inline have a width??
19449         
19450         var settings=this;
19451         ['xs','sm','md','lg'].map(function(size){
19452             if (settings[size]) {
19453                 cfg.cls += ' col-' + size + '-' + settings[size];
19454             }
19455         });
19456         
19457         var inputblock = input;
19458         
19459         if (this.before || this.after) {
19460             
19461             inputblock = {
19462                 cls : 'input-group',
19463                 tag : 'span',
19464                 cn :  [] 
19465             };
19466             if (this.before) {
19467                 inputblock.cn.push({
19468                     tag :'span',
19469                     cls : 'input-group-addon',
19470                     html : this.before
19471                 });
19472             }
19473             inputblock.cn.push(input);
19474             if (this.after) {
19475                 inputblock.cn.push({
19476                     tag :'span',
19477                     cls : 'input-group-addon',
19478                     html : this.after
19479                 });
19480             }
19481             
19482         };
19483         
19484         
19485         if (this.fieldLabel && this.fieldLabel.length) {
19486             cfg.cn.push(fieldLabel);
19487         }
19488        
19489         // normal bootstrap puts the input inside the label.
19490         // however with our styled version - it has to go after the input.
19491        
19492         //lbl.cn.push(inputblock);
19493         
19494         var lblwrap =  {
19495             tag: 'span',
19496             cls: 'radio' + inline,
19497             cn: [
19498                 inputblock,
19499                 lbl
19500             ]
19501         };
19502         
19503         cfg.cn.push( lblwrap);
19504         
19505         if(this.boxLabel){
19506             lbl.cn.push({
19507                 tag: 'span',
19508                 html: this.boxLabel
19509             })
19510         }
19511          
19512         
19513         return cfg;
19514         
19515     },
19516     
19517     initEvents : function()
19518     {
19519 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19520         
19521         this.inputEl().on('click', this.onClick,  this);
19522         if (this.boxLabel) {
19523             //Roo.log('find label');
19524             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19525         }
19526         
19527     },
19528     
19529     inputEl: function ()
19530     {
19531         return this.el.select('input.roo-radio',true).first();
19532     },
19533     onClick : function()
19534     {   
19535         Roo.log("click");
19536         this.setChecked(true);
19537     },
19538     
19539     setChecked : function(state,suppressEvent)
19540     {
19541         if(state){
19542             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19543                 v.dom.checked = false;
19544             });
19545         }
19546         Roo.log(this.inputEl().dom);
19547         this.checked = state;
19548         this.inputEl().dom.checked = state;
19549         
19550         if(suppressEvent !== true){
19551             this.fireEvent('check', this, state);
19552         }
19553         
19554         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19555         
19556     },
19557     
19558     getGroupValue : function()
19559     {
19560         var value = '';
19561         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19562             if(v.dom.checked == true){
19563                 value = v.dom.value;
19564             }
19565         });
19566         
19567         return value;
19568     },
19569     
19570     /**
19571      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19572      * @return {Mixed} value The field value
19573      */
19574     getValue : function(){
19575         return this.getGroupValue();
19576     }
19577     
19578 });
19579
19580  
19581 //<script type="text/javascript">
19582
19583 /*
19584  * Based  Ext JS Library 1.1.1
19585  * Copyright(c) 2006-2007, Ext JS, LLC.
19586  * LGPL
19587  *
19588  */
19589  
19590 /**
19591  * @class Roo.HtmlEditorCore
19592  * @extends Roo.Component
19593  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19594  *
19595  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19596  */
19597
19598 Roo.HtmlEditorCore = function(config){
19599     
19600     
19601     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19602     
19603     
19604     this.addEvents({
19605         /**
19606          * @event initialize
19607          * Fires when the editor is fully initialized (including the iframe)
19608          * @param {Roo.HtmlEditorCore} this
19609          */
19610         initialize: true,
19611         /**
19612          * @event activate
19613          * Fires when the editor is first receives the focus. Any insertion must wait
19614          * until after this event.
19615          * @param {Roo.HtmlEditorCore} this
19616          */
19617         activate: true,
19618          /**
19619          * @event beforesync
19620          * Fires before the textarea is updated with content from the editor iframe. Return false
19621          * to cancel the sync.
19622          * @param {Roo.HtmlEditorCore} this
19623          * @param {String} html
19624          */
19625         beforesync: true,
19626          /**
19627          * @event beforepush
19628          * Fires before the iframe editor is updated with content from the textarea. Return false
19629          * to cancel the push.
19630          * @param {Roo.HtmlEditorCore} this
19631          * @param {String} html
19632          */
19633         beforepush: true,
19634          /**
19635          * @event sync
19636          * Fires when the textarea is updated with content from the editor iframe.
19637          * @param {Roo.HtmlEditorCore} this
19638          * @param {String} html
19639          */
19640         sync: true,
19641          /**
19642          * @event push
19643          * Fires when the iframe editor is updated with content from the textarea.
19644          * @param {Roo.HtmlEditorCore} this
19645          * @param {String} html
19646          */
19647         push: true,
19648         
19649         /**
19650          * @event editorevent
19651          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19652          * @param {Roo.HtmlEditorCore} this
19653          */
19654         editorevent: true
19655         
19656     });
19657     
19658     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19659     
19660     // defaults : white / black...
19661     this.applyBlacklists();
19662     
19663     
19664     
19665 };
19666
19667
19668 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19669
19670
19671      /**
19672      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19673      */
19674     
19675     owner : false,
19676     
19677      /**
19678      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19679      *                        Roo.resizable.
19680      */
19681     resizable : false,
19682      /**
19683      * @cfg {Number} height (in pixels)
19684      */   
19685     height: 300,
19686    /**
19687      * @cfg {Number} width (in pixels)
19688      */   
19689     width: 500,
19690     
19691     /**
19692      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19693      * 
19694      */
19695     stylesheets: false,
19696     
19697     // id of frame..
19698     frameId: false,
19699     
19700     // private properties
19701     validationEvent : false,
19702     deferHeight: true,
19703     initialized : false,
19704     activated : false,
19705     sourceEditMode : false,
19706     onFocus : Roo.emptyFn,
19707     iframePad:3,
19708     hideMode:'offsets',
19709     
19710     clearUp: true,
19711     
19712     // blacklist + whitelisted elements..
19713     black: false,
19714     white: false,
19715      
19716     
19717
19718     /**
19719      * Protected method that will not generally be called directly. It
19720      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19721      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19722      */
19723     getDocMarkup : function(){
19724         // body styles..
19725         var st = '';
19726         
19727         // inherit styels from page...?? 
19728         if (this.stylesheets === false) {
19729             
19730             Roo.get(document.head).select('style').each(function(node) {
19731                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19732             });
19733             
19734             Roo.get(document.head).select('link').each(function(node) { 
19735                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19736             });
19737             
19738         } else if (!this.stylesheets.length) {
19739                 // simple..
19740                 st = '<style type="text/css">' +
19741                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19742                    '</style>';
19743         } else { 
19744             
19745         }
19746         
19747         st +=  '<style type="text/css">' +
19748             'IMG { cursor: pointer } ' +
19749         '</style>';
19750
19751         
19752         return '<html><head>' + st  +
19753             //<style type="text/css">' +
19754             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19755             //'</style>' +
19756             ' </head><body class="roo-htmleditor-body"></body></html>';
19757     },
19758
19759     // private
19760     onRender : function(ct, position)
19761     {
19762         var _t = this;
19763         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19764         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19765         
19766         
19767         this.el.dom.style.border = '0 none';
19768         this.el.dom.setAttribute('tabIndex', -1);
19769         this.el.addClass('x-hidden hide');
19770         
19771         
19772         
19773         if(Roo.isIE){ // fix IE 1px bogus margin
19774             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19775         }
19776        
19777         
19778         this.frameId = Roo.id();
19779         
19780          
19781         
19782         var iframe = this.owner.wrap.createChild({
19783             tag: 'iframe',
19784             cls: 'form-control', // bootstrap..
19785             id: this.frameId,
19786             name: this.frameId,
19787             frameBorder : 'no',
19788             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19789         }, this.el
19790         );
19791         
19792         
19793         this.iframe = iframe.dom;
19794
19795          this.assignDocWin();
19796         
19797         this.doc.designMode = 'on';
19798        
19799         this.doc.open();
19800         this.doc.write(this.getDocMarkup());
19801         this.doc.close();
19802
19803         
19804         var task = { // must defer to wait for browser to be ready
19805             run : function(){
19806                 //console.log("run task?" + this.doc.readyState);
19807                 this.assignDocWin();
19808                 if(this.doc.body || this.doc.readyState == 'complete'){
19809                     try {
19810                         this.doc.designMode="on";
19811                     } catch (e) {
19812                         return;
19813                     }
19814                     Roo.TaskMgr.stop(task);
19815                     this.initEditor.defer(10, this);
19816                 }
19817             },
19818             interval : 10,
19819             duration: 10000,
19820             scope: this
19821         };
19822         Roo.TaskMgr.start(task);
19823
19824     },
19825
19826     // private
19827     onResize : function(w, h)
19828     {
19829          Roo.log('resize: ' +w + ',' + h );
19830         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19831         if(!this.iframe){
19832             return;
19833         }
19834         if(typeof w == 'number'){
19835             
19836             this.iframe.style.width = w + 'px';
19837         }
19838         if(typeof h == 'number'){
19839             
19840             this.iframe.style.height = h + 'px';
19841             if(this.doc){
19842                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19843             }
19844         }
19845         
19846     },
19847
19848     /**
19849      * Toggles the editor between standard and source edit mode.
19850      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19851      */
19852     toggleSourceEdit : function(sourceEditMode){
19853         
19854         this.sourceEditMode = sourceEditMode === true;
19855         
19856         if(this.sourceEditMode){
19857  
19858             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19859             
19860         }else{
19861             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19862             //this.iframe.className = '';
19863             this.deferFocus();
19864         }
19865         //this.setSize(this.owner.wrap.getSize());
19866         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19867     },
19868
19869     
19870   
19871
19872     /**
19873      * Protected method that will not generally be called directly. If you need/want
19874      * custom HTML cleanup, this is the method you should override.
19875      * @param {String} html The HTML to be cleaned
19876      * return {String} The cleaned HTML
19877      */
19878     cleanHtml : function(html){
19879         html = String(html);
19880         if(html.length > 5){
19881             if(Roo.isSafari){ // strip safari nonsense
19882                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19883             }
19884         }
19885         if(html == '&nbsp;'){
19886             html = '';
19887         }
19888         return html;
19889     },
19890
19891     /**
19892      * HTML Editor -> Textarea
19893      * Protected method that will not generally be called directly. Syncs the contents
19894      * of the editor iframe with the textarea.
19895      */
19896     syncValue : function(){
19897         if(this.initialized){
19898             var bd = (this.doc.body || this.doc.documentElement);
19899             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19900             var html = bd.innerHTML;
19901             if(Roo.isSafari){
19902                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19903                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19904                 if(m && m[1]){
19905                     html = '<div style="'+m[0]+'">' + html + '</div>';
19906                 }
19907             }
19908             html = this.cleanHtml(html);
19909             // fix up the special chars.. normaly like back quotes in word...
19910             // however we do not want to do this with chinese..
19911             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19912                 var cc = b.charCodeAt();
19913                 if (
19914                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19915                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19916                     (cc >= 0xf900 && cc < 0xfb00 )
19917                 ) {
19918                         return b;
19919                 }
19920                 return "&#"+cc+";" 
19921             });
19922             if(this.owner.fireEvent('beforesync', this, html) !== false){
19923                 this.el.dom.value = html;
19924                 this.owner.fireEvent('sync', this, html);
19925             }
19926         }
19927     },
19928
19929     /**
19930      * Protected method that will not generally be called directly. Pushes the value of the textarea
19931      * into the iframe editor.
19932      */
19933     pushValue : function(){
19934         if(this.initialized){
19935             var v = this.el.dom.value.trim();
19936             
19937 //            if(v.length < 1){
19938 //                v = '&#160;';
19939 //            }
19940             
19941             if(this.owner.fireEvent('beforepush', this, v) !== false){
19942                 var d = (this.doc.body || this.doc.documentElement);
19943                 d.innerHTML = v;
19944                 this.cleanUpPaste();
19945                 this.el.dom.value = d.innerHTML;
19946                 this.owner.fireEvent('push', this, v);
19947             }
19948         }
19949     },
19950
19951     // private
19952     deferFocus : function(){
19953         this.focus.defer(10, this);
19954     },
19955
19956     // doc'ed in Field
19957     focus : function(){
19958         if(this.win && !this.sourceEditMode){
19959             this.win.focus();
19960         }else{
19961             this.el.focus();
19962         }
19963     },
19964     
19965     assignDocWin: function()
19966     {
19967         var iframe = this.iframe;
19968         
19969          if(Roo.isIE){
19970             this.doc = iframe.contentWindow.document;
19971             this.win = iframe.contentWindow;
19972         } else {
19973 //            if (!Roo.get(this.frameId)) {
19974 //                return;
19975 //            }
19976 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19977 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19978             
19979             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19980                 return;
19981             }
19982             
19983             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19984             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19985         }
19986     },
19987     
19988     // private
19989     initEditor : function(){
19990         //console.log("INIT EDITOR");
19991         this.assignDocWin();
19992         
19993         
19994         
19995         this.doc.designMode="on";
19996         this.doc.open();
19997         this.doc.write(this.getDocMarkup());
19998         this.doc.close();
19999         
20000         var dbody = (this.doc.body || this.doc.documentElement);
20001         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20002         // this copies styles from the containing element into thsi one..
20003         // not sure why we need all of this..
20004         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20005         
20006         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20007         //ss['background-attachment'] = 'fixed'; // w3c
20008         dbody.bgProperties = 'fixed'; // ie
20009         //Roo.DomHelper.applyStyles(dbody, ss);
20010         Roo.EventManager.on(this.doc, {
20011             //'mousedown': this.onEditorEvent,
20012             'mouseup': this.onEditorEvent,
20013             'dblclick': this.onEditorEvent,
20014             'click': this.onEditorEvent,
20015             'keyup': this.onEditorEvent,
20016             buffer:100,
20017             scope: this
20018         });
20019         if(Roo.isGecko){
20020             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20021         }
20022         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20023             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20024         }
20025         this.initialized = true;
20026
20027         this.owner.fireEvent('initialize', this);
20028         this.pushValue();
20029     },
20030
20031     // private
20032     onDestroy : function(){
20033         
20034         
20035         
20036         if(this.rendered){
20037             
20038             //for (var i =0; i < this.toolbars.length;i++) {
20039             //    // fixme - ask toolbars for heights?
20040             //    this.toolbars[i].onDestroy();
20041            // }
20042             
20043             //this.wrap.dom.innerHTML = '';
20044             //this.wrap.remove();
20045         }
20046     },
20047
20048     // private
20049     onFirstFocus : function(){
20050         
20051         this.assignDocWin();
20052         
20053         
20054         this.activated = true;
20055          
20056     
20057         if(Roo.isGecko){ // prevent silly gecko errors
20058             this.win.focus();
20059             var s = this.win.getSelection();
20060             if(!s.focusNode || s.focusNode.nodeType != 3){
20061                 var r = s.getRangeAt(0);
20062                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20063                 r.collapse(true);
20064                 this.deferFocus();
20065             }
20066             try{
20067                 this.execCmd('useCSS', true);
20068                 this.execCmd('styleWithCSS', false);
20069             }catch(e){}
20070         }
20071         this.owner.fireEvent('activate', this);
20072     },
20073
20074     // private
20075     adjustFont: function(btn){
20076         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20077         //if(Roo.isSafari){ // safari
20078         //    adjust *= 2;
20079        // }
20080         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20081         if(Roo.isSafari){ // safari
20082             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20083             v =  (v < 10) ? 10 : v;
20084             v =  (v > 48) ? 48 : v;
20085             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20086             
20087         }
20088         
20089         
20090         v = Math.max(1, v+adjust);
20091         
20092         this.execCmd('FontSize', v  );
20093     },
20094
20095     onEditorEvent : function(e)
20096     {
20097         this.owner.fireEvent('editorevent', this, e);
20098       //  this.updateToolbar();
20099         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20100     },
20101
20102     insertTag : function(tg)
20103     {
20104         // could be a bit smarter... -> wrap the current selected tRoo..
20105         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20106             
20107             range = this.createRange(this.getSelection());
20108             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20109             wrappingNode.appendChild(range.extractContents());
20110             range.insertNode(wrappingNode);
20111
20112             return;
20113             
20114             
20115             
20116         }
20117         this.execCmd("formatblock",   tg);
20118         
20119     },
20120     
20121     insertText : function(txt)
20122     {
20123         
20124         
20125         var range = this.createRange();
20126         range.deleteContents();
20127                //alert(Sender.getAttribute('label'));
20128                
20129         range.insertNode(this.doc.createTextNode(txt));
20130     } ,
20131     
20132      
20133
20134     /**
20135      * Executes a Midas editor command on the editor document and performs necessary focus and
20136      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20137      * @param {String} cmd The Midas command
20138      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20139      */
20140     relayCmd : function(cmd, value){
20141         this.win.focus();
20142         this.execCmd(cmd, value);
20143         this.owner.fireEvent('editorevent', this);
20144         //this.updateToolbar();
20145         this.owner.deferFocus();
20146     },
20147
20148     /**
20149      * Executes a Midas editor command directly on the editor document.
20150      * For visual commands, you should use {@link #relayCmd} instead.
20151      * <b>This should only be called after the editor is initialized.</b>
20152      * @param {String} cmd The Midas command
20153      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20154      */
20155     execCmd : function(cmd, value){
20156         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20157         this.syncValue();
20158     },
20159  
20160  
20161    
20162     /**
20163      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20164      * to insert tRoo.
20165      * @param {String} text | dom node.. 
20166      */
20167     insertAtCursor : function(text)
20168     {
20169         
20170         
20171         
20172         if(!this.activated){
20173             return;
20174         }
20175         /*
20176         if(Roo.isIE){
20177             this.win.focus();
20178             var r = this.doc.selection.createRange();
20179             if(r){
20180                 r.collapse(true);
20181                 r.pasteHTML(text);
20182                 this.syncValue();
20183                 this.deferFocus();
20184             
20185             }
20186             return;
20187         }
20188         */
20189         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20190             this.win.focus();
20191             
20192             
20193             // from jquery ui (MIT licenced)
20194             var range, node;
20195             var win = this.win;
20196             
20197             if (win.getSelection && win.getSelection().getRangeAt) {
20198                 range = win.getSelection().getRangeAt(0);
20199                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20200                 range.insertNode(node);
20201             } else if (win.document.selection && win.document.selection.createRange) {
20202                 // no firefox support
20203                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20204                 win.document.selection.createRange().pasteHTML(txt);
20205             } else {
20206                 // no firefox support
20207                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20208                 this.execCmd('InsertHTML', txt);
20209             } 
20210             
20211             this.syncValue();
20212             
20213             this.deferFocus();
20214         }
20215     },
20216  // private
20217     mozKeyPress : function(e){
20218         if(e.ctrlKey){
20219             var c = e.getCharCode(), cmd;
20220           
20221             if(c > 0){
20222                 c = String.fromCharCode(c).toLowerCase();
20223                 switch(c){
20224                     case 'b':
20225                         cmd = 'bold';
20226                         break;
20227                     case 'i':
20228                         cmd = 'italic';
20229                         break;
20230                     
20231                     case 'u':
20232                         cmd = 'underline';
20233                         break;
20234                     
20235                     case 'v':
20236                         this.cleanUpPaste.defer(100, this);
20237                         return;
20238                         
20239                 }
20240                 if(cmd){
20241                     this.win.focus();
20242                     this.execCmd(cmd);
20243                     this.deferFocus();
20244                     e.preventDefault();
20245                 }
20246                 
20247             }
20248         }
20249     },
20250
20251     // private
20252     fixKeys : function(){ // load time branching for fastest keydown performance
20253         if(Roo.isIE){
20254             return function(e){
20255                 var k = e.getKey(), r;
20256                 if(k == e.TAB){
20257                     e.stopEvent();
20258                     r = this.doc.selection.createRange();
20259                     if(r){
20260                         r.collapse(true);
20261                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20262                         this.deferFocus();
20263                     }
20264                     return;
20265                 }
20266                 
20267                 if(k == e.ENTER){
20268                     r = this.doc.selection.createRange();
20269                     if(r){
20270                         var target = r.parentElement();
20271                         if(!target || target.tagName.toLowerCase() != 'li'){
20272                             e.stopEvent();
20273                             r.pasteHTML('<br />');
20274                             r.collapse(false);
20275                             r.select();
20276                         }
20277                     }
20278                 }
20279                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20280                     this.cleanUpPaste.defer(100, this);
20281                     return;
20282                 }
20283                 
20284                 
20285             };
20286         }else if(Roo.isOpera){
20287             return function(e){
20288                 var k = e.getKey();
20289                 if(k == e.TAB){
20290                     e.stopEvent();
20291                     this.win.focus();
20292                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20293                     this.deferFocus();
20294                 }
20295                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20296                     this.cleanUpPaste.defer(100, this);
20297                     return;
20298                 }
20299                 
20300             };
20301         }else if(Roo.isSafari){
20302             return function(e){
20303                 var k = e.getKey();
20304                 
20305                 if(k == e.TAB){
20306                     e.stopEvent();
20307                     this.execCmd('InsertText','\t');
20308                     this.deferFocus();
20309                     return;
20310                 }
20311                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20312                     this.cleanUpPaste.defer(100, this);
20313                     return;
20314                 }
20315                 
20316              };
20317         }
20318     }(),
20319     
20320     getAllAncestors: function()
20321     {
20322         var p = this.getSelectedNode();
20323         var a = [];
20324         if (!p) {
20325             a.push(p); // push blank onto stack..
20326             p = this.getParentElement();
20327         }
20328         
20329         
20330         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20331             a.push(p);
20332             p = p.parentNode;
20333         }
20334         a.push(this.doc.body);
20335         return a;
20336     },
20337     lastSel : false,
20338     lastSelNode : false,
20339     
20340     
20341     getSelection : function() 
20342     {
20343         this.assignDocWin();
20344         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20345     },
20346     
20347     getSelectedNode: function() 
20348     {
20349         // this may only work on Gecko!!!
20350         
20351         // should we cache this!!!!
20352         
20353         
20354         
20355          
20356         var range = this.createRange(this.getSelection()).cloneRange();
20357         
20358         if (Roo.isIE) {
20359             var parent = range.parentElement();
20360             while (true) {
20361                 var testRange = range.duplicate();
20362                 testRange.moveToElementText(parent);
20363                 if (testRange.inRange(range)) {
20364                     break;
20365                 }
20366                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20367                     break;
20368                 }
20369                 parent = parent.parentElement;
20370             }
20371             return parent;
20372         }
20373         
20374         // is ancestor a text element.
20375         var ac =  range.commonAncestorContainer;
20376         if (ac.nodeType == 3) {
20377             ac = ac.parentNode;
20378         }
20379         
20380         var ar = ac.childNodes;
20381          
20382         var nodes = [];
20383         var other_nodes = [];
20384         var has_other_nodes = false;
20385         for (var i=0;i<ar.length;i++) {
20386             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20387                 continue;
20388             }
20389             // fullly contained node.
20390             
20391             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20392                 nodes.push(ar[i]);
20393                 continue;
20394             }
20395             
20396             // probably selected..
20397             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20398                 other_nodes.push(ar[i]);
20399                 continue;
20400             }
20401             // outer..
20402             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20403                 continue;
20404             }
20405             
20406             
20407             has_other_nodes = true;
20408         }
20409         if (!nodes.length && other_nodes.length) {
20410             nodes= other_nodes;
20411         }
20412         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20413             return false;
20414         }
20415         
20416         return nodes[0];
20417     },
20418     createRange: function(sel)
20419     {
20420         // this has strange effects when using with 
20421         // top toolbar - not sure if it's a great idea.
20422         //this.editor.contentWindow.focus();
20423         if (typeof sel != "undefined") {
20424             try {
20425                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20426             } catch(e) {
20427                 return this.doc.createRange();
20428             }
20429         } else {
20430             return this.doc.createRange();
20431         }
20432     },
20433     getParentElement: function()
20434     {
20435         
20436         this.assignDocWin();
20437         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20438         
20439         var range = this.createRange(sel);
20440          
20441         try {
20442             var p = range.commonAncestorContainer;
20443             while (p.nodeType == 3) { // text node
20444                 p = p.parentNode;
20445             }
20446             return p;
20447         } catch (e) {
20448             return null;
20449         }
20450     
20451     },
20452     /***
20453      *
20454      * Range intersection.. the hard stuff...
20455      *  '-1' = before
20456      *  '0' = hits..
20457      *  '1' = after.
20458      *         [ -- selected range --- ]
20459      *   [fail]                        [fail]
20460      *
20461      *    basically..
20462      *      if end is before start or  hits it. fail.
20463      *      if start is after end or hits it fail.
20464      *
20465      *   if either hits (but other is outside. - then it's not 
20466      *   
20467      *    
20468      **/
20469     
20470     
20471     // @see http://www.thismuchiknow.co.uk/?p=64.
20472     rangeIntersectsNode : function(range, node)
20473     {
20474         var nodeRange = node.ownerDocument.createRange();
20475         try {
20476             nodeRange.selectNode(node);
20477         } catch (e) {
20478             nodeRange.selectNodeContents(node);
20479         }
20480     
20481         var rangeStartRange = range.cloneRange();
20482         rangeStartRange.collapse(true);
20483     
20484         var rangeEndRange = range.cloneRange();
20485         rangeEndRange.collapse(false);
20486     
20487         var nodeStartRange = nodeRange.cloneRange();
20488         nodeStartRange.collapse(true);
20489     
20490         var nodeEndRange = nodeRange.cloneRange();
20491         nodeEndRange.collapse(false);
20492     
20493         return rangeStartRange.compareBoundaryPoints(
20494                  Range.START_TO_START, nodeEndRange) == -1 &&
20495                rangeEndRange.compareBoundaryPoints(
20496                  Range.START_TO_START, nodeStartRange) == 1;
20497         
20498          
20499     },
20500     rangeCompareNode : function(range, node)
20501     {
20502         var nodeRange = node.ownerDocument.createRange();
20503         try {
20504             nodeRange.selectNode(node);
20505         } catch (e) {
20506             nodeRange.selectNodeContents(node);
20507         }
20508         
20509         
20510         range.collapse(true);
20511     
20512         nodeRange.collapse(true);
20513      
20514         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20515         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20516          
20517         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20518         
20519         var nodeIsBefore   =  ss == 1;
20520         var nodeIsAfter    = ee == -1;
20521         
20522         if (nodeIsBefore && nodeIsAfter) {
20523             return 0; // outer
20524         }
20525         if (!nodeIsBefore && nodeIsAfter) {
20526             return 1; //right trailed.
20527         }
20528         
20529         if (nodeIsBefore && !nodeIsAfter) {
20530             return 2;  // left trailed.
20531         }
20532         // fully contined.
20533         return 3;
20534     },
20535
20536     // private? - in a new class?
20537     cleanUpPaste :  function()
20538     {
20539         // cleans up the whole document..
20540         Roo.log('cleanuppaste');
20541         
20542         this.cleanUpChildren(this.doc.body);
20543         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20544         if (clean != this.doc.body.innerHTML) {
20545             this.doc.body.innerHTML = clean;
20546         }
20547         
20548     },
20549     
20550     cleanWordChars : function(input) {// change the chars to hex code
20551         var he = Roo.HtmlEditorCore;
20552         
20553         var output = input;
20554         Roo.each(he.swapCodes, function(sw) { 
20555             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20556             
20557             output = output.replace(swapper, sw[1]);
20558         });
20559         
20560         return output;
20561     },
20562     
20563     
20564     cleanUpChildren : function (n)
20565     {
20566         if (!n.childNodes.length) {
20567             return;
20568         }
20569         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20570            this.cleanUpChild(n.childNodes[i]);
20571         }
20572     },
20573     
20574     
20575         
20576     
20577     cleanUpChild : function (node)
20578     {
20579         var ed = this;
20580         //console.log(node);
20581         if (node.nodeName == "#text") {
20582             // clean up silly Windows -- stuff?
20583             return; 
20584         }
20585         if (node.nodeName == "#comment") {
20586             node.parentNode.removeChild(node);
20587             // clean up silly Windows -- stuff?
20588             return; 
20589         }
20590         var lcname = node.tagName.toLowerCase();
20591         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20592         // whitelist of tags..
20593         
20594         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20595             // remove node.
20596             node.parentNode.removeChild(node);
20597             return;
20598             
20599         }
20600         
20601         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20602         
20603         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20604         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20605         
20606         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20607         //    remove_keep_children = true;
20608         //}
20609         
20610         if (remove_keep_children) {
20611             this.cleanUpChildren(node);
20612             // inserts everything just before this node...
20613             while (node.childNodes.length) {
20614                 var cn = node.childNodes[0];
20615                 node.removeChild(cn);
20616                 node.parentNode.insertBefore(cn, node);
20617             }
20618             node.parentNode.removeChild(node);
20619             return;
20620         }
20621         
20622         if (!node.attributes || !node.attributes.length) {
20623             this.cleanUpChildren(node);
20624             return;
20625         }
20626         
20627         function cleanAttr(n,v)
20628         {
20629             
20630             if (v.match(/^\./) || v.match(/^\//)) {
20631                 return;
20632             }
20633             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20634                 return;
20635             }
20636             if (v.match(/^#/)) {
20637                 return;
20638             }
20639 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20640             node.removeAttribute(n);
20641             
20642         }
20643         
20644         var cwhite = this.cwhite;
20645         var cblack = this.cblack;
20646             
20647         function cleanStyle(n,v)
20648         {
20649             if (v.match(/expression/)) { //XSS?? should we even bother..
20650                 node.removeAttribute(n);
20651                 return;
20652             }
20653             
20654             var parts = v.split(/;/);
20655             var clean = [];
20656             
20657             Roo.each(parts, function(p) {
20658                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20659                 if (!p.length) {
20660                     return true;
20661                 }
20662                 var l = p.split(':').shift().replace(/\s+/g,'');
20663                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20664                 
20665                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20666 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20667                     //node.removeAttribute(n);
20668                     return true;
20669                 }
20670                 //Roo.log()
20671                 // only allow 'c whitelisted system attributes'
20672                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20673 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20674                     //node.removeAttribute(n);
20675                     return true;
20676                 }
20677                 
20678                 
20679                  
20680                 
20681                 clean.push(p);
20682                 return true;
20683             });
20684             if (clean.length) { 
20685                 node.setAttribute(n, clean.join(';'));
20686             } else {
20687                 node.removeAttribute(n);
20688             }
20689             
20690         }
20691         
20692         
20693         for (var i = node.attributes.length-1; i > -1 ; i--) {
20694             var a = node.attributes[i];
20695             //console.log(a);
20696             
20697             if (a.name.toLowerCase().substr(0,2)=='on')  {
20698                 node.removeAttribute(a.name);
20699                 continue;
20700             }
20701             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20702                 node.removeAttribute(a.name);
20703                 continue;
20704             }
20705             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20706                 cleanAttr(a.name,a.value); // fixme..
20707                 continue;
20708             }
20709             if (a.name == 'style') {
20710                 cleanStyle(a.name,a.value);
20711                 continue;
20712             }
20713             /// clean up MS crap..
20714             // tecnically this should be a list of valid class'es..
20715             
20716             
20717             if (a.name == 'class') {
20718                 if (a.value.match(/^Mso/)) {
20719                     node.className = '';
20720                 }
20721                 
20722                 if (a.value.match(/body/)) {
20723                     node.className = '';
20724                 }
20725                 continue;
20726             }
20727             
20728             // style cleanup!?
20729             // class cleanup?
20730             
20731         }
20732         
20733         
20734         this.cleanUpChildren(node);
20735         
20736         
20737     },
20738     
20739     /**
20740      * Clean up MS wordisms...
20741      */
20742     cleanWord : function(node)
20743     {
20744         
20745         
20746         if (!node) {
20747             this.cleanWord(this.doc.body);
20748             return;
20749         }
20750         if (node.nodeName == "#text") {
20751             // clean up silly Windows -- stuff?
20752             return; 
20753         }
20754         if (node.nodeName == "#comment") {
20755             node.parentNode.removeChild(node);
20756             // clean up silly Windows -- stuff?
20757             return; 
20758         }
20759         
20760         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20761             node.parentNode.removeChild(node);
20762             return;
20763         }
20764         
20765         // remove - but keep children..
20766         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20767             while (node.childNodes.length) {
20768                 var cn = node.childNodes[0];
20769                 node.removeChild(cn);
20770                 node.parentNode.insertBefore(cn, node);
20771             }
20772             node.parentNode.removeChild(node);
20773             this.iterateChildren(node, this.cleanWord);
20774             return;
20775         }
20776         // clean styles
20777         if (node.className.length) {
20778             
20779             var cn = node.className.split(/\W+/);
20780             var cna = [];
20781             Roo.each(cn, function(cls) {
20782                 if (cls.match(/Mso[a-zA-Z]+/)) {
20783                     return;
20784                 }
20785                 cna.push(cls);
20786             });
20787             node.className = cna.length ? cna.join(' ') : '';
20788             if (!cna.length) {
20789                 node.removeAttribute("class");
20790             }
20791         }
20792         
20793         if (node.hasAttribute("lang")) {
20794             node.removeAttribute("lang");
20795         }
20796         
20797         if (node.hasAttribute("style")) {
20798             
20799             var styles = node.getAttribute("style").split(";");
20800             var nstyle = [];
20801             Roo.each(styles, function(s) {
20802                 if (!s.match(/:/)) {
20803                     return;
20804                 }
20805                 var kv = s.split(":");
20806                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20807                     return;
20808                 }
20809                 // what ever is left... we allow.
20810                 nstyle.push(s);
20811             });
20812             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20813             if (!nstyle.length) {
20814                 node.removeAttribute('style');
20815             }
20816         }
20817         this.iterateChildren(node, this.cleanWord);
20818         
20819         
20820         
20821     },
20822     /**
20823      * iterateChildren of a Node, calling fn each time, using this as the scole..
20824      * @param {DomNode} node node to iterate children of.
20825      * @param {Function} fn method of this class to call on each item.
20826      */
20827     iterateChildren : function(node, fn)
20828     {
20829         if (!node.childNodes.length) {
20830                 return;
20831         }
20832         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20833            fn.call(this, node.childNodes[i])
20834         }
20835     },
20836     
20837     
20838     /**
20839      * cleanTableWidths.
20840      *
20841      * Quite often pasting from word etc.. results in tables with column and widths.
20842      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20843      *
20844      */
20845     cleanTableWidths : function(node)
20846     {
20847          
20848          
20849         if (!node) {
20850             this.cleanTableWidths(this.doc.body);
20851             return;
20852         }
20853         
20854         // ignore list...
20855         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20856             return; 
20857         }
20858         Roo.log(node.tagName);
20859         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20860             this.iterateChildren(node, this.cleanTableWidths);
20861             return;
20862         }
20863         if (node.hasAttribute('width')) {
20864             node.removeAttribute('width');
20865         }
20866         
20867          
20868         if (node.hasAttribute("style")) {
20869             // pretty basic...
20870             
20871             var styles = node.getAttribute("style").split(";");
20872             var nstyle = [];
20873             Roo.each(styles, function(s) {
20874                 if (!s.match(/:/)) {
20875                     return;
20876                 }
20877                 var kv = s.split(":");
20878                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20879                     return;
20880                 }
20881                 // what ever is left... we allow.
20882                 nstyle.push(s);
20883             });
20884             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20885             if (!nstyle.length) {
20886                 node.removeAttribute('style');
20887             }
20888         }
20889         
20890         this.iterateChildren(node, this.cleanTableWidths);
20891         
20892         
20893     },
20894     
20895     
20896     
20897     
20898     domToHTML : function(currentElement, depth, nopadtext) {
20899         
20900         depth = depth || 0;
20901         nopadtext = nopadtext || false;
20902     
20903         if (!currentElement) {
20904             return this.domToHTML(this.doc.body);
20905         }
20906         
20907         //Roo.log(currentElement);
20908         var j;
20909         var allText = false;
20910         var nodeName = currentElement.nodeName;
20911         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20912         
20913         if  (nodeName == '#text') {
20914             
20915             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20916         }
20917         
20918         
20919         var ret = '';
20920         if (nodeName != 'BODY') {
20921              
20922             var i = 0;
20923             // Prints the node tagName, such as <A>, <IMG>, etc
20924             if (tagName) {
20925                 var attr = [];
20926                 for(i = 0; i < currentElement.attributes.length;i++) {
20927                     // quoting?
20928                     var aname = currentElement.attributes.item(i).name;
20929                     if (!currentElement.attributes.item(i).value.length) {
20930                         continue;
20931                     }
20932                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20933                 }
20934                 
20935                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20936             } 
20937             else {
20938                 
20939                 // eack
20940             }
20941         } else {
20942             tagName = false;
20943         }
20944         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20945             return ret;
20946         }
20947         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20948             nopadtext = true;
20949         }
20950         
20951         
20952         // Traverse the tree
20953         i = 0;
20954         var currentElementChild = currentElement.childNodes.item(i);
20955         var allText = true;
20956         var innerHTML  = '';
20957         lastnode = '';
20958         while (currentElementChild) {
20959             // Formatting code (indent the tree so it looks nice on the screen)
20960             var nopad = nopadtext;
20961             if (lastnode == 'SPAN') {
20962                 nopad  = true;
20963             }
20964             // text
20965             if  (currentElementChild.nodeName == '#text') {
20966                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20967                 toadd = nopadtext ? toadd : toadd.trim();
20968                 if (!nopad && toadd.length > 80) {
20969                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20970                 }
20971                 innerHTML  += toadd;
20972                 
20973                 i++;
20974                 currentElementChild = currentElement.childNodes.item(i);
20975                 lastNode = '';
20976                 continue;
20977             }
20978             allText = false;
20979             
20980             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20981                 
20982             // Recursively traverse the tree structure of the child node
20983             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20984             lastnode = currentElementChild.nodeName;
20985             i++;
20986             currentElementChild=currentElement.childNodes.item(i);
20987         }
20988         
20989         ret += innerHTML;
20990         
20991         if (!allText) {
20992                 // The remaining code is mostly for formatting the tree
20993             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20994         }
20995         
20996         
20997         if (tagName) {
20998             ret+= "</"+tagName+">";
20999         }
21000         return ret;
21001         
21002     },
21003         
21004     applyBlacklists : function()
21005     {
21006         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21007         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21008         
21009         this.white = [];
21010         this.black = [];
21011         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21012             if (b.indexOf(tag) > -1) {
21013                 return;
21014             }
21015             this.white.push(tag);
21016             
21017         }, this);
21018         
21019         Roo.each(w, function(tag) {
21020             if (b.indexOf(tag) > -1) {
21021                 return;
21022             }
21023             if (this.white.indexOf(tag) > -1) {
21024                 return;
21025             }
21026             this.white.push(tag);
21027             
21028         }, this);
21029         
21030         
21031         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21032             if (w.indexOf(tag) > -1) {
21033                 return;
21034             }
21035             this.black.push(tag);
21036             
21037         }, this);
21038         
21039         Roo.each(b, function(tag) {
21040             if (w.indexOf(tag) > -1) {
21041                 return;
21042             }
21043             if (this.black.indexOf(tag) > -1) {
21044                 return;
21045             }
21046             this.black.push(tag);
21047             
21048         }, this);
21049         
21050         
21051         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21052         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21053         
21054         this.cwhite = [];
21055         this.cblack = [];
21056         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21057             if (b.indexOf(tag) > -1) {
21058                 return;
21059             }
21060             this.cwhite.push(tag);
21061             
21062         }, this);
21063         
21064         Roo.each(w, function(tag) {
21065             if (b.indexOf(tag) > -1) {
21066                 return;
21067             }
21068             if (this.cwhite.indexOf(tag) > -1) {
21069                 return;
21070             }
21071             this.cwhite.push(tag);
21072             
21073         }, this);
21074         
21075         
21076         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21077             if (w.indexOf(tag) > -1) {
21078                 return;
21079             }
21080             this.cblack.push(tag);
21081             
21082         }, this);
21083         
21084         Roo.each(b, function(tag) {
21085             if (w.indexOf(tag) > -1) {
21086                 return;
21087             }
21088             if (this.cblack.indexOf(tag) > -1) {
21089                 return;
21090             }
21091             this.cblack.push(tag);
21092             
21093         }, this);
21094     },
21095     
21096     setStylesheets : function(stylesheets)
21097     {
21098         if(typeof(stylesheets) == 'string'){
21099             Roo.get(this.iframe.contentDocument.head).createChild({
21100                 tag : 'link',
21101                 rel : 'stylesheet',
21102                 type : 'text/css',
21103                 href : stylesheets
21104             });
21105             
21106             return;
21107         }
21108         var _this = this;
21109      
21110         Roo.each(stylesheets, function(s) {
21111             if(!s.length){
21112                 return;
21113             }
21114             
21115             Roo.get(_this.iframe.contentDocument.head).createChild({
21116                 tag : 'link',
21117                 rel : 'stylesheet',
21118                 type : 'text/css',
21119                 href : s
21120             });
21121         });
21122
21123         
21124     },
21125     
21126     removeStylesheets : function()
21127     {
21128         var _this = this;
21129         
21130         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21131             s.remove();
21132         });
21133     }
21134     
21135     // hide stuff that is not compatible
21136     /**
21137      * @event blur
21138      * @hide
21139      */
21140     /**
21141      * @event change
21142      * @hide
21143      */
21144     /**
21145      * @event focus
21146      * @hide
21147      */
21148     /**
21149      * @event specialkey
21150      * @hide
21151      */
21152     /**
21153      * @cfg {String} fieldClass @hide
21154      */
21155     /**
21156      * @cfg {String} focusClass @hide
21157      */
21158     /**
21159      * @cfg {String} autoCreate @hide
21160      */
21161     /**
21162      * @cfg {String} inputType @hide
21163      */
21164     /**
21165      * @cfg {String} invalidClass @hide
21166      */
21167     /**
21168      * @cfg {String} invalidText @hide
21169      */
21170     /**
21171      * @cfg {String} msgFx @hide
21172      */
21173     /**
21174      * @cfg {String} validateOnBlur @hide
21175      */
21176 });
21177
21178 Roo.HtmlEditorCore.white = [
21179         'area', 'br', 'img', 'input', 'hr', 'wbr',
21180         
21181        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21182        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21183        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21184        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21185        'table',   'ul',         'xmp', 
21186        
21187        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21188       'thead',   'tr', 
21189      
21190       'dir', 'menu', 'ol', 'ul', 'dl',
21191        
21192       'embed',  'object'
21193 ];
21194
21195
21196 Roo.HtmlEditorCore.black = [
21197     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21198         'applet', // 
21199         'base',   'basefont', 'bgsound', 'blink',  'body', 
21200         'frame',  'frameset', 'head',    'html',   'ilayer', 
21201         'iframe', 'layer',  'link',     'meta',    'object',   
21202         'script', 'style' ,'title',  'xml' // clean later..
21203 ];
21204 Roo.HtmlEditorCore.clean = [
21205     'script', 'style', 'title', 'xml'
21206 ];
21207 Roo.HtmlEditorCore.remove = [
21208     'font'
21209 ];
21210 // attributes..
21211
21212 Roo.HtmlEditorCore.ablack = [
21213     'on'
21214 ];
21215     
21216 Roo.HtmlEditorCore.aclean = [ 
21217     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21218 ];
21219
21220 // protocols..
21221 Roo.HtmlEditorCore.pwhite= [
21222         'http',  'https',  'mailto'
21223 ];
21224
21225 // white listed style attributes.
21226 Roo.HtmlEditorCore.cwhite= [
21227       //  'text-align', /// default is to allow most things..
21228       
21229          
21230 //        'font-size'//??
21231 ];
21232
21233 // black listed style attributes.
21234 Roo.HtmlEditorCore.cblack= [
21235       //  'font-size' -- this can be set by the project 
21236 ];
21237
21238
21239 Roo.HtmlEditorCore.swapCodes   =[ 
21240     [    8211, "--" ], 
21241     [    8212, "--" ], 
21242     [    8216,  "'" ],  
21243     [    8217, "'" ],  
21244     [    8220, '"' ],  
21245     [    8221, '"' ],  
21246     [    8226, "*" ],  
21247     [    8230, "..." ]
21248 ]; 
21249
21250     /*
21251  * - LGPL
21252  *
21253  * HtmlEditor
21254  * 
21255  */
21256
21257 /**
21258  * @class Roo.bootstrap.HtmlEditor
21259  * @extends Roo.bootstrap.TextArea
21260  * Bootstrap HtmlEditor class
21261
21262  * @constructor
21263  * Create a new HtmlEditor
21264  * @param {Object} config The config object
21265  */
21266
21267 Roo.bootstrap.HtmlEditor = function(config){
21268     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21269     if (!this.toolbars) {
21270         this.toolbars = [];
21271     }
21272     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21273     this.addEvents({
21274             /**
21275              * @event initialize
21276              * Fires when the editor is fully initialized (including the iframe)
21277              * @param {HtmlEditor} this
21278              */
21279             initialize: true,
21280             /**
21281              * @event activate
21282              * Fires when the editor is first receives the focus. Any insertion must wait
21283              * until after this event.
21284              * @param {HtmlEditor} this
21285              */
21286             activate: true,
21287              /**
21288              * @event beforesync
21289              * Fires before the textarea is updated with content from the editor iframe. Return false
21290              * to cancel the sync.
21291              * @param {HtmlEditor} this
21292              * @param {String} html
21293              */
21294             beforesync: true,
21295              /**
21296              * @event beforepush
21297              * Fires before the iframe editor is updated with content from the textarea. Return false
21298              * to cancel the push.
21299              * @param {HtmlEditor} this
21300              * @param {String} html
21301              */
21302             beforepush: true,
21303              /**
21304              * @event sync
21305              * Fires when the textarea is updated with content from the editor iframe.
21306              * @param {HtmlEditor} this
21307              * @param {String} html
21308              */
21309             sync: true,
21310              /**
21311              * @event push
21312              * Fires when the iframe editor is updated with content from the textarea.
21313              * @param {HtmlEditor} this
21314              * @param {String} html
21315              */
21316             push: true,
21317              /**
21318              * @event editmodechange
21319              * Fires when the editor switches edit modes
21320              * @param {HtmlEditor} this
21321              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21322              */
21323             editmodechange: true,
21324             /**
21325              * @event editorevent
21326              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21327              * @param {HtmlEditor} this
21328              */
21329             editorevent: true,
21330             /**
21331              * @event firstfocus
21332              * Fires when on first focus - needed by toolbars..
21333              * @param {HtmlEditor} this
21334              */
21335             firstfocus: true,
21336             /**
21337              * @event autosave
21338              * Auto save the htmlEditor value as a file into Events
21339              * @param {HtmlEditor} this
21340              */
21341             autosave: true,
21342             /**
21343              * @event savedpreview
21344              * preview the saved version of htmlEditor
21345              * @param {HtmlEditor} this
21346              */
21347             savedpreview: true
21348         });
21349 };
21350
21351
21352 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21353     
21354     
21355       /**
21356      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21357      */
21358     toolbars : false,
21359    
21360      /**
21361      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21362      *                        Roo.resizable.
21363      */
21364     resizable : false,
21365      /**
21366      * @cfg {Number} height (in pixels)
21367      */   
21368     height: 300,
21369    /**
21370      * @cfg {Number} width (in pixels)
21371      */   
21372     width: false,
21373     
21374     /**
21375      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21376      * 
21377      */
21378     stylesheets: false,
21379     
21380     // id of frame..
21381     frameId: false,
21382     
21383     // private properties
21384     validationEvent : false,
21385     deferHeight: true,
21386     initialized : false,
21387     activated : false,
21388     
21389     onFocus : Roo.emptyFn,
21390     iframePad:3,
21391     hideMode:'offsets',
21392     
21393     
21394     tbContainer : false,
21395     
21396     toolbarContainer :function() {
21397         return this.wrap.select('.x-html-editor-tb',true).first();
21398     },
21399
21400     /**
21401      * Protected method that will not generally be called directly. It
21402      * is called when the editor creates its toolbar. Override this method if you need to
21403      * add custom toolbar buttons.
21404      * @param {HtmlEditor} editor
21405      */
21406     createToolbar : function(){
21407         
21408         Roo.log("create toolbars");
21409         
21410         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21411         this.toolbars[0].render(this.toolbarContainer());
21412         
21413         return;
21414         
21415 //        if (!editor.toolbars || !editor.toolbars.length) {
21416 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21417 //        }
21418 //        
21419 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21420 //            editor.toolbars[i] = Roo.factory(
21421 //                    typeof(editor.toolbars[i]) == 'string' ?
21422 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21423 //                Roo.bootstrap.HtmlEditor);
21424 //            editor.toolbars[i].init(editor);
21425 //        }
21426     },
21427
21428      
21429     // private
21430     onRender : function(ct, position)
21431     {
21432        // Roo.log("Call onRender: " + this.xtype);
21433         var _t = this;
21434         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21435       
21436         this.wrap = this.inputEl().wrap({
21437             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21438         });
21439         
21440         this.editorcore.onRender(ct, position);
21441          
21442         if (this.resizable) {
21443             this.resizeEl = new Roo.Resizable(this.wrap, {
21444                 pinned : true,
21445                 wrap: true,
21446                 dynamic : true,
21447                 minHeight : this.height,
21448                 height: this.height,
21449                 handles : this.resizable,
21450                 width: this.width,
21451                 listeners : {
21452                     resize : function(r, w, h) {
21453                         _t.onResize(w,h); // -something
21454                     }
21455                 }
21456             });
21457             
21458         }
21459         this.createToolbar(this);
21460        
21461         
21462         if(!this.width && this.resizable){
21463             this.setSize(this.wrap.getSize());
21464         }
21465         if (this.resizeEl) {
21466             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21467             // should trigger onReize..
21468         }
21469         
21470     },
21471
21472     // private
21473     onResize : function(w, h)
21474     {
21475         Roo.log('resize: ' +w + ',' + h );
21476         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21477         var ew = false;
21478         var eh = false;
21479         
21480         if(this.inputEl() ){
21481             if(typeof w == 'number'){
21482                 var aw = w - this.wrap.getFrameWidth('lr');
21483                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21484                 ew = aw;
21485             }
21486             if(typeof h == 'number'){
21487                  var tbh = -11;  // fixme it needs to tool bar size!
21488                 for (var i =0; i < this.toolbars.length;i++) {
21489                     // fixme - ask toolbars for heights?
21490                     tbh += this.toolbars[i].el.getHeight();
21491                     //if (this.toolbars[i].footer) {
21492                     //    tbh += this.toolbars[i].footer.el.getHeight();
21493                     //}
21494                 }
21495               
21496                 
21497                 
21498                 
21499                 
21500                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21501                 ah -= 5; // knock a few pixes off for look..
21502                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21503                 var eh = ah;
21504             }
21505         }
21506         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21507         this.editorcore.onResize(ew,eh);
21508         
21509     },
21510
21511     /**
21512      * Toggles the editor between standard and source edit mode.
21513      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21514      */
21515     toggleSourceEdit : function(sourceEditMode)
21516     {
21517         this.editorcore.toggleSourceEdit(sourceEditMode);
21518         
21519         if(this.editorcore.sourceEditMode){
21520             Roo.log('editor - showing textarea');
21521             
21522 //            Roo.log('in');
21523 //            Roo.log(this.syncValue());
21524             this.syncValue();
21525             this.inputEl().removeClass(['hide', 'x-hidden']);
21526             this.inputEl().dom.removeAttribute('tabIndex');
21527             this.inputEl().focus();
21528         }else{
21529             Roo.log('editor - hiding textarea');
21530 //            Roo.log('out')
21531 //            Roo.log(this.pushValue()); 
21532             this.pushValue();
21533             
21534             this.inputEl().addClass(['hide', 'x-hidden']);
21535             this.inputEl().dom.setAttribute('tabIndex', -1);
21536             //this.deferFocus();
21537         }
21538          
21539         if(this.resizable){
21540             this.setSize(this.wrap.getSize());
21541         }
21542         
21543         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21544     },
21545  
21546     // private (for BoxComponent)
21547     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21548
21549     // private (for BoxComponent)
21550     getResizeEl : function(){
21551         return this.wrap;
21552     },
21553
21554     // private (for BoxComponent)
21555     getPositionEl : function(){
21556         return this.wrap;
21557     },
21558
21559     // private
21560     initEvents : function(){
21561         this.originalValue = this.getValue();
21562     },
21563
21564 //    /**
21565 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21566 //     * @method
21567 //     */
21568 //    markInvalid : Roo.emptyFn,
21569 //    /**
21570 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21571 //     * @method
21572 //     */
21573 //    clearInvalid : Roo.emptyFn,
21574
21575     setValue : function(v){
21576         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21577         this.editorcore.pushValue();
21578     },
21579
21580      
21581     // private
21582     deferFocus : function(){
21583         this.focus.defer(10, this);
21584     },
21585
21586     // doc'ed in Field
21587     focus : function(){
21588         this.editorcore.focus();
21589         
21590     },
21591       
21592
21593     // private
21594     onDestroy : function(){
21595         
21596         
21597         
21598         if(this.rendered){
21599             
21600             for (var i =0; i < this.toolbars.length;i++) {
21601                 // fixme - ask toolbars for heights?
21602                 this.toolbars[i].onDestroy();
21603             }
21604             
21605             this.wrap.dom.innerHTML = '';
21606             this.wrap.remove();
21607         }
21608     },
21609
21610     // private
21611     onFirstFocus : function(){
21612         //Roo.log("onFirstFocus");
21613         this.editorcore.onFirstFocus();
21614          for (var i =0; i < this.toolbars.length;i++) {
21615             this.toolbars[i].onFirstFocus();
21616         }
21617         
21618     },
21619     
21620     // private
21621     syncValue : function()
21622     {   
21623         this.editorcore.syncValue();
21624     },
21625     
21626     pushValue : function()
21627     {   
21628         this.editorcore.pushValue();
21629     }
21630      
21631     
21632     // hide stuff that is not compatible
21633     /**
21634      * @event blur
21635      * @hide
21636      */
21637     /**
21638      * @event change
21639      * @hide
21640      */
21641     /**
21642      * @event focus
21643      * @hide
21644      */
21645     /**
21646      * @event specialkey
21647      * @hide
21648      */
21649     /**
21650      * @cfg {String} fieldClass @hide
21651      */
21652     /**
21653      * @cfg {String} focusClass @hide
21654      */
21655     /**
21656      * @cfg {String} autoCreate @hide
21657      */
21658     /**
21659      * @cfg {String} inputType @hide
21660      */
21661     /**
21662      * @cfg {String} invalidClass @hide
21663      */
21664     /**
21665      * @cfg {String} invalidText @hide
21666      */
21667     /**
21668      * @cfg {String} msgFx @hide
21669      */
21670     /**
21671      * @cfg {String} validateOnBlur @hide
21672      */
21673 });
21674  
21675     
21676    
21677    
21678    
21679       
21680 Roo.namespace('Roo.bootstrap.htmleditor');
21681 /**
21682  * @class Roo.bootstrap.HtmlEditorToolbar1
21683  * Basic Toolbar
21684  * 
21685  * Usage:
21686  *
21687  new Roo.bootstrap.HtmlEditor({
21688     ....
21689     toolbars : [
21690         new Roo.bootstrap.HtmlEditorToolbar1({
21691             disable : { fonts: 1 , format: 1, ..., ... , ...],
21692             btns : [ .... ]
21693         })
21694     }
21695      
21696  * 
21697  * @cfg {Object} disable List of elements to disable..
21698  * @cfg {Array} btns List of additional buttons.
21699  * 
21700  * 
21701  * NEEDS Extra CSS? 
21702  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21703  */
21704  
21705 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21706 {
21707     
21708     Roo.apply(this, config);
21709     
21710     // default disabled, based on 'good practice'..
21711     this.disable = this.disable || {};
21712     Roo.applyIf(this.disable, {
21713         fontSize : true,
21714         colors : true,
21715         specialElements : true
21716     });
21717     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21718     
21719     this.editor = config.editor;
21720     this.editorcore = config.editor.editorcore;
21721     
21722     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21723     
21724     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21725     // dont call parent... till later.
21726 }
21727 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21728      
21729     bar : true,
21730     
21731     editor : false,
21732     editorcore : false,
21733     
21734     
21735     formats : [
21736         "p" ,  
21737         "h1","h2","h3","h4","h5","h6", 
21738         "pre", "code", 
21739         "abbr", "acronym", "address", "cite", "samp", "var",
21740         'div','span'
21741     ],
21742     
21743     onRender : function(ct, position)
21744     {
21745        // Roo.log("Call onRender: " + this.xtype);
21746         
21747        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21748        Roo.log(this.el);
21749        this.el.dom.style.marginBottom = '0';
21750        var _this = this;
21751        var editorcore = this.editorcore;
21752        var editor= this.editor;
21753        
21754        var children = [];
21755        var btn = function(id,cmd , toggle, handler){
21756        
21757             var  event = toggle ? 'toggle' : 'click';
21758        
21759             var a = {
21760                 size : 'sm',
21761                 xtype: 'Button',
21762                 xns: Roo.bootstrap,
21763                 glyphicon : id,
21764                 cmd : id || cmd,
21765                 enableToggle:toggle !== false,
21766                 //html : 'submit'
21767                 pressed : toggle ? false : null,
21768                 listeners : {}
21769             };
21770             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21771                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21772             };
21773             children.push(a);
21774             return a;
21775        }
21776         
21777         var style = {
21778                 xtype: 'Button',
21779                 size : 'sm',
21780                 xns: Roo.bootstrap,
21781                 glyphicon : 'font',
21782                 //html : 'submit'
21783                 menu : {
21784                     xtype: 'Menu',
21785                     xns: Roo.bootstrap,
21786                     items:  []
21787                 }
21788         };
21789         Roo.each(this.formats, function(f) {
21790             style.menu.items.push({
21791                 xtype :'MenuItem',
21792                 xns: Roo.bootstrap,
21793                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21794                 tagname : f,
21795                 listeners : {
21796                     click : function()
21797                     {
21798                         editorcore.insertTag(this.tagname);
21799                         editor.focus();
21800                     }
21801                 }
21802                 
21803             });
21804         });
21805          children.push(style);   
21806             
21807             
21808         btn('bold',false,true);
21809         btn('italic',false,true);
21810         btn('align-left', 'justifyleft',true);
21811         btn('align-center', 'justifycenter',true);
21812         btn('align-right' , 'justifyright',true);
21813         btn('link', false, false, function(btn) {
21814             //Roo.log("create link?");
21815             var url = prompt(this.createLinkText, this.defaultLinkValue);
21816             if(url && url != 'http:/'+'/'){
21817                 this.editorcore.relayCmd('createlink', url);
21818             }
21819         }),
21820         btn('list','insertunorderedlist',true);
21821         btn('pencil', false,true, function(btn){
21822                 Roo.log(this);
21823                 
21824                 this.toggleSourceEdit(btn.pressed);
21825         });
21826         /*
21827         var cog = {
21828                 xtype: 'Button',
21829                 size : 'sm',
21830                 xns: Roo.bootstrap,
21831                 glyphicon : 'cog',
21832                 //html : 'submit'
21833                 menu : {
21834                     xtype: 'Menu',
21835                     xns: Roo.bootstrap,
21836                     items:  []
21837                 }
21838         };
21839         
21840         cog.menu.items.push({
21841             xtype :'MenuItem',
21842             xns: Roo.bootstrap,
21843             html : Clean styles,
21844             tagname : f,
21845             listeners : {
21846                 click : function()
21847                 {
21848                     editorcore.insertTag(this.tagname);
21849                     editor.focus();
21850                 }
21851             }
21852             
21853         });
21854        */
21855         
21856          
21857        this.xtype = 'NavSimplebar';
21858         
21859         for(var i=0;i< children.length;i++) {
21860             
21861             this.buttons.add(this.addxtypeChild(children[i]));
21862             
21863         }
21864         
21865         editor.on('editorevent', this.updateToolbar, this);
21866     },
21867     onBtnClick : function(id)
21868     {
21869        this.editorcore.relayCmd(id);
21870        this.editorcore.focus();
21871     },
21872     
21873     /**
21874      * Protected method that will not generally be called directly. It triggers
21875      * a toolbar update by reading the markup state of the current selection in the editor.
21876      */
21877     updateToolbar: function(){
21878
21879         if(!this.editorcore.activated){
21880             this.editor.onFirstFocus(); // is this neeed?
21881             return;
21882         }
21883
21884         var btns = this.buttons; 
21885         var doc = this.editorcore.doc;
21886         btns.get('bold').setActive(doc.queryCommandState('bold'));
21887         btns.get('italic').setActive(doc.queryCommandState('italic'));
21888         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21889         
21890         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21891         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21892         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21893         
21894         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21895         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21896          /*
21897         
21898         var ans = this.editorcore.getAllAncestors();
21899         if (this.formatCombo) {
21900             
21901             
21902             var store = this.formatCombo.store;
21903             this.formatCombo.setValue("");
21904             for (var i =0; i < ans.length;i++) {
21905                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21906                     // select it..
21907                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21908                     break;
21909                 }
21910             }
21911         }
21912         
21913         
21914         
21915         // hides menus... - so this cant be on a menu...
21916         Roo.bootstrap.MenuMgr.hideAll();
21917         */
21918         Roo.bootstrap.MenuMgr.hideAll();
21919         //this.editorsyncValue();
21920     },
21921     onFirstFocus: function() {
21922         this.buttons.each(function(item){
21923            item.enable();
21924         });
21925     },
21926     toggleSourceEdit : function(sourceEditMode){
21927         
21928           
21929         if(sourceEditMode){
21930             Roo.log("disabling buttons");
21931            this.buttons.each( function(item){
21932                 if(item.cmd != 'pencil'){
21933                     item.disable();
21934                 }
21935             });
21936           
21937         }else{
21938             Roo.log("enabling buttons");
21939             if(this.editorcore.initialized){
21940                 this.buttons.each( function(item){
21941                     item.enable();
21942                 });
21943             }
21944             
21945         }
21946         Roo.log("calling toggole on editor");
21947         // tell the editor that it's been pressed..
21948         this.editor.toggleSourceEdit(sourceEditMode);
21949        
21950     }
21951 });
21952
21953
21954
21955
21956
21957 /**
21958  * @class Roo.bootstrap.Table.AbstractSelectionModel
21959  * @extends Roo.util.Observable
21960  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21961  * implemented by descendant classes.  This class should not be directly instantiated.
21962  * @constructor
21963  */
21964 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21965     this.locked = false;
21966     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21967 };
21968
21969
21970 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21971     /** @ignore Called by the grid automatically. Do not call directly. */
21972     init : function(grid){
21973         this.grid = grid;
21974         this.initEvents();
21975     },
21976
21977     /**
21978      * Locks the selections.
21979      */
21980     lock : function(){
21981         this.locked = true;
21982     },
21983
21984     /**
21985      * Unlocks the selections.
21986      */
21987     unlock : function(){
21988         this.locked = false;
21989     },
21990
21991     /**
21992      * Returns true if the selections are locked.
21993      * @return {Boolean}
21994      */
21995     isLocked : function(){
21996         return this.locked;
21997     }
21998 });
21999 /**
22000  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22001  * @class Roo.bootstrap.Table.RowSelectionModel
22002  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22003  * It supports multiple selections and keyboard selection/navigation. 
22004  * @constructor
22005  * @param {Object} config
22006  */
22007
22008 Roo.bootstrap.Table.RowSelectionModel = function(config){
22009     Roo.apply(this, config);
22010     this.selections = new Roo.util.MixedCollection(false, function(o){
22011         return o.id;
22012     });
22013
22014     this.last = false;
22015     this.lastActive = false;
22016
22017     this.addEvents({
22018         /**
22019              * @event selectionchange
22020              * Fires when the selection changes
22021              * @param {SelectionModel} this
22022              */
22023             "selectionchange" : true,
22024         /**
22025              * @event afterselectionchange
22026              * Fires after the selection changes (eg. by key press or clicking)
22027              * @param {SelectionModel} this
22028              */
22029             "afterselectionchange" : true,
22030         /**
22031              * @event beforerowselect
22032              * Fires when a row is selected being selected, return false to cancel.
22033              * @param {SelectionModel} this
22034              * @param {Number} rowIndex The selected index
22035              * @param {Boolean} keepExisting False if other selections will be cleared
22036              */
22037             "beforerowselect" : true,
22038         /**
22039              * @event rowselect
22040              * Fires when a row is selected.
22041              * @param {SelectionModel} this
22042              * @param {Number} rowIndex The selected index
22043              * @param {Roo.data.Record} r The record
22044              */
22045             "rowselect" : true,
22046         /**
22047              * @event rowdeselect
22048              * Fires when a row is deselected.
22049              * @param {SelectionModel} this
22050              * @param {Number} rowIndex The selected index
22051              */
22052         "rowdeselect" : true
22053     });
22054     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22055     this.locked = false;
22056 };
22057
22058 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22059     /**
22060      * @cfg {Boolean} singleSelect
22061      * True to allow selection of only one row at a time (defaults to false)
22062      */
22063     singleSelect : false,
22064
22065     // private
22066     initEvents : function(){
22067
22068         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22069             this.grid.on("mousedown", this.handleMouseDown, this);
22070         }else{ // allow click to work like normal
22071             this.grid.on("rowclick", this.handleDragableRowClick, this);
22072         }
22073
22074         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22075             "up" : function(e){
22076                 if(!e.shiftKey){
22077                     this.selectPrevious(e.shiftKey);
22078                 }else if(this.last !== false && this.lastActive !== false){
22079                     var last = this.last;
22080                     this.selectRange(this.last,  this.lastActive-1);
22081                     this.grid.getView().focusRow(this.lastActive);
22082                     if(last !== false){
22083                         this.last = last;
22084                     }
22085                 }else{
22086                     this.selectFirstRow();
22087                 }
22088                 this.fireEvent("afterselectionchange", this);
22089             },
22090             "down" : function(e){
22091                 if(!e.shiftKey){
22092                     this.selectNext(e.shiftKey);
22093                 }else if(this.last !== false && this.lastActive !== false){
22094                     var last = this.last;
22095                     this.selectRange(this.last,  this.lastActive+1);
22096                     this.grid.getView().focusRow(this.lastActive);
22097                     if(last !== false){
22098                         this.last = last;
22099                     }
22100                 }else{
22101                     this.selectFirstRow();
22102                 }
22103                 this.fireEvent("afterselectionchange", this);
22104             },
22105             scope: this
22106         });
22107
22108         var view = this.grid.view;
22109         view.on("refresh", this.onRefresh, this);
22110         view.on("rowupdated", this.onRowUpdated, this);
22111         view.on("rowremoved", this.onRemove, this);
22112     },
22113
22114     // private
22115     onRefresh : function(){
22116         var ds = this.grid.dataSource, i, v = this.grid.view;
22117         var s = this.selections;
22118         s.each(function(r){
22119             if((i = ds.indexOfId(r.id)) != -1){
22120                 v.onRowSelect(i);
22121             }else{
22122                 s.remove(r);
22123             }
22124         });
22125     },
22126
22127     // private
22128     onRemove : function(v, index, r){
22129         this.selections.remove(r);
22130     },
22131
22132     // private
22133     onRowUpdated : function(v, index, r){
22134         if(this.isSelected(r)){
22135             v.onRowSelect(index);
22136         }
22137     },
22138
22139     /**
22140      * Select records.
22141      * @param {Array} records The records to select
22142      * @param {Boolean} keepExisting (optional) True to keep existing selections
22143      */
22144     selectRecords : function(records, keepExisting){
22145         if(!keepExisting){
22146             this.clearSelections();
22147         }
22148         var ds = this.grid.dataSource;
22149         for(var i = 0, len = records.length; i < len; i++){
22150             this.selectRow(ds.indexOf(records[i]), true);
22151         }
22152     },
22153
22154     /**
22155      * Gets the number of selected rows.
22156      * @return {Number}
22157      */
22158     getCount : function(){
22159         return this.selections.length;
22160     },
22161
22162     /**
22163      * Selects the first row in the grid.
22164      */
22165     selectFirstRow : function(){
22166         this.selectRow(0);
22167     },
22168
22169     /**
22170      * Select the last row.
22171      * @param {Boolean} keepExisting (optional) True to keep existing selections
22172      */
22173     selectLastRow : function(keepExisting){
22174         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22175     },
22176
22177     /**
22178      * Selects the row immediately following the last selected row.
22179      * @param {Boolean} keepExisting (optional) True to keep existing selections
22180      */
22181     selectNext : function(keepExisting){
22182         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22183             this.selectRow(this.last+1, keepExisting);
22184             this.grid.getView().focusRow(this.last);
22185         }
22186     },
22187
22188     /**
22189      * Selects the row that precedes the last selected row.
22190      * @param {Boolean} keepExisting (optional) True to keep existing selections
22191      */
22192     selectPrevious : function(keepExisting){
22193         if(this.last){
22194             this.selectRow(this.last-1, keepExisting);
22195             this.grid.getView().focusRow(this.last);
22196         }
22197     },
22198
22199     /**
22200      * Returns the selected records
22201      * @return {Array} Array of selected records
22202      */
22203     getSelections : function(){
22204         return [].concat(this.selections.items);
22205     },
22206
22207     /**
22208      * Returns the first selected record.
22209      * @return {Record}
22210      */
22211     getSelected : function(){
22212         return this.selections.itemAt(0);
22213     },
22214
22215
22216     /**
22217      * Clears all selections.
22218      */
22219     clearSelections : function(fast){
22220         if(this.locked) {
22221             return;
22222         }
22223         if(fast !== true){
22224             var ds = this.grid.dataSource;
22225             var s = this.selections;
22226             s.each(function(r){
22227                 this.deselectRow(ds.indexOfId(r.id));
22228             }, this);
22229             s.clear();
22230         }else{
22231             this.selections.clear();
22232         }
22233         this.last = false;
22234     },
22235
22236
22237     /**
22238      * Selects all rows.
22239      */
22240     selectAll : function(){
22241         if(this.locked) {
22242             return;
22243         }
22244         this.selections.clear();
22245         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22246             this.selectRow(i, true);
22247         }
22248     },
22249
22250     /**
22251      * Returns True if there is a selection.
22252      * @return {Boolean}
22253      */
22254     hasSelection : function(){
22255         return this.selections.length > 0;
22256     },
22257
22258     /**
22259      * Returns True if the specified row is selected.
22260      * @param {Number/Record} record The record or index of the record to check
22261      * @return {Boolean}
22262      */
22263     isSelected : function(index){
22264         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22265         return (r && this.selections.key(r.id) ? true : false);
22266     },
22267
22268     /**
22269      * Returns True if the specified record id is selected.
22270      * @param {String} id The id of record to check
22271      * @return {Boolean}
22272      */
22273     isIdSelected : function(id){
22274         return (this.selections.key(id) ? true : false);
22275     },
22276
22277     // private
22278     handleMouseDown : function(e, t){
22279         var view = this.grid.getView(), rowIndex;
22280         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22281             return;
22282         };
22283         if(e.shiftKey && this.last !== false){
22284             var last = this.last;
22285             this.selectRange(last, rowIndex, e.ctrlKey);
22286             this.last = last; // reset the last
22287             view.focusRow(rowIndex);
22288         }else{
22289             var isSelected = this.isSelected(rowIndex);
22290             if(e.button !== 0 && isSelected){
22291                 view.focusRow(rowIndex);
22292             }else if(e.ctrlKey && isSelected){
22293                 this.deselectRow(rowIndex);
22294             }else if(!isSelected){
22295                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22296                 view.focusRow(rowIndex);
22297             }
22298         }
22299         this.fireEvent("afterselectionchange", this);
22300     },
22301     // private
22302     handleDragableRowClick :  function(grid, rowIndex, e) 
22303     {
22304         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22305             this.selectRow(rowIndex, false);
22306             grid.view.focusRow(rowIndex);
22307              this.fireEvent("afterselectionchange", this);
22308         }
22309     },
22310     
22311     /**
22312      * Selects multiple rows.
22313      * @param {Array} rows Array of the indexes of the row to select
22314      * @param {Boolean} keepExisting (optional) True to keep existing selections
22315      */
22316     selectRows : function(rows, keepExisting){
22317         if(!keepExisting){
22318             this.clearSelections();
22319         }
22320         for(var i = 0, len = rows.length; i < len; i++){
22321             this.selectRow(rows[i], true);
22322         }
22323     },
22324
22325     /**
22326      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22327      * @param {Number} startRow The index of the first row in the range
22328      * @param {Number} endRow The index of the last row in the range
22329      * @param {Boolean} keepExisting (optional) True to retain existing selections
22330      */
22331     selectRange : function(startRow, endRow, keepExisting){
22332         if(this.locked) {
22333             return;
22334         }
22335         if(!keepExisting){
22336             this.clearSelections();
22337         }
22338         if(startRow <= endRow){
22339             for(var i = startRow; i <= endRow; i++){
22340                 this.selectRow(i, true);
22341             }
22342         }else{
22343             for(var i = startRow; i >= endRow; i--){
22344                 this.selectRow(i, true);
22345             }
22346         }
22347     },
22348
22349     /**
22350      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22351      * @param {Number} startRow The index of the first row in the range
22352      * @param {Number} endRow The index of the last row in the range
22353      */
22354     deselectRange : function(startRow, endRow, preventViewNotify){
22355         if(this.locked) {
22356             return;
22357         }
22358         for(var i = startRow; i <= endRow; i++){
22359             this.deselectRow(i, preventViewNotify);
22360         }
22361     },
22362
22363     /**
22364      * Selects a row.
22365      * @param {Number} row The index of the row to select
22366      * @param {Boolean} keepExisting (optional) True to keep existing selections
22367      */
22368     selectRow : function(index, keepExisting, preventViewNotify){
22369         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22370             return;
22371         }
22372         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22373             if(!keepExisting || this.singleSelect){
22374                 this.clearSelections();
22375             }
22376             var r = this.grid.dataSource.getAt(index);
22377             this.selections.add(r);
22378             this.last = this.lastActive = index;
22379             if(!preventViewNotify){
22380                 this.grid.getView().onRowSelect(index);
22381             }
22382             this.fireEvent("rowselect", this, index, r);
22383             this.fireEvent("selectionchange", this);
22384         }
22385     },
22386
22387     /**
22388      * Deselects a row.
22389      * @param {Number} row The index of the row to deselect
22390      */
22391     deselectRow : function(index, preventViewNotify){
22392         if(this.locked) {
22393             return;
22394         }
22395         if(this.last == index){
22396             this.last = false;
22397         }
22398         if(this.lastActive == index){
22399             this.lastActive = false;
22400         }
22401         var r = this.grid.dataSource.getAt(index);
22402         this.selections.remove(r);
22403         if(!preventViewNotify){
22404             this.grid.getView().onRowDeselect(index);
22405         }
22406         this.fireEvent("rowdeselect", this, index);
22407         this.fireEvent("selectionchange", this);
22408     },
22409
22410     // private
22411     restoreLast : function(){
22412         if(this._last){
22413             this.last = this._last;
22414         }
22415     },
22416
22417     // private
22418     acceptsNav : function(row, col, cm){
22419         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22420     },
22421
22422     // private
22423     onEditorKey : function(field, e){
22424         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22425         if(k == e.TAB){
22426             e.stopEvent();
22427             ed.completeEdit();
22428             if(e.shiftKey){
22429                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22430             }else{
22431                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22432             }
22433         }else if(k == e.ENTER && !e.ctrlKey){
22434             e.stopEvent();
22435             ed.completeEdit();
22436             if(e.shiftKey){
22437                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22438             }else{
22439                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22440             }
22441         }else if(k == e.ESC){
22442             ed.cancelEdit();
22443         }
22444         if(newCell){
22445             g.startEditing(newCell[0], newCell[1]);
22446         }
22447     }
22448 });/*
22449  * Based on:
22450  * Ext JS Library 1.1.1
22451  * Copyright(c) 2006-2007, Ext JS, LLC.
22452  *
22453  * Originally Released Under LGPL - original licence link has changed is not relivant.
22454  *
22455  * Fork - LGPL
22456  * <script type="text/javascript">
22457  */
22458  
22459 /**
22460  * @class Roo.bootstrap.PagingToolbar
22461  * @extends Roo.bootstrap.NavSimplebar
22462  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22463  * @constructor
22464  * Create a new PagingToolbar
22465  * @param {Object} config The config object
22466  * @param {Roo.data.Store} store
22467  */
22468 Roo.bootstrap.PagingToolbar = function(config)
22469 {
22470     // old args format still supported... - xtype is prefered..
22471         // created from xtype...
22472     
22473     this.ds = config.dataSource;
22474     
22475     if (config.store && !this.ds) {
22476         this.store= Roo.factory(config.store, Roo.data);
22477         this.ds = this.store;
22478         this.ds.xmodule = this.xmodule || false;
22479     }
22480     
22481     this.toolbarItems = [];
22482     if (config.items) {
22483         this.toolbarItems = config.items;
22484     }
22485     
22486     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22487     
22488     this.cursor = 0;
22489     
22490     if (this.ds) { 
22491         this.bind(this.ds);
22492     }
22493     
22494     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22495     
22496 };
22497
22498 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22499     /**
22500      * @cfg {Roo.data.Store} dataSource
22501      * The underlying data store providing the paged data
22502      */
22503     /**
22504      * @cfg {String/HTMLElement/Element} container
22505      * container The id or element that will contain the toolbar
22506      */
22507     /**
22508      * @cfg {Boolean} displayInfo
22509      * True to display the displayMsg (defaults to false)
22510      */
22511     /**
22512      * @cfg {Number} pageSize
22513      * The number of records to display per page (defaults to 20)
22514      */
22515     pageSize: 20,
22516     /**
22517      * @cfg {String} displayMsg
22518      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22519      */
22520     displayMsg : 'Displaying {0} - {1} of {2}',
22521     /**
22522      * @cfg {String} emptyMsg
22523      * The message to display when no records are found (defaults to "No data to display")
22524      */
22525     emptyMsg : 'No data to display',
22526     /**
22527      * Customizable piece of the default paging text (defaults to "Page")
22528      * @type String
22529      */
22530     beforePageText : "Page",
22531     /**
22532      * Customizable piece of the default paging text (defaults to "of %0")
22533      * @type String
22534      */
22535     afterPageText : "of {0}",
22536     /**
22537      * Customizable piece of the default paging text (defaults to "First Page")
22538      * @type String
22539      */
22540     firstText : "First Page",
22541     /**
22542      * Customizable piece of the default paging text (defaults to "Previous Page")
22543      * @type String
22544      */
22545     prevText : "Previous Page",
22546     /**
22547      * Customizable piece of the default paging text (defaults to "Next Page")
22548      * @type String
22549      */
22550     nextText : "Next Page",
22551     /**
22552      * Customizable piece of the default paging text (defaults to "Last Page")
22553      * @type String
22554      */
22555     lastText : "Last Page",
22556     /**
22557      * Customizable piece of the default paging text (defaults to "Refresh")
22558      * @type String
22559      */
22560     refreshText : "Refresh",
22561
22562     buttons : false,
22563     // private
22564     onRender : function(ct, position) 
22565     {
22566         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22567         this.navgroup.parentId = this.id;
22568         this.navgroup.onRender(this.el, null);
22569         // add the buttons to the navgroup
22570         
22571         if(this.displayInfo){
22572             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22573             this.displayEl = this.el.select('.x-paging-info', true).first();
22574 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22575 //            this.displayEl = navel.el.select('span',true).first();
22576         }
22577         
22578         var _this = this;
22579         
22580         if(this.buttons){
22581             Roo.each(_this.buttons, function(e){ // this might need to use render????
22582                Roo.factory(e).onRender(_this.el, null);
22583             });
22584         }
22585             
22586         Roo.each(_this.toolbarItems, function(e) {
22587             _this.navgroup.addItem(e);
22588         });
22589         
22590         
22591         this.first = this.navgroup.addItem({
22592             tooltip: this.firstText,
22593             cls: "prev",
22594             icon : 'fa fa-backward',
22595             disabled: true,
22596             preventDefault: true,
22597             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22598         });
22599         
22600         this.prev =  this.navgroup.addItem({
22601             tooltip: this.prevText,
22602             cls: "prev",
22603             icon : 'fa fa-step-backward',
22604             disabled: true,
22605             preventDefault: true,
22606             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22607         });
22608     //this.addSeparator();
22609         
22610         
22611         var field = this.navgroup.addItem( {
22612             tagtype : 'span',
22613             cls : 'x-paging-position',
22614             
22615             html : this.beforePageText  +
22616                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22617                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22618          } ); //?? escaped?
22619         
22620         this.field = field.el.select('input', true).first();
22621         this.field.on("keydown", this.onPagingKeydown, this);
22622         this.field.on("focus", function(){this.dom.select();});
22623     
22624     
22625         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22626         //this.field.setHeight(18);
22627         //this.addSeparator();
22628         this.next = this.navgroup.addItem({
22629             tooltip: this.nextText,
22630             cls: "next",
22631             html : ' <i class="fa fa-step-forward">',
22632             disabled: true,
22633             preventDefault: true,
22634             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22635         });
22636         this.last = this.navgroup.addItem({
22637             tooltip: this.lastText,
22638             icon : 'fa fa-forward',
22639             cls: "next",
22640             disabled: true,
22641             preventDefault: true,
22642             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22643         });
22644     //this.addSeparator();
22645         this.loading = this.navgroup.addItem({
22646             tooltip: this.refreshText,
22647             icon: 'fa fa-refresh',
22648             preventDefault: true,
22649             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22650         });
22651         
22652     },
22653
22654     // private
22655     updateInfo : function(){
22656         if(this.displayEl){
22657             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22658             var msg = count == 0 ?
22659                 this.emptyMsg :
22660                 String.format(
22661                     this.displayMsg,
22662                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22663                 );
22664             this.displayEl.update(msg);
22665         }
22666     },
22667
22668     // private
22669     onLoad : function(ds, r, o){
22670        this.cursor = o.params ? o.params.start : 0;
22671        var d = this.getPageData(),
22672             ap = d.activePage,
22673             ps = d.pages;
22674         
22675        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22676        this.field.dom.value = ap;
22677        this.first.setDisabled(ap == 1);
22678        this.prev.setDisabled(ap == 1);
22679        this.next.setDisabled(ap == ps);
22680        this.last.setDisabled(ap == ps);
22681        this.loading.enable();
22682        this.updateInfo();
22683     },
22684
22685     // private
22686     getPageData : function(){
22687         var total = this.ds.getTotalCount();
22688         return {
22689             total : total,
22690             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22691             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22692         };
22693     },
22694
22695     // private
22696     onLoadError : function(){
22697         this.loading.enable();
22698     },
22699
22700     // private
22701     onPagingKeydown : function(e){
22702         var k = e.getKey();
22703         var d = this.getPageData();
22704         if(k == e.RETURN){
22705             var v = this.field.dom.value, pageNum;
22706             if(!v || isNaN(pageNum = parseInt(v, 10))){
22707                 this.field.dom.value = d.activePage;
22708                 return;
22709             }
22710             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22711             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22712             e.stopEvent();
22713         }
22714         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))
22715         {
22716           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22717           this.field.dom.value = pageNum;
22718           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22719           e.stopEvent();
22720         }
22721         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22722         {
22723           var v = this.field.dom.value, pageNum; 
22724           var increment = (e.shiftKey) ? 10 : 1;
22725           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22726                 increment *= -1;
22727           }
22728           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22729             this.field.dom.value = d.activePage;
22730             return;
22731           }
22732           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22733           {
22734             this.field.dom.value = parseInt(v, 10) + increment;
22735             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22736             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22737           }
22738           e.stopEvent();
22739         }
22740     },
22741
22742     // private
22743     beforeLoad : function(){
22744         if(this.loading){
22745             this.loading.disable();
22746         }
22747     },
22748
22749     // private
22750     onClick : function(which){
22751         
22752         var ds = this.ds;
22753         if (!ds) {
22754             return;
22755         }
22756         
22757         switch(which){
22758             case "first":
22759                 ds.load({params:{start: 0, limit: this.pageSize}});
22760             break;
22761             case "prev":
22762                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22763             break;
22764             case "next":
22765                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22766             break;
22767             case "last":
22768                 var total = ds.getTotalCount();
22769                 var extra = total % this.pageSize;
22770                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22771                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22772             break;
22773             case "refresh":
22774                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22775             break;
22776         }
22777     },
22778
22779     /**
22780      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22781      * @param {Roo.data.Store} store The data store to unbind
22782      */
22783     unbind : function(ds){
22784         ds.un("beforeload", this.beforeLoad, this);
22785         ds.un("load", this.onLoad, this);
22786         ds.un("loadexception", this.onLoadError, this);
22787         ds.un("remove", this.updateInfo, this);
22788         ds.un("add", this.updateInfo, this);
22789         this.ds = undefined;
22790     },
22791
22792     /**
22793      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22794      * @param {Roo.data.Store} store The data store to bind
22795      */
22796     bind : function(ds){
22797         ds.on("beforeload", this.beforeLoad, this);
22798         ds.on("load", this.onLoad, this);
22799         ds.on("loadexception", this.onLoadError, this);
22800         ds.on("remove", this.updateInfo, this);
22801         ds.on("add", this.updateInfo, this);
22802         this.ds = ds;
22803     }
22804 });/*
22805  * - LGPL
22806  *
22807  * element
22808  * 
22809  */
22810
22811 /**
22812  * @class Roo.bootstrap.MessageBar
22813  * @extends Roo.bootstrap.Component
22814  * Bootstrap MessageBar class
22815  * @cfg {String} html contents of the MessageBar
22816  * @cfg {String} weight (info | success | warning | danger) default info
22817  * @cfg {String} beforeClass insert the bar before the given class
22818  * @cfg {Boolean} closable (true | false) default false
22819  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22820  * 
22821  * @constructor
22822  * Create a new Element
22823  * @param {Object} config The config object
22824  */
22825
22826 Roo.bootstrap.MessageBar = function(config){
22827     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22828 };
22829
22830 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22831     
22832     html: '',
22833     weight: 'info',
22834     closable: false,
22835     fixed: false,
22836     beforeClass: 'bootstrap-sticky-wrap',
22837     
22838     getAutoCreate : function(){
22839         
22840         var cfg = {
22841             tag: 'div',
22842             cls: 'alert alert-dismissable alert-' + this.weight,
22843             cn: [
22844                 {
22845                     tag: 'span',
22846                     cls: 'message',
22847                     html: this.html || ''
22848                 }
22849             ]
22850         };
22851         
22852         if(this.fixed){
22853             cfg.cls += ' alert-messages-fixed';
22854         }
22855         
22856         if(this.closable){
22857             cfg.cn.push({
22858                 tag: 'button',
22859                 cls: 'close',
22860                 html: 'x'
22861             });
22862         }
22863         
22864         return cfg;
22865     },
22866     
22867     onRender : function(ct, position)
22868     {
22869         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22870         
22871         if(!this.el){
22872             var cfg = Roo.apply({},  this.getAutoCreate());
22873             cfg.id = Roo.id();
22874             
22875             if (this.cls) {
22876                 cfg.cls += ' ' + this.cls;
22877             }
22878             if (this.style) {
22879                 cfg.style = this.style;
22880             }
22881             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22882             
22883             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22884         }
22885         
22886         this.el.select('>button.close').on('click', this.hide, this);
22887         
22888     },
22889     
22890     show : function()
22891     {
22892         if (!this.rendered) {
22893             this.render();
22894         }
22895         
22896         this.el.show();
22897         
22898         this.fireEvent('show', this);
22899         
22900     },
22901     
22902     hide : function()
22903     {
22904         if (!this.rendered) {
22905             this.render();
22906         }
22907         
22908         this.el.hide();
22909         
22910         this.fireEvent('hide', this);
22911     },
22912     
22913     update : function()
22914     {
22915 //        var e = this.el.dom.firstChild;
22916 //        
22917 //        if(this.closable){
22918 //            e = e.nextSibling;
22919 //        }
22920 //        
22921 //        e.data = this.html || '';
22922
22923         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22924     }
22925    
22926 });
22927
22928  
22929
22930      /*
22931  * - LGPL
22932  *
22933  * Graph
22934  * 
22935  */
22936
22937
22938 /**
22939  * @class Roo.bootstrap.Graph
22940  * @extends Roo.bootstrap.Component
22941  * Bootstrap Graph class
22942 > Prameters
22943  -sm {number} sm 4
22944  -md {number} md 5
22945  @cfg {String} graphtype  bar | vbar | pie
22946  @cfg {number} g_x coodinator | centre x (pie)
22947  @cfg {number} g_y coodinator | centre y (pie)
22948  @cfg {number} g_r radius (pie)
22949  @cfg {number} g_height height of the chart (respected by all elements in the set)
22950  @cfg {number} g_width width of the chart (respected by all elements in the set)
22951  @cfg {Object} title The title of the chart
22952     
22953  -{Array}  values
22954  -opts (object) options for the chart 
22955      o {
22956      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22957      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22958      o vgutter (number)
22959      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.
22960      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22961      o to
22962      o stretch (boolean)
22963      o }
22964  -opts (object) options for the pie
22965      o{
22966      o cut
22967      o startAngle (number)
22968      o endAngle (number)
22969      } 
22970  *
22971  * @constructor
22972  * Create a new Input
22973  * @param {Object} config The config object
22974  */
22975
22976 Roo.bootstrap.Graph = function(config){
22977     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22978     
22979     this.addEvents({
22980         // img events
22981         /**
22982          * @event click
22983          * The img click event for the img.
22984          * @param {Roo.EventObject} e
22985          */
22986         "click" : true
22987     });
22988 };
22989
22990 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22991     
22992     sm: 4,
22993     md: 5,
22994     graphtype: 'bar',
22995     g_height: 250,
22996     g_width: 400,
22997     g_x: 50,
22998     g_y: 50,
22999     g_r: 30,
23000     opts:{
23001         //g_colors: this.colors,
23002         g_type: 'soft',
23003         g_gutter: '20%'
23004
23005     },
23006     title : false,
23007
23008     getAutoCreate : function(){
23009         
23010         var cfg = {
23011             tag: 'div',
23012             html : null
23013         };
23014         
23015         
23016         return  cfg;
23017     },
23018
23019     onRender : function(ct,position){
23020         
23021         
23022         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23023         
23024         if (typeof(Raphael) == 'undefined') {
23025             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23026             return;
23027         }
23028         
23029         this.raphael = Raphael(this.el.dom);
23030         
23031                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23032                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23033                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23034                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23035                 /*
23036                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23037                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23038                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23039                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23040                 
23041                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23042                 r.barchart(330, 10, 300, 220, data1);
23043                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23044                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23045                 */
23046                 
23047                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23048                 // r.barchart(30, 30, 560, 250,  xdata, {
23049                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23050                 //     axis : "0 0 1 1",
23051                 //     axisxlabels :  xdata
23052                 //     //yvalues : cols,
23053                    
23054                 // });
23055 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23056 //        
23057 //        this.load(null,xdata,{
23058 //                axis : "0 0 1 1",
23059 //                axisxlabels :  xdata
23060 //                });
23061
23062     },
23063
23064     load : function(graphtype,xdata,opts)
23065     {
23066         this.raphael.clear();
23067         if(!graphtype) {
23068             graphtype = this.graphtype;
23069         }
23070         if(!opts){
23071             opts = this.opts;
23072         }
23073         var r = this.raphael,
23074             fin = function () {
23075                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23076             },
23077             fout = function () {
23078                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23079             },
23080             pfin = function() {
23081                 this.sector.stop();
23082                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23083
23084                 if (this.label) {
23085                     this.label[0].stop();
23086                     this.label[0].attr({ r: 7.5 });
23087                     this.label[1].attr({ "font-weight": 800 });
23088                 }
23089             },
23090             pfout = function() {
23091                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23092
23093                 if (this.label) {
23094                     this.label[0].animate({ r: 5 }, 500, "bounce");
23095                     this.label[1].attr({ "font-weight": 400 });
23096                 }
23097             };
23098
23099         switch(graphtype){
23100             case 'bar':
23101                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23102                 break;
23103             case 'hbar':
23104                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23105                 break;
23106             case 'pie':
23107 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23108 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23109 //            
23110                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23111                 
23112                 break;
23113
23114         }
23115         
23116         if(this.title){
23117             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23118         }
23119         
23120     },
23121     
23122     setTitle: function(o)
23123     {
23124         this.title = o;
23125     },
23126     
23127     initEvents: function() {
23128         
23129         if(!this.href){
23130             this.el.on('click', this.onClick, this);
23131         }
23132     },
23133     
23134     onClick : function(e)
23135     {
23136         Roo.log('img onclick');
23137         this.fireEvent('click', this, e);
23138     }
23139    
23140 });
23141
23142  
23143 /*
23144  * - LGPL
23145  *
23146  * numberBox
23147  * 
23148  */
23149 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23150
23151 /**
23152  * @class Roo.bootstrap.dash.NumberBox
23153  * @extends Roo.bootstrap.Component
23154  * Bootstrap NumberBox class
23155  * @cfg {String} headline Box headline
23156  * @cfg {String} content Box content
23157  * @cfg {String} icon Box icon
23158  * @cfg {String} footer Footer text
23159  * @cfg {String} fhref Footer href
23160  * 
23161  * @constructor
23162  * Create a new NumberBox
23163  * @param {Object} config The config object
23164  */
23165
23166
23167 Roo.bootstrap.dash.NumberBox = function(config){
23168     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23169     
23170 };
23171
23172 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23173     
23174     headline : '',
23175     content : '',
23176     icon : '',
23177     footer : '',
23178     fhref : '',
23179     ficon : '',
23180     
23181     getAutoCreate : function(){
23182         
23183         var cfg = {
23184             tag : 'div',
23185             cls : 'small-box ',
23186             cn : [
23187                 {
23188                     tag : 'div',
23189                     cls : 'inner',
23190                     cn :[
23191                         {
23192                             tag : 'h3',
23193                             cls : 'roo-headline',
23194                             html : this.headline
23195                         },
23196                         {
23197                             tag : 'p',
23198                             cls : 'roo-content',
23199                             html : this.content
23200                         }
23201                     ]
23202                 }
23203             ]
23204         };
23205         
23206         if(this.icon){
23207             cfg.cn.push({
23208                 tag : 'div',
23209                 cls : 'icon',
23210                 cn :[
23211                     {
23212                         tag : 'i',
23213                         cls : 'ion ' + this.icon
23214                     }
23215                 ]
23216             });
23217         }
23218         
23219         if(this.footer){
23220             var footer = {
23221                 tag : 'a',
23222                 cls : 'small-box-footer',
23223                 href : this.fhref || '#',
23224                 html : this.footer
23225             };
23226             
23227             cfg.cn.push(footer);
23228             
23229         }
23230         
23231         return  cfg;
23232     },
23233
23234     onRender : function(ct,position){
23235         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23236
23237
23238        
23239                 
23240     },
23241
23242     setHeadline: function (value)
23243     {
23244         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23245     },
23246     
23247     setFooter: function (value, href)
23248     {
23249         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23250         
23251         if(href){
23252             this.el.select('a.small-box-footer',true).first().attr('href', href);
23253         }
23254         
23255     },
23256
23257     setContent: function (value)
23258     {
23259         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23260     },
23261
23262     initEvents: function() 
23263     {   
23264         
23265     }
23266     
23267 });
23268
23269  
23270 /*
23271  * - LGPL
23272  *
23273  * TabBox
23274  * 
23275  */
23276 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23277
23278 /**
23279  * @class Roo.bootstrap.dash.TabBox
23280  * @extends Roo.bootstrap.Component
23281  * Bootstrap TabBox class
23282  * @cfg {String} title Title of the TabBox
23283  * @cfg {String} icon Icon of the TabBox
23284  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23285  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23286  * 
23287  * @constructor
23288  * Create a new TabBox
23289  * @param {Object} config The config object
23290  */
23291
23292
23293 Roo.bootstrap.dash.TabBox = function(config){
23294     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23295     this.addEvents({
23296         // raw events
23297         /**
23298          * @event addpane
23299          * When a pane is added
23300          * @param {Roo.bootstrap.dash.TabPane} pane
23301          */
23302         "addpane" : true,
23303         /**
23304          * @event activatepane
23305          * When a pane is activated
23306          * @param {Roo.bootstrap.dash.TabPane} pane
23307          */
23308         "activatepane" : true
23309         
23310          
23311     });
23312     
23313     this.panes = [];
23314 };
23315
23316 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23317
23318     title : '',
23319     icon : false,
23320     showtabs : true,
23321     tabScrollable : false,
23322     
23323     getChildContainer : function()
23324     {
23325         return this.el.select('.tab-content', true).first();
23326     },
23327     
23328     getAutoCreate : function(){
23329         
23330         var header = {
23331             tag: 'li',
23332             cls: 'pull-left header',
23333             html: this.title,
23334             cn : []
23335         };
23336         
23337         if(this.icon){
23338             header.cn.push({
23339                 tag: 'i',
23340                 cls: 'fa ' + this.icon
23341             });
23342         }
23343         
23344         var h = {
23345             tag: 'ul',
23346             cls: 'nav nav-tabs pull-right',
23347             cn: [
23348                 header
23349             ]
23350         };
23351         
23352         if(this.tabScrollable){
23353             h = {
23354                 tag: 'div',
23355                 cls: 'tab-header',
23356                 cn: [
23357                     {
23358                         tag: 'ul',
23359                         cls: 'nav nav-tabs pull-right',
23360                         cn: [
23361                             header
23362                         ]
23363                     }
23364                 ]
23365             };
23366         }
23367         
23368         var cfg = {
23369             tag: 'div',
23370             cls: 'nav-tabs-custom',
23371             cn: [
23372                 h,
23373                 {
23374                     tag: 'div',
23375                     cls: 'tab-content no-padding',
23376                     cn: []
23377                 }
23378             ]
23379         };
23380
23381         return  cfg;
23382     },
23383     initEvents : function()
23384     {
23385         //Roo.log('add add pane handler');
23386         this.on('addpane', this.onAddPane, this);
23387     },
23388      /**
23389      * Updates the box title
23390      * @param {String} html to set the title to.
23391      */
23392     setTitle : function(value)
23393     {
23394         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23395     },
23396     onAddPane : function(pane)
23397     {
23398         this.panes.push(pane);
23399         //Roo.log('addpane');
23400         //Roo.log(pane);
23401         // tabs are rendere left to right..
23402         if(!this.showtabs){
23403             return;
23404         }
23405         
23406         var ctr = this.el.select('.nav-tabs', true).first();
23407          
23408          
23409         var existing = ctr.select('.nav-tab',true);
23410         var qty = existing.getCount();;
23411         
23412         
23413         var tab = ctr.createChild({
23414             tag : 'li',
23415             cls : 'nav-tab' + (qty ? '' : ' active'),
23416             cn : [
23417                 {
23418                     tag : 'a',
23419                     href:'#',
23420                     html : pane.title
23421                 }
23422             ]
23423         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23424         pane.tab = tab;
23425         
23426         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23427         if (!qty) {
23428             pane.el.addClass('active');
23429         }
23430         
23431                 
23432     },
23433     onTabClick : function(ev,un,ob,pane)
23434     {
23435         //Roo.log('tab - prev default');
23436         ev.preventDefault();
23437         
23438         
23439         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23440         pane.tab.addClass('active');
23441         //Roo.log(pane.title);
23442         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23443         // technically we should have a deactivate event.. but maybe add later.
23444         // and it should not de-activate the selected tab...
23445         this.fireEvent('activatepane', pane);
23446         pane.el.addClass('active');
23447         pane.fireEvent('activate');
23448         
23449         
23450     },
23451     
23452     getActivePane : function()
23453     {
23454         var r = false;
23455         Roo.each(this.panes, function(p) {
23456             if(p.el.hasClass('active')){
23457                 r = p;
23458                 return false;
23459             }
23460             
23461             return;
23462         });
23463         
23464         return r;
23465     }
23466     
23467     
23468 });
23469
23470  
23471 /*
23472  * - LGPL
23473  *
23474  * Tab pane
23475  * 
23476  */
23477 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23478 /**
23479  * @class Roo.bootstrap.TabPane
23480  * @extends Roo.bootstrap.Component
23481  * Bootstrap TabPane class
23482  * @cfg {Boolean} active (false | true) Default false
23483  * @cfg {String} title title of panel
23484
23485  * 
23486  * @constructor
23487  * Create a new TabPane
23488  * @param {Object} config The config object
23489  */
23490
23491 Roo.bootstrap.dash.TabPane = function(config){
23492     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23493     
23494     this.addEvents({
23495         // raw events
23496         /**
23497          * @event activate
23498          * When a pane is activated
23499          * @param {Roo.bootstrap.dash.TabPane} pane
23500          */
23501         "activate" : true
23502          
23503     });
23504 };
23505
23506 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23507     
23508     active : false,
23509     title : '',
23510     
23511     // the tabBox that this is attached to.
23512     tab : false,
23513      
23514     getAutoCreate : function() 
23515     {
23516         var cfg = {
23517             tag: 'div',
23518             cls: 'tab-pane'
23519         };
23520         
23521         if(this.active){
23522             cfg.cls += ' active';
23523         }
23524         
23525         return cfg;
23526     },
23527     initEvents  : function()
23528     {
23529         //Roo.log('trigger add pane handler');
23530         this.parent().fireEvent('addpane', this)
23531     },
23532     
23533      /**
23534      * Updates the tab title 
23535      * @param {String} html to set the title to.
23536      */
23537     setTitle: function(str)
23538     {
23539         if (!this.tab) {
23540             return;
23541         }
23542         this.title = str;
23543         this.tab.select('a', true).first().dom.innerHTML = str;
23544         
23545     }
23546     
23547     
23548     
23549 });
23550
23551  
23552
23553
23554  /*
23555  * - LGPL
23556  *
23557  * menu
23558  * 
23559  */
23560 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23561
23562 /**
23563  * @class Roo.bootstrap.menu.Menu
23564  * @extends Roo.bootstrap.Component
23565  * Bootstrap Menu class - container for Menu
23566  * @cfg {String} html Text of the menu
23567  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23568  * @cfg {String} icon Font awesome icon
23569  * @cfg {String} pos Menu align to (top | bottom) default bottom
23570  * 
23571  * 
23572  * @constructor
23573  * Create a new Menu
23574  * @param {Object} config The config object
23575  */
23576
23577
23578 Roo.bootstrap.menu.Menu = function(config){
23579     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23580     
23581     this.addEvents({
23582         /**
23583          * @event beforeshow
23584          * Fires before this menu is displayed
23585          * @param {Roo.bootstrap.menu.Menu} this
23586          */
23587         beforeshow : true,
23588         /**
23589          * @event beforehide
23590          * Fires before this menu is hidden
23591          * @param {Roo.bootstrap.menu.Menu} this
23592          */
23593         beforehide : true,
23594         /**
23595          * @event show
23596          * Fires after this menu is displayed
23597          * @param {Roo.bootstrap.menu.Menu} this
23598          */
23599         show : true,
23600         /**
23601          * @event hide
23602          * Fires after this menu is hidden
23603          * @param {Roo.bootstrap.menu.Menu} this
23604          */
23605         hide : true,
23606         /**
23607          * @event click
23608          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23609          * @param {Roo.bootstrap.menu.Menu} this
23610          * @param {Roo.EventObject} e
23611          */
23612         click : true
23613     });
23614     
23615 };
23616
23617 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23618     
23619     submenu : false,
23620     html : '',
23621     weight : 'default',
23622     icon : false,
23623     pos : 'bottom',
23624     
23625     
23626     getChildContainer : function() {
23627         if(this.isSubMenu){
23628             return this.el;
23629         }
23630         
23631         return this.el.select('ul.dropdown-menu', true).first();  
23632     },
23633     
23634     getAutoCreate : function()
23635     {
23636         var text = [
23637             {
23638                 tag : 'span',
23639                 cls : 'roo-menu-text',
23640                 html : this.html
23641             }
23642         ];
23643         
23644         if(this.icon){
23645             text.unshift({
23646                 tag : 'i',
23647                 cls : 'fa ' + this.icon
23648             })
23649         }
23650         
23651         
23652         var cfg = {
23653             tag : 'div',
23654             cls : 'btn-group',
23655             cn : [
23656                 {
23657                     tag : 'button',
23658                     cls : 'dropdown-button btn btn-' + this.weight,
23659                     cn : text
23660                 },
23661                 {
23662                     tag : 'button',
23663                     cls : 'dropdown-toggle btn btn-' + this.weight,
23664                     cn : [
23665                         {
23666                             tag : 'span',
23667                             cls : 'caret'
23668                         }
23669                     ]
23670                 },
23671                 {
23672                     tag : 'ul',
23673                     cls : 'dropdown-menu'
23674                 }
23675             ]
23676             
23677         };
23678         
23679         if(this.pos == 'top'){
23680             cfg.cls += ' dropup';
23681         }
23682         
23683         if(this.isSubMenu){
23684             cfg = {
23685                 tag : 'ul',
23686                 cls : 'dropdown-menu'
23687             }
23688         }
23689         
23690         return cfg;
23691     },
23692     
23693     onRender : function(ct, position)
23694     {
23695         this.isSubMenu = ct.hasClass('dropdown-submenu');
23696         
23697         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23698     },
23699     
23700     initEvents : function() 
23701     {
23702         if(this.isSubMenu){
23703             return;
23704         }
23705         
23706         this.hidden = true;
23707         
23708         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23709         this.triggerEl.on('click', this.onTriggerPress, this);
23710         
23711         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23712         this.buttonEl.on('click', this.onClick, this);
23713         
23714     },
23715     
23716     list : function()
23717     {
23718         if(this.isSubMenu){
23719             return this.el;
23720         }
23721         
23722         return this.el.select('ul.dropdown-menu', true).first();
23723     },
23724     
23725     onClick : function(e)
23726     {
23727         this.fireEvent("click", this, e);
23728     },
23729     
23730     onTriggerPress  : function(e)
23731     {   
23732         if (this.isVisible()) {
23733             this.hide();
23734         } else {
23735             this.show();
23736         }
23737     },
23738     
23739     isVisible : function(){
23740         return !this.hidden;
23741     },
23742     
23743     show : function()
23744     {
23745         this.fireEvent("beforeshow", this);
23746         
23747         this.hidden = false;
23748         this.el.addClass('open');
23749         
23750         Roo.get(document).on("mouseup", this.onMouseUp, this);
23751         
23752         this.fireEvent("show", this);
23753         
23754         
23755     },
23756     
23757     hide : function()
23758     {
23759         this.fireEvent("beforehide", this);
23760         
23761         this.hidden = true;
23762         this.el.removeClass('open');
23763         
23764         Roo.get(document).un("mouseup", this.onMouseUp);
23765         
23766         this.fireEvent("hide", this);
23767     },
23768     
23769     onMouseUp : function()
23770     {
23771         this.hide();
23772     }
23773     
23774 });
23775
23776  
23777  /*
23778  * - LGPL
23779  *
23780  * menu item
23781  * 
23782  */
23783 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23784
23785 /**
23786  * @class Roo.bootstrap.menu.Item
23787  * @extends Roo.bootstrap.Component
23788  * Bootstrap MenuItem class
23789  * @cfg {Boolean} submenu (true | false) default false
23790  * @cfg {String} html text of the item
23791  * @cfg {String} href the link
23792  * @cfg {Boolean} disable (true | false) default false
23793  * @cfg {Boolean} preventDefault (true | false) default true
23794  * @cfg {String} icon Font awesome icon
23795  * @cfg {String} pos Submenu align to (left | right) default right 
23796  * 
23797  * 
23798  * @constructor
23799  * Create a new Item
23800  * @param {Object} config The config object
23801  */
23802
23803
23804 Roo.bootstrap.menu.Item = function(config){
23805     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23806     this.addEvents({
23807         /**
23808          * @event mouseover
23809          * Fires when the mouse is hovering over this menu
23810          * @param {Roo.bootstrap.menu.Item} this
23811          * @param {Roo.EventObject} e
23812          */
23813         mouseover : true,
23814         /**
23815          * @event mouseout
23816          * Fires when the mouse exits this menu
23817          * @param {Roo.bootstrap.menu.Item} this
23818          * @param {Roo.EventObject} e
23819          */
23820         mouseout : true,
23821         // raw events
23822         /**
23823          * @event click
23824          * The raw click event for the entire grid.
23825          * @param {Roo.EventObject} e
23826          */
23827         click : true
23828     });
23829 };
23830
23831 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23832     
23833     submenu : false,
23834     href : '',
23835     html : '',
23836     preventDefault: true,
23837     disable : false,
23838     icon : false,
23839     pos : 'right',
23840     
23841     getAutoCreate : function()
23842     {
23843         var text = [
23844             {
23845                 tag : 'span',
23846                 cls : 'roo-menu-item-text',
23847                 html : this.html
23848             }
23849         ];
23850         
23851         if(this.icon){
23852             text.unshift({
23853                 tag : 'i',
23854                 cls : 'fa ' + this.icon
23855             })
23856         }
23857         
23858         var cfg = {
23859             tag : 'li',
23860             cn : [
23861                 {
23862                     tag : 'a',
23863                     href : this.href || '#',
23864                     cn : text
23865                 }
23866             ]
23867         };
23868         
23869         if(this.disable){
23870             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23871         }
23872         
23873         if(this.submenu){
23874             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23875             
23876             if(this.pos == 'left'){
23877                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23878             }
23879         }
23880         
23881         return cfg;
23882     },
23883     
23884     initEvents : function() 
23885     {
23886         this.el.on('mouseover', this.onMouseOver, this);
23887         this.el.on('mouseout', this.onMouseOut, this);
23888         
23889         this.el.select('a', true).first().on('click', this.onClick, this);
23890         
23891     },
23892     
23893     onClick : function(e)
23894     {
23895         if(this.preventDefault){
23896             e.preventDefault();
23897         }
23898         
23899         this.fireEvent("click", this, e);
23900     },
23901     
23902     onMouseOver : function(e)
23903     {
23904         if(this.submenu && this.pos == 'left'){
23905             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23906         }
23907         
23908         this.fireEvent("mouseover", this, e);
23909     },
23910     
23911     onMouseOut : function(e)
23912     {
23913         this.fireEvent("mouseout", this, e);
23914     }
23915 });
23916
23917  
23918
23919  /*
23920  * - LGPL
23921  *
23922  * menu separator
23923  * 
23924  */
23925 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23926
23927 /**
23928  * @class Roo.bootstrap.menu.Separator
23929  * @extends Roo.bootstrap.Component
23930  * Bootstrap Separator class
23931  * 
23932  * @constructor
23933  * Create a new Separator
23934  * @param {Object} config The config object
23935  */
23936
23937
23938 Roo.bootstrap.menu.Separator = function(config){
23939     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23940 };
23941
23942 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23943     
23944     getAutoCreate : function(){
23945         var cfg = {
23946             tag : 'li',
23947             cls: 'divider'
23948         };
23949         
23950         return cfg;
23951     }
23952    
23953 });
23954
23955  
23956
23957  /*
23958  * - LGPL
23959  *
23960  * Tooltip
23961  * 
23962  */
23963
23964 /**
23965  * @class Roo.bootstrap.Tooltip
23966  * Bootstrap Tooltip class
23967  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23968  * to determine which dom element triggers the tooltip.
23969  * 
23970  * It needs to add support for additional attributes like tooltip-position
23971  * 
23972  * @constructor
23973  * Create a new Toolti
23974  * @param {Object} config The config object
23975  */
23976
23977 Roo.bootstrap.Tooltip = function(config){
23978     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23979 };
23980
23981 Roo.apply(Roo.bootstrap.Tooltip, {
23982     /**
23983      * @function init initialize tooltip monitoring.
23984      * @static
23985      */
23986     currentEl : false,
23987     currentTip : false,
23988     currentRegion : false,
23989     
23990     //  init : delay?
23991     
23992     init : function()
23993     {
23994         Roo.get(document).on('mouseover', this.enter ,this);
23995         Roo.get(document).on('mouseout', this.leave, this);
23996          
23997         
23998         this.currentTip = new Roo.bootstrap.Tooltip();
23999     },
24000     
24001     enter : function(ev)
24002     {
24003         var dom = ev.getTarget();
24004         
24005         //Roo.log(['enter',dom]);
24006         var el = Roo.fly(dom);
24007         if (this.currentEl) {
24008             //Roo.log(dom);
24009             //Roo.log(this.currentEl);
24010             //Roo.log(this.currentEl.contains(dom));
24011             if (this.currentEl == el) {
24012                 return;
24013             }
24014             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24015                 return;
24016             }
24017
24018         }
24019         
24020         if (this.currentTip.el) {
24021             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24022         }    
24023         //Roo.log(ev);
24024         var bindEl = el;
24025         
24026         // you can not look for children, as if el is the body.. then everythign is the child..
24027         if (!el.attr('tooltip')) { //
24028             if (!el.select("[tooltip]").elements.length) {
24029                 return;
24030             }
24031             // is the mouse over this child...?
24032             bindEl = el.select("[tooltip]").first();
24033             var xy = ev.getXY();
24034             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24035                 //Roo.log("not in region.");
24036                 return;
24037             }
24038             //Roo.log("child element over..");
24039             
24040         }
24041         this.currentEl = bindEl;
24042         this.currentTip.bind(bindEl);
24043         this.currentRegion = Roo.lib.Region.getRegion(dom);
24044         this.currentTip.enter();
24045         
24046     },
24047     leave : function(ev)
24048     {
24049         var dom = ev.getTarget();
24050         //Roo.log(['leave',dom]);
24051         if (!this.currentEl) {
24052             return;
24053         }
24054         
24055         
24056         if (dom != this.currentEl.dom) {
24057             return;
24058         }
24059         var xy = ev.getXY();
24060         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24061             return;
24062         }
24063         // only activate leave if mouse cursor is outside... bounding box..
24064         
24065         
24066         
24067         
24068         if (this.currentTip) {
24069             this.currentTip.leave();
24070         }
24071         //Roo.log('clear currentEl');
24072         this.currentEl = false;
24073         
24074         
24075     },
24076     alignment : {
24077         'left' : ['r-l', [-2,0], 'right'],
24078         'right' : ['l-r', [2,0], 'left'],
24079         'bottom' : ['t-b', [0,2], 'top'],
24080         'top' : [ 'b-t', [0,-2], 'bottom']
24081     }
24082     
24083 });
24084
24085
24086 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24087     
24088     
24089     bindEl : false,
24090     
24091     delay : null, // can be { show : 300 , hide: 500}
24092     
24093     timeout : null,
24094     
24095     hoverState : null, //???
24096     
24097     placement : 'bottom', 
24098     
24099     getAutoCreate : function(){
24100     
24101         var cfg = {
24102            cls : 'tooltip',
24103            role : 'tooltip',
24104            cn : [
24105                 {
24106                     cls : 'tooltip-arrow'
24107                 },
24108                 {
24109                     cls : 'tooltip-inner'
24110                 }
24111            ]
24112         };
24113         
24114         return cfg;
24115     },
24116     bind : function(el)
24117     {
24118         this.bindEl = el;
24119     },
24120       
24121     
24122     enter : function () {
24123        
24124         if (this.timeout != null) {
24125             clearTimeout(this.timeout);
24126         }
24127         
24128         this.hoverState = 'in';
24129          //Roo.log("enter - show");
24130         if (!this.delay || !this.delay.show) {
24131             this.show();
24132             return;
24133         }
24134         var _t = this;
24135         this.timeout = setTimeout(function () {
24136             if (_t.hoverState == 'in') {
24137                 _t.show();
24138             }
24139         }, this.delay.show);
24140     },
24141     leave : function()
24142     {
24143         clearTimeout(this.timeout);
24144     
24145         this.hoverState = 'out';
24146          if (!this.delay || !this.delay.hide) {
24147             this.hide();
24148             return;
24149         }
24150        
24151         var _t = this;
24152         this.timeout = setTimeout(function () {
24153             //Roo.log("leave - timeout");
24154             
24155             if (_t.hoverState == 'out') {
24156                 _t.hide();
24157                 Roo.bootstrap.Tooltip.currentEl = false;
24158             }
24159         }, delay);
24160     },
24161     
24162     show : function ()
24163     {
24164         if (!this.el) {
24165             this.render(document.body);
24166         }
24167         // set content.
24168         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24169         
24170         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24171         
24172         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24173         
24174         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24175         
24176         var placement = typeof this.placement == 'function' ?
24177             this.placement.call(this, this.el, on_el) :
24178             this.placement;
24179             
24180         var autoToken = /\s?auto?\s?/i;
24181         var autoPlace = autoToken.test(placement);
24182         if (autoPlace) {
24183             placement = placement.replace(autoToken, '') || 'top';
24184         }
24185         
24186         //this.el.detach()
24187         //this.el.setXY([0,0]);
24188         this.el.show();
24189         //this.el.dom.style.display='block';
24190         
24191         //this.el.appendTo(on_el);
24192         
24193         var p = this.getPosition();
24194         var box = this.el.getBox();
24195         
24196         if (autoPlace) {
24197             // fixme..
24198         }
24199         
24200         var align = Roo.bootstrap.Tooltip.alignment[placement];
24201         
24202         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24203         
24204         if(placement == 'top' || placement == 'bottom'){
24205             if(xy[0] < 0){
24206                 placement = 'right';
24207             }
24208             
24209             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24210                 placement = 'left';
24211             }
24212         }
24213         
24214         align = Roo.bootstrap.Tooltip.alignment[placement];
24215         
24216         this.el.alignTo(this.bindEl, align[0],align[1]);
24217         //var arrow = this.el.select('.arrow',true).first();
24218         //arrow.set(align[2], 
24219         
24220         this.el.addClass(placement);
24221         
24222         this.el.addClass('in fade');
24223         
24224         this.hoverState = null;
24225         
24226         if (this.el.hasClass('fade')) {
24227             // fade it?
24228         }
24229         
24230     },
24231     hide : function()
24232     {
24233          
24234         if (!this.el) {
24235             return;
24236         }
24237         //this.el.setXY([0,0]);
24238         this.el.removeClass('in');
24239         //this.el.hide();
24240         
24241     }
24242     
24243 });
24244  
24245
24246  /*
24247  * - LGPL
24248  *
24249  * Location Picker
24250  * 
24251  */
24252
24253 /**
24254  * @class Roo.bootstrap.LocationPicker
24255  * @extends Roo.bootstrap.Component
24256  * Bootstrap LocationPicker class
24257  * @cfg {Number} latitude Position when init default 0
24258  * @cfg {Number} longitude Position when init default 0
24259  * @cfg {Number} zoom default 15
24260  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24261  * @cfg {Boolean} mapTypeControl default false
24262  * @cfg {Boolean} disableDoubleClickZoom default false
24263  * @cfg {Boolean} scrollwheel default true
24264  * @cfg {Boolean} streetViewControl default false
24265  * @cfg {Number} radius default 0
24266  * @cfg {String} locationName
24267  * @cfg {Boolean} draggable default true
24268  * @cfg {Boolean} enableAutocomplete default false
24269  * @cfg {Boolean} enableReverseGeocode default true
24270  * @cfg {String} markerTitle
24271  * 
24272  * @constructor
24273  * Create a new LocationPicker
24274  * @param {Object} config The config object
24275  */
24276
24277
24278 Roo.bootstrap.LocationPicker = function(config){
24279     
24280     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24281     
24282     this.addEvents({
24283         /**
24284          * @event initial
24285          * Fires when the picker initialized.
24286          * @param {Roo.bootstrap.LocationPicker} this
24287          * @param {Google Location} location
24288          */
24289         initial : true,
24290         /**
24291          * @event positionchanged
24292          * Fires when the picker position changed.
24293          * @param {Roo.bootstrap.LocationPicker} this
24294          * @param {Google Location} location
24295          */
24296         positionchanged : true,
24297         /**
24298          * @event resize
24299          * Fires when the map resize.
24300          * @param {Roo.bootstrap.LocationPicker} this
24301          */
24302         resize : true,
24303         /**
24304          * @event show
24305          * Fires when the map show.
24306          * @param {Roo.bootstrap.LocationPicker} this
24307          */
24308         show : true,
24309         /**
24310          * @event hide
24311          * Fires when the map hide.
24312          * @param {Roo.bootstrap.LocationPicker} this
24313          */
24314         hide : true,
24315         /**
24316          * @event mapClick
24317          * Fires when click the map.
24318          * @param {Roo.bootstrap.LocationPicker} this
24319          * @param {Map event} e
24320          */
24321         mapClick : true,
24322         /**
24323          * @event mapRightClick
24324          * Fires when right click the map.
24325          * @param {Roo.bootstrap.LocationPicker} this
24326          * @param {Map event} e
24327          */
24328         mapRightClick : true,
24329         /**
24330          * @event markerClick
24331          * Fires when click the marker.
24332          * @param {Roo.bootstrap.LocationPicker} this
24333          * @param {Map event} e
24334          */
24335         markerClick : true,
24336         /**
24337          * @event markerRightClick
24338          * Fires when right click the marker.
24339          * @param {Roo.bootstrap.LocationPicker} this
24340          * @param {Map event} e
24341          */
24342         markerRightClick : true,
24343         /**
24344          * @event OverlayViewDraw
24345          * Fires when OverlayView Draw
24346          * @param {Roo.bootstrap.LocationPicker} this
24347          */
24348         OverlayViewDraw : true,
24349         /**
24350          * @event OverlayViewOnAdd
24351          * Fires when OverlayView Draw
24352          * @param {Roo.bootstrap.LocationPicker} this
24353          */
24354         OverlayViewOnAdd : true,
24355         /**
24356          * @event OverlayViewOnRemove
24357          * Fires when OverlayView Draw
24358          * @param {Roo.bootstrap.LocationPicker} this
24359          */
24360         OverlayViewOnRemove : true,
24361         /**
24362          * @event OverlayViewShow
24363          * Fires when OverlayView Draw
24364          * @param {Roo.bootstrap.LocationPicker} this
24365          * @param {Pixel} cpx
24366          */
24367         OverlayViewShow : true,
24368         /**
24369          * @event OverlayViewHide
24370          * Fires when OverlayView Draw
24371          * @param {Roo.bootstrap.LocationPicker} this
24372          */
24373         OverlayViewHide : true,
24374         /**
24375          * @event loadexception
24376          * Fires when load google lib failed.
24377          * @param {Roo.bootstrap.LocationPicker} this
24378          */
24379         loadexception : true
24380     });
24381         
24382 };
24383
24384 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24385     
24386     gMapContext: false,
24387     
24388     latitude: 0,
24389     longitude: 0,
24390     zoom: 15,
24391     mapTypeId: false,
24392     mapTypeControl: false,
24393     disableDoubleClickZoom: false,
24394     scrollwheel: true,
24395     streetViewControl: false,
24396     radius: 0,
24397     locationName: '',
24398     draggable: true,
24399     enableAutocomplete: false,
24400     enableReverseGeocode: true,
24401     markerTitle: '',
24402     
24403     getAutoCreate: function()
24404     {
24405
24406         var cfg = {
24407             tag: 'div',
24408             cls: 'roo-location-picker'
24409         };
24410         
24411         return cfg
24412     },
24413     
24414     initEvents: function(ct, position)
24415     {       
24416         if(!this.el.getWidth() || this.isApplied()){
24417             return;
24418         }
24419         
24420         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24421         
24422         this.initial();
24423     },
24424     
24425     initial: function()
24426     {
24427         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24428             this.fireEvent('loadexception', this);
24429             return;
24430         }
24431         
24432         if(!this.mapTypeId){
24433             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24434         }
24435         
24436         this.gMapContext = this.GMapContext();
24437         
24438         this.initOverlayView();
24439         
24440         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24441         
24442         var _this = this;
24443                 
24444         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24445             _this.setPosition(_this.gMapContext.marker.position);
24446         });
24447         
24448         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24449             _this.fireEvent('mapClick', this, event);
24450             
24451         });
24452
24453         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24454             _this.fireEvent('mapRightClick', this, event);
24455             
24456         });
24457         
24458         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24459             _this.fireEvent('markerClick', this, event);
24460             
24461         });
24462
24463         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24464             _this.fireEvent('markerRightClick', this, event);
24465             
24466         });
24467         
24468         this.setPosition(this.gMapContext.location);
24469         
24470         this.fireEvent('initial', this, this.gMapContext.location);
24471     },
24472     
24473     initOverlayView: function()
24474     {
24475         var _this = this;
24476         
24477         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24478             
24479             draw: function()
24480             {
24481                 _this.fireEvent('OverlayViewDraw', _this);
24482             },
24483             
24484             onAdd: function()
24485             {
24486                 _this.fireEvent('OverlayViewOnAdd', _this);
24487             },
24488             
24489             onRemove: function()
24490             {
24491                 _this.fireEvent('OverlayViewOnRemove', _this);
24492             },
24493             
24494             show: function(cpx)
24495             {
24496                 _this.fireEvent('OverlayViewShow', _this, cpx);
24497             },
24498             
24499             hide: function()
24500             {
24501                 _this.fireEvent('OverlayViewHide', _this);
24502             }
24503             
24504         });
24505     },
24506     
24507     fromLatLngToContainerPixel: function(event)
24508     {
24509         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24510     },
24511     
24512     isApplied: function() 
24513     {
24514         return this.getGmapContext() == false ? false : true;
24515     },
24516     
24517     getGmapContext: function() 
24518     {
24519         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24520     },
24521     
24522     GMapContext: function() 
24523     {
24524         var position = new google.maps.LatLng(this.latitude, this.longitude);
24525         
24526         var _map = new google.maps.Map(this.el.dom, {
24527             center: position,
24528             zoom: this.zoom,
24529             mapTypeId: this.mapTypeId,
24530             mapTypeControl: this.mapTypeControl,
24531             disableDoubleClickZoom: this.disableDoubleClickZoom,
24532             scrollwheel: this.scrollwheel,
24533             streetViewControl: this.streetViewControl,
24534             locationName: this.locationName,
24535             draggable: this.draggable,
24536             enableAutocomplete: this.enableAutocomplete,
24537             enableReverseGeocode: this.enableReverseGeocode
24538         });
24539         
24540         var _marker = new google.maps.Marker({
24541             position: position,
24542             map: _map,
24543             title: this.markerTitle,
24544             draggable: this.draggable
24545         });
24546         
24547         return {
24548             map: _map,
24549             marker: _marker,
24550             circle: null,
24551             location: position,
24552             radius: this.radius,
24553             locationName: this.locationName,
24554             addressComponents: {
24555                 formatted_address: null,
24556                 addressLine1: null,
24557                 addressLine2: null,
24558                 streetName: null,
24559                 streetNumber: null,
24560                 city: null,
24561                 district: null,
24562                 state: null,
24563                 stateOrProvince: null
24564             },
24565             settings: this,
24566             domContainer: this.el.dom,
24567             geodecoder: new google.maps.Geocoder()
24568         };
24569     },
24570     
24571     drawCircle: function(center, radius, options) 
24572     {
24573         if (this.gMapContext.circle != null) {
24574             this.gMapContext.circle.setMap(null);
24575         }
24576         if (radius > 0) {
24577             radius *= 1;
24578             options = Roo.apply({}, options, {
24579                 strokeColor: "#0000FF",
24580                 strokeOpacity: .35,
24581                 strokeWeight: 2,
24582                 fillColor: "#0000FF",
24583                 fillOpacity: .2
24584             });
24585             
24586             options.map = this.gMapContext.map;
24587             options.radius = radius;
24588             options.center = center;
24589             this.gMapContext.circle = new google.maps.Circle(options);
24590             return this.gMapContext.circle;
24591         }
24592         
24593         return null;
24594     },
24595     
24596     setPosition: function(location) 
24597     {
24598         this.gMapContext.location = location;
24599         this.gMapContext.marker.setPosition(location);
24600         this.gMapContext.map.panTo(location);
24601         this.drawCircle(location, this.gMapContext.radius, {});
24602         
24603         var _this = this;
24604         
24605         if (this.gMapContext.settings.enableReverseGeocode) {
24606             this.gMapContext.geodecoder.geocode({
24607                 latLng: this.gMapContext.location
24608             }, function(results, status) {
24609                 
24610                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24611                     _this.gMapContext.locationName = results[0].formatted_address;
24612                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24613                     
24614                     _this.fireEvent('positionchanged', this, location);
24615                 }
24616             });
24617             
24618             return;
24619         }
24620         
24621         this.fireEvent('positionchanged', this, location);
24622     },
24623     
24624     resize: function()
24625     {
24626         google.maps.event.trigger(this.gMapContext.map, "resize");
24627         
24628         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24629         
24630         this.fireEvent('resize', this);
24631     },
24632     
24633     setPositionByLatLng: function(latitude, longitude)
24634     {
24635         this.setPosition(new google.maps.LatLng(latitude, longitude));
24636     },
24637     
24638     getCurrentPosition: function() 
24639     {
24640         return {
24641             latitude: this.gMapContext.location.lat(),
24642             longitude: this.gMapContext.location.lng()
24643         };
24644     },
24645     
24646     getAddressName: function() 
24647     {
24648         return this.gMapContext.locationName;
24649     },
24650     
24651     getAddressComponents: function() 
24652     {
24653         return this.gMapContext.addressComponents;
24654     },
24655     
24656     address_component_from_google_geocode: function(address_components) 
24657     {
24658         var result = {};
24659         
24660         for (var i = 0; i < address_components.length; i++) {
24661             var component = address_components[i];
24662             if (component.types.indexOf("postal_code") >= 0) {
24663                 result.postalCode = component.short_name;
24664             } else if (component.types.indexOf("street_number") >= 0) {
24665                 result.streetNumber = component.short_name;
24666             } else if (component.types.indexOf("route") >= 0) {
24667                 result.streetName = component.short_name;
24668             } else if (component.types.indexOf("neighborhood") >= 0) {
24669                 result.city = component.short_name;
24670             } else if (component.types.indexOf("locality") >= 0) {
24671                 result.city = component.short_name;
24672             } else if (component.types.indexOf("sublocality") >= 0) {
24673                 result.district = component.short_name;
24674             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24675                 result.stateOrProvince = component.short_name;
24676             } else if (component.types.indexOf("country") >= 0) {
24677                 result.country = component.short_name;
24678             }
24679         }
24680         
24681         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24682         result.addressLine2 = "";
24683         return result;
24684     },
24685     
24686     setZoomLevel: function(zoom)
24687     {
24688         this.gMapContext.map.setZoom(zoom);
24689     },
24690     
24691     show: function()
24692     {
24693         if(!this.el){
24694             return;
24695         }
24696         
24697         this.el.show();
24698         
24699         this.resize();
24700         
24701         this.fireEvent('show', this);
24702     },
24703     
24704     hide: function()
24705     {
24706         if(!this.el){
24707             return;
24708         }
24709         
24710         this.el.hide();
24711         
24712         this.fireEvent('hide', this);
24713     }
24714     
24715 });
24716
24717 Roo.apply(Roo.bootstrap.LocationPicker, {
24718     
24719     OverlayView : function(map, options)
24720     {
24721         options = options || {};
24722         
24723         this.setMap(map);
24724     }
24725     
24726     
24727 });/*
24728  * - LGPL
24729  *
24730  * Alert
24731  * 
24732  */
24733
24734 /**
24735  * @class Roo.bootstrap.Alert
24736  * @extends Roo.bootstrap.Component
24737  * Bootstrap Alert class
24738  * @cfg {String} title The title of alert
24739  * @cfg {String} html The content of alert
24740  * @cfg {String} weight (  success | info | warning | danger )
24741  * @cfg {String} faicon font-awesomeicon
24742  * 
24743  * @constructor
24744  * Create a new alert
24745  * @param {Object} config The config object
24746  */
24747
24748
24749 Roo.bootstrap.Alert = function(config){
24750     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24751     
24752 };
24753
24754 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24755     
24756     title: '',
24757     html: '',
24758     weight: false,
24759     faicon: false,
24760     
24761     getAutoCreate : function()
24762     {
24763         
24764         var cfg = {
24765             tag : 'div',
24766             cls : 'alert',
24767             cn : [
24768                 {
24769                     tag : 'i',
24770                     cls : 'roo-alert-icon'
24771                     
24772                 },
24773                 {
24774                     tag : 'b',
24775                     cls : 'roo-alert-title',
24776                     html : this.title
24777                 },
24778                 {
24779                     tag : 'span',
24780                     cls : 'roo-alert-text',
24781                     html : this.html
24782                 }
24783             ]
24784         };
24785         
24786         if(this.faicon){
24787             cfg.cn[0].cls += ' fa ' + this.faicon;
24788         }
24789         
24790         if(this.weight){
24791             cfg.cls += ' alert-' + this.weight;
24792         }
24793         
24794         return cfg;
24795     },
24796     
24797     initEvents: function() 
24798     {
24799         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24800     },
24801     
24802     setTitle : function(str)
24803     {
24804         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24805     },
24806     
24807     setText : function(str)
24808     {
24809         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24810     },
24811     
24812     setWeight : function(weight)
24813     {
24814         if(this.weight){
24815             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24816         }
24817         
24818         this.weight = weight;
24819         
24820         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24821     },
24822     
24823     setIcon : function(icon)
24824     {
24825         if(this.faicon){
24826             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24827         }
24828         
24829         this.faicon = icon;
24830         
24831         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24832     },
24833     
24834     hide: function() 
24835     {
24836         this.el.hide();   
24837     },
24838     
24839     show: function() 
24840     {  
24841         this.el.show();   
24842     }
24843     
24844 });
24845
24846  
24847 /*
24848 * Licence: LGPL
24849 */
24850
24851 /**
24852  * @class Roo.bootstrap.UploadCropbox
24853  * @extends Roo.bootstrap.Component
24854  * Bootstrap UploadCropbox class
24855  * @cfg {String} emptyText show when image has been loaded
24856  * @cfg {String} rotateNotify show when image too small to rotate
24857  * @cfg {Number} errorTimeout default 3000
24858  * @cfg {Number} minWidth default 300
24859  * @cfg {Number} minHeight default 300
24860  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24861  * @cfg {Boolean} isDocument (true|false) default false
24862  * @cfg {String} url action url
24863  * @cfg {String} paramName default 'imageUpload'
24864  * @cfg {String} method default POST
24865  * @cfg {Boolean} loadMask (true|false) default true
24866  * @cfg {Boolean} loadingText default 'Loading...'
24867  * 
24868  * @constructor
24869  * Create a new UploadCropbox
24870  * @param {Object} config The config object
24871  */
24872
24873 Roo.bootstrap.UploadCropbox = function(config){
24874     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24875     
24876     this.addEvents({
24877         /**
24878          * @event beforeselectfile
24879          * Fire before select file
24880          * @param {Roo.bootstrap.UploadCropbox} this
24881          */
24882         "beforeselectfile" : true,
24883         /**
24884          * @event initial
24885          * Fire after initEvent
24886          * @param {Roo.bootstrap.UploadCropbox} this
24887          */
24888         "initial" : true,
24889         /**
24890          * @event crop
24891          * Fire after initEvent
24892          * @param {Roo.bootstrap.UploadCropbox} this
24893          * @param {String} data
24894          */
24895         "crop" : true,
24896         /**
24897          * @event prepare
24898          * Fire when preparing the file data
24899          * @param {Roo.bootstrap.UploadCropbox} this
24900          * @param {Object} file
24901          */
24902         "prepare" : true,
24903         /**
24904          * @event exception
24905          * Fire when get exception
24906          * @param {Roo.bootstrap.UploadCropbox} this
24907          * @param {XMLHttpRequest} xhr
24908          */
24909         "exception" : true,
24910         /**
24911          * @event beforeloadcanvas
24912          * Fire before load the canvas
24913          * @param {Roo.bootstrap.UploadCropbox} this
24914          * @param {String} src
24915          */
24916         "beforeloadcanvas" : true,
24917         /**
24918          * @event trash
24919          * Fire when trash image
24920          * @param {Roo.bootstrap.UploadCropbox} this
24921          */
24922         "trash" : true,
24923         /**
24924          * @event download
24925          * Fire when download the image
24926          * @param {Roo.bootstrap.UploadCropbox} this
24927          */
24928         "download" : true,
24929         /**
24930          * @event footerbuttonclick
24931          * Fire when footerbuttonclick
24932          * @param {Roo.bootstrap.UploadCropbox} this
24933          * @param {String} type
24934          */
24935         "footerbuttonclick" : true,
24936         /**
24937          * @event resize
24938          * Fire when resize
24939          * @param {Roo.bootstrap.UploadCropbox} this
24940          */
24941         "resize" : true,
24942         /**
24943          * @event rotate
24944          * Fire when rotate the image
24945          * @param {Roo.bootstrap.UploadCropbox} this
24946          * @param {String} pos
24947          */
24948         "rotate" : true,
24949         /**
24950          * @event inspect
24951          * Fire when inspect the file
24952          * @param {Roo.bootstrap.UploadCropbox} this
24953          * @param {Object} file
24954          */
24955         "inspect" : true,
24956         /**
24957          * @event upload
24958          * Fire when xhr upload the file
24959          * @param {Roo.bootstrap.UploadCropbox} this
24960          * @param {Object} data
24961          */
24962         "upload" : true,
24963         /**
24964          * @event arrange
24965          * Fire when arrange the file data
24966          * @param {Roo.bootstrap.UploadCropbox} this
24967          * @param {Object} formData
24968          */
24969         "arrange" : true
24970     });
24971     
24972     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24973 };
24974
24975 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24976     
24977     emptyText : 'Click to upload image',
24978     rotateNotify : 'Image is too small to rotate',
24979     errorTimeout : 3000,
24980     scale : 0,
24981     baseScale : 1,
24982     rotate : 0,
24983     dragable : false,
24984     pinching : false,
24985     mouseX : 0,
24986     mouseY : 0,
24987     cropData : false,
24988     minWidth : 300,
24989     minHeight : 300,
24990     file : false,
24991     exif : {},
24992     baseRotate : 1,
24993     cropType : 'image/jpeg',
24994     buttons : false,
24995     canvasLoaded : false,
24996     isDocument : false,
24997     method : 'POST',
24998     paramName : 'imageUpload',
24999     loadMask : true,
25000     loadingText : 'Loading...',
25001     maskEl : false,
25002     
25003     getAutoCreate : function()
25004     {
25005         var cfg = {
25006             tag : 'div',
25007             cls : 'roo-upload-cropbox',
25008             cn : [
25009                 {
25010                     tag : 'input',
25011                     cls : 'roo-upload-cropbox-selector',
25012                     type : 'file'
25013                 },
25014                 {
25015                     tag : 'div',
25016                     cls : 'roo-upload-cropbox-body',
25017                     style : 'cursor:pointer',
25018                     cn : [
25019                         {
25020                             tag : 'div',
25021                             cls : 'roo-upload-cropbox-preview'
25022                         },
25023                         {
25024                             tag : 'div',
25025                             cls : 'roo-upload-cropbox-thumb'
25026                         },
25027                         {
25028                             tag : 'div',
25029                             cls : 'roo-upload-cropbox-empty-notify',
25030                             html : this.emptyText
25031                         },
25032                         {
25033                             tag : 'div',
25034                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25035                             html : this.rotateNotify
25036                         }
25037                     ]
25038                 },
25039                 {
25040                     tag : 'div',
25041                     cls : 'roo-upload-cropbox-footer',
25042                     cn : {
25043                         tag : 'div',
25044                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25045                         cn : []
25046                     }
25047                 }
25048             ]
25049         };
25050         
25051         return cfg;
25052     },
25053     
25054     onRender : function(ct, position)
25055     {
25056         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25057         
25058         if (this.buttons.length) {
25059             
25060             Roo.each(this.buttons, function(bb) {
25061                 
25062                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25063                 
25064                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25065                 
25066             }, this);
25067         }
25068         
25069         if(this.loadMask){
25070             this.maskEl = this.el;
25071         }
25072     },
25073     
25074     initEvents : function()
25075     {
25076         this.urlAPI = (window.createObjectURL && window) || 
25077                                 (window.URL && URL.revokeObjectURL && URL) || 
25078                                 (window.webkitURL && webkitURL);
25079                         
25080         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25081         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25082         
25083         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25084         this.selectorEl.hide();
25085         
25086         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25087         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25088         
25089         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25090         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25091         this.thumbEl.hide();
25092         
25093         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25094         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25095         
25096         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25097         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25098         this.errorEl.hide();
25099         
25100         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25101         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25102         this.footerEl.hide();
25103         
25104         this.setThumbBoxSize();
25105         
25106         this.bind();
25107         
25108         this.resize();
25109         
25110         this.fireEvent('initial', this);
25111     },
25112
25113     bind : function()
25114     {
25115         var _this = this;
25116         
25117         window.addEventListener("resize", function() { _this.resize(); } );
25118         
25119         this.bodyEl.on('click', this.beforeSelectFile, this);
25120         
25121         if(Roo.isTouch){
25122             this.bodyEl.on('touchstart', this.onTouchStart, this);
25123             this.bodyEl.on('touchmove', this.onTouchMove, this);
25124             this.bodyEl.on('touchend', this.onTouchEnd, this);
25125         }
25126         
25127         if(!Roo.isTouch){
25128             this.bodyEl.on('mousedown', this.onMouseDown, this);
25129             this.bodyEl.on('mousemove', this.onMouseMove, this);
25130             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25131             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25132             Roo.get(document).on('mouseup', this.onMouseUp, this);
25133         }
25134         
25135         this.selectorEl.on('change', this.onFileSelected, this);
25136     },
25137     
25138     reset : function()
25139     {    
25140         this.scale = 0;
25141         this.baseScale = 1;
25142         this.rotate = 0;
25143         this.baseRotate = 1;
25144         this.dragable = false;
25145         this.pinching = false;
25146         this.mouseX = 0;
25147         this.mouseY = 0;
25148         this.cropData = false;
25149         this.notifyEl.dom.innerHTML = this.emptyText;
25150         
25151         this.selectorEl.dom.value = '';
25152         
25153     },
25154     
25155     resize : function()
25156     {
25157         if(this.fireEvent('resize', this) != false){
25158             this.setThumbBoxPosition();
25159             this.setCanvasPosition();
25160         }
25161     },
25162     
25163     onFooterButtonClick : function(e, el, o, type)
25164     {
25165         switch (type) {
25166             case 'rotate-left' :
25167                 this.onRotateLeft(e);
25168                 break;
25169             case 'rotate-right' :
25170                 this.onRotateRight(e);
25171                 break;
25172             case 'picture' :
25173                 this.beforeSelectFile(e);
25174                 break;
25175             case 'trash' :
25176                 this.trash(e);
25177                 break;
25178             case 'crop' :
25179                 this.crop(e);
25180                 break;
25181             case 'download' :
25182                 this.download(e);
25183                 break;
25184             default :
25185                 break;
25186         }
25187         
25188         this.fireEvent('footerbuttonclick', this, type);
25189     },
25190     
25191     beforeSelectFile : function(e)
25192     {
25193         e.preventDefault();
25194         
25195         if(this.fireEvent('beforeselectfile', this) != false){
25196             this.selectorEl.dom.click();
25197         }
25198     },
25199     
25200     onFileSelected : function(e)
25201     {
25202         e.preventDefault();
25203         
25204         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25205             return;
25206         }
25207         
25208         var file = this.selectorEl.dom.files[0];
25209         
25210         if(this.fireEvent('inspect', this, file) != false){
25211             this.prepare(file);
25212         }
25213         
25214     },
25215     
25216     trash : function(e)
25217     {
25218         this.fireEvent('trash', this);
25219     },
25220     
25221     download : function(e)
25222     {
25223         this.fireEvent('download', this);
25224     },
25225     
25226     loadCanvas : function(src)
25227     {   
25228         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25229             
25230             this.reset();
25231             
25232             this.imageEl = document.createElement('img');
25233             
25234             var _this = this;
25235             
25236             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25237             
25238             this.imageEl.src = src;
25239         }
25240     },
25241     
25242     onLoadCanvas : function()
25243     {   
25244         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25245         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25246         
25247         this.bodyEl.un('click', this.beforeSelectFile, this);
25248         
25249         this.notifyEl.hide();
25250         this.thumbEl.show();
25251         this.footerEl.show();
25252         
25253         this.baseRotateLevel();
25254         
25255         if(this.isDocument){
25256             this.setThumbBoxSize();
25257         }
25258         
25259         this.setThumbBoxPosition();
25260         
25261         this.baseScaleLevel();
25262         
25263         this.draw();
25264         
25265         this.resize();
25266         
25267         this.canvasLoaded = true;
25268         
25269         if(this.loadMask){
25270             this.maskEl.unmask();
25271         }
25272         
25273     },
25274     
25275     setCanvasPosition : function()
25276     {   
25277         if(!this.canvasEl){
25278             return;
25279         }
25280         
25281         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25282         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25283         
25284         this.previewEl.setLeft(pw);
25285         this.previewEl.setTop(ph);
25286         
25287     },
25288     
25289     onMouseDown : function(e)
25290     {   
25291         e.stopEvent();
25292         
25293         this.dragable = true;
25294         this.pinching = false;
25295         
25296         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25297             this.dragable = false;
25298             return;
25299         }
25300         
25301         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25302         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25303         
25304     },
25305     
25306     onMouseMove : function(e)
25307     {   
25308         e.stopEvent();
25309         
25310         if(!this.canvasLoaded){
25311             return;
25312         }
25313         
25314         if (!this.dragable){
25315             return;
25316         }
25317         
25318         var minX = Math.ceil(this.thumbEl.getLeft(true));
25319         var minY = Math.ceil(this.thumbEl.getTop(true));
25320         
25321         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25322         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25323         
25324         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25325         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25326         
25327         x = x - this.mouseX;
25328         y = y - this.mouseY;
25329         
25330         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25331         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25332         
25333         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25334         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25335         
25336         this.previewEl.setLeft(bgX);
25337         this.previewEl.setTop(bgY);
25338         
25339         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25340         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25341     },
25342     
25343     onMouseUp : function(e)
25344     {   
25345         e.stopEvent();
25346         
25347         this.dragable = false;
25348     },
25349     
25350     onMouseWheel : function(e)
25351     {   
25352         e.stopEvent();
25353         
25354         this.startScale = this.scale;
25355         
25356         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25357         
25358         if(!this.zoomable()){
25359             this.scale = this.startScale;
25360             return;
25361         }
25362         
25363         this.draw();
25364         
25365         return;
25366     },
25367     
25368     zoomable : function()
25369     {
25370         var minScale = this.thumbEl.getWidth() / this.minWidth;
25371         
25372         if(this.minWidth < this.minHeight){
25373             minScale = this.thumbEl.getHeight() / this.minHeight;
25374         }
25375         
25376         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25377         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25378         
25379         if(
25380                 this.isDocument &&
25381                 (this.rotate == 0 || this.rotate == 180) && 
25382                 (
25383                     width > this.imageEl.OriginWidth || 
25384                     height > this.imageEl.OriginHeight ||
25385                     (width < this.minWidth && height < this.minHeight)
25386                 )
25387         ){
25388             return false;
25389         }
25390         
25391         if(
25392                 this.isDocument &&
25393                 (this.rotate == 90 || this.rotate == 270) && 
25394                 (
25395                     width > this.imageEl.OriginWidth || 
25396                     height > this.imageEl.OriginHeight ||
25397                     (width < this.minHeight && height < this.minWidth)
25398                 )
25399         ){
25400             return false;
25401         }
25402         
25403         if(
25404                 !this.isDocument &&
25405                 (this.rotate == 0 || this.rotate == 180) && 
25406                 (
25407                     width < this.minWidth || 
25408                     width > this.imageEl.OriginWidth || 
25409                     height < this.minHeight || 
25410                     height > this.imageEl.OriginHeight
25411                 )
25412         ){
25413             return false;
25414         }
25415         
25416         if(
25417                 !this.isDocument &&
25418                 (this.rotate == 90 || this.rotate == 270) && 
25419                 (
25420                     width < this.minHeight || 
25421                     width > this.imageEl.OriginWidth || 
25422                     height < this.minWidth || 
25423                     height > this.imageEl.OriginHeight
25424                 )
25425         ){
25426             return false;
25427         }
25428         
25429         return true;
25430         
25431     },
25432     
25433     onRotateLeft : function(e)
25434     {   
25435         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25436             
25437             var minScale = this.thumbEl.getWidth() / this.minWidth;
25438             
25439             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25440             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25441             
25442             this.startScale = this.scale;
25443             
25444             while (this.getScaleLevel() < minScale){
25445             
25446                 this.scale = this.scale + 1;
25447                 
25448                 if(!this.zoomable()){
25449                     break;
25450                 }
25451                 
25452                 if(
25453                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25454                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25455                 ){
25456                     continue;
25457                 }
25458                 
25459                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25460
25461                 this.draw();
25462                 
25463                 return;
25464             }
25465             
25466             this.scale = this.startScale;
25467             
25468             this.onRotateFail();
25469             
25470             return false;
25471         }
25472         
25473         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25474
25475         if(this.isDocument){
25476             this.setThumbBoxSize();
25477             this.setThumbBoxPosition();
25478             this.setCanvasPosition();
25479         }
25480         
25481         this.draw();
25482         
25483         this.fireEvent('rotate', this, 'left');
25484         
25485     },
25486     
25487     onRotateRight : function(e)
25488     {
25489         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25490             
25491             var minScale = this.thumbEl.getWidth() / this.minWidth;
25492         
25493             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25494             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25495             
25496             this.startScale = this.scale;
25497             
25498             while (this.getScaleLevel() < minScale){
25499             
25500                 this.scale = this.scale + 1;
25501                 
25502                 if(!this.zoomable()){
25503                     break;
25504                 }
25505                 
25506                 if(
25507                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25508                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25509                 ){
25510                     continue;
25511                 }
25512                 
25513                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25514
25515                 this.draw();
25516                 
25517                 return;
25518             }
25519             
25520             this.scale = this.startScale;
25521             
25522             this.onRotateFail();
25523             
25524             return false;
25525         }
25526         
25527         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25528
25529         if(this.isDocument){
25530             this.setThumbBoxSize();
25531             this.setThumbBoxPosition();
25532             this.setCanvasPosition();
25533         }
25534         
25535         this.draw();
25536         
25537         this.fireEvent('rotate', this, 'right');
25538     },
25539     
25540     onRotateFail : function()
25541     {
25542         this.errorEl.show(true);
25543         
25544         var _this = this;
25545         
25546         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25547     },
25548     
25549     draw : function()
25550     {
25551         this.previewEl.dom.innerHTML = '';
25552         
25553         var canvasEl = document.createElement("canvas");
25554         
25555         var contextEl = canvasEl.getContext("2d");
25556         
25557         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25558         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25559         var center = this.imageEl.OriginWidth / 2;
25560         
25561         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25562             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25563             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25564             center = this.imageEl.OriginHeight / 2;
25565         }
25566         
25567         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25568         
25569         contextEl.translate(center, center);
25570         contextEl.rotate(this.rotate * Math.PI / 180);
25571
25572         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25573         
25574         this.canvasEl = document.createElement("canvas");
25575         
25576         this.contextEl = this.canvasEl.getContext("2d");
25577         
25578         switch (this.rotate) {
25579             case 0 :
25580                 
25581                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25582                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25583                 
25584                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25585                 
25586                 break;
25587             case 90 : 
25588                 
25589                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25590                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25591                 
25592                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25593                     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);
25594                     break;
25595                 }
25596                 
25597                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25598                 
25599                 break;
25600             case 180 :
25601                 
25602                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25603                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25604                 
25605                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25606                     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);
25607                     break;
25608                 }
25609                 
25610                 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);
25611                 
25612                 break;
25613             case 270 :
25614                 
25615                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25616                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25617         
25618                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25619                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25620                     break;
25621                 }
25622                 
25623                 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);
25624                 
25625                 break;
25626             default : 
25627                 break;
25628         }
25629         
25630         this.previewEl.appendChild(this.canvasEl);
25631         
25632         this.setCanvasPosition();
25633     },
25634     
25635     crop : function()
25636     {
25637         if(!this.canvasLoaded){
25638             return;
25639         }
25640         
25641         var imageCanvas = document.createElement("canvas");
25642         
25643         var imageContext = imageCanvas.getContext("2d");
25644         
25645         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25646         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25647         
25648         var center = imageCanvas.width / 2;
25649         
25650         imageContext.translate(center, center);
25651         
25652         imageContext.rotate(this.rotate * Math.PI / 180);
25653         
25654         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25655         
25656         var canvas = document.createElement("canvas");
25657         
25658         var context = canvas.getContext("2d");
25659                 
25660         canvas.width = this.minWidth;
25661         canvas.height = this.minHeight;
25662
25663         switch (this.rotate) {
25664             case 0 :
25665                 
25666                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25667                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25668                 
25669                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25670                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25671                 
25672                 var targetWidth = this.minWidth - 2 * x;
25673                 var targetHeight = this.minHeight - 2 * y;
25674                 
25675                 var scale = 1;
25676                 
25677                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25678                     scale = targetWidth / width;
25679                 }
25680                 
25681                 if(x > 0 && y == 0){
25682                     scale = targetHeight / height;
25683                 }
25684                 
25685                 if(x > 0 && y > 0){
25686                     scale = targetWidth / width;
25687                     
25688                     if(width < height){
25689                         scale = targetHeight / height;
25690                     }
25691                 }
25692                 
25693                 context.scale(scale, scale);
25694                 
25695                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25696                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25697
25698                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25699                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25700
25701                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25702                 
25703                 break;
25704             case 90 : 
25705                 
25706                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25707                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25708                 
25709                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25710                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25711                 
25712                 var targetWidth = this.minWidth - 2 * x;
25713                 var targetHeight = this.minHeight - 2 * y;
25714                 
25715                 var scale = 1;
25716                 
25717                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25718                     scale = targetWidth / width;
25719                 }
25720                 
25721                 if(x > 0 && y == 0){
25722                     scale = targetHeight / height;
25723                 }
25724                 
25725                 if(x > 0 && y > 0){
25726                     scale = targetWidth / width;
25727                     
25728                     if(width < height){
25729                         scale = targetHeight / height;
25730                     }
25731                 }
25732                 
25733                 context.scale(scale, scale);
25734                 
25735                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25736                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25737
25738                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25739                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25740                 
25741                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25742                 
25743                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25744                 
25745                 break;
25746             case 180 :
25747                 
25748                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25749                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25750                 
25751                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25752                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25753                 
25754                 var targetWidth = this.minWidth - 2 * x;
25755                 var targetHeight = this.minHeight - 2 * y;
25756                 
25757                 var scale = 1;
25758                 
25759                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25760                     scale = targetWidth / width;
25761                 }
25762                 
25763                 if(x > 0 && y == 0){
25764                     scale = targetHeight / height;
25765                 }
25766                 
25767                 if(x > 0 && y > 0){
25768                     scale = targetWidth / width;
25769                     
25770                     if(width < height){
25771                         scale = targetHeight / height;
25772                     }
25773                 }
25774                 
25775                 context.scale(scale, scale);
25776                 
25777                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25778                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25779
25780                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25781                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25782
25783                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25784                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25785                 
25786                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25787                 
25788                 break;
25789             case 270 :
25790                 
25791                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25792                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25793                 
25794                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25795                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25796                 
25797                 var targetWidth = this.minWidth - 2 * x;
25798                 var targetHeight = this.minHeight - 2 * y;
25799                 
25800                 var scale = 1;
25801                 
25802                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25803                     scale = targetWidth / width;
25804                 }
25805                 
25806                 if(x > 0 && y == 0){
25807                     scale = targetHeight / height;
25808                 }
25809                 
25810                 if(x > 0 && y > 0){
25811                     scale = targetWidth / width;
25812                     
25813                     if(width < height){
25814                         scale = targetHeight / height;
25815                     }
25816                 }
25817                 
25818                 context.scale(scale, scale);
25819                 
25820                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25821                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25822
25823                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25824                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25825                 
25826                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25827                 
25828                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25829                 
25830                 break;
25831             default : 
25832                 break;
25833         }
25834         
25835         this.cropData = canvas.toDataURL(this.cropType);
25836         
25837         if(this.fireEvent('crop', this, this.cropData) !== false){
25838             this.process(this.file, this.cropData);
25839         }
25840         
25841         return;
25842         
25843     },
25844     
25845     setThumbBoxSize : function()
25846     {
25847         var width, height;
25848         
25849         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25850             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25851             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25852             
25853             this.minWidth = width;
25854             this.minHeight = height;
25855             
25856             if(this.rotate == 90 || this.rotate == 270){
25857                 this.minWidth = height;
25858                 this.minHeight = width;
25859             }
25860         }
25861         
25862         height = 300;
25863         width = Math.ceil(this.minWidth * height / this.minHeight);
25864         
25865         if(this.minWidth > this.minHeight){
25866             width = 300;
25867             height = Math.ceil(this.minHeight * width / this.minWidth);
25868         }
25869         
25870         this.thumbEl.setStyle({
25871             width : width + 'px',
25872             height : height + 'px'
25873         });
25874
25875         return;
25876             
25877     },
25878     
25879     setThumbBoxPosition : function()
25880     {
25881         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25882         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25883         
25884         this.thumbEl.setLeft(x);
25885         this.thumbEl.setTop(y);
25886         
25887     },
25888     
25889     baseRotateLevel : function()
25890     {
25891         this.baseRotate = 1;
25892         
25893         if(
25894                 typeof(this.exif) != 'undefined' &&
25895                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25896                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25897         ){
25898             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25899         }
25900         
25901         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25902         
25903     },
25904     
25905     baseScaleLevel : function()
25906     {
25907         var width, height;
25908         
25909         if(this.isDocument){
25910             
25911             if(this.baseRotate == 6 || this.baseRotate == 8){
25912             
25913                 height = this.thumbEl.getHeight();
25914                 this.baseScale = height / this.imageEl.OriginWidth;
25915
25916                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25917                     width = this.thumbEl.getWidth();
25918                     this.baseScale = width / this.imageEl.OriginHeight;
25919                 }
25920
25921                 return;
25922             }
25923
25924             height = this.thumbEl.getHeight();
25925             this.baseScale = height / this.imageEl.OriginHeight;
25926
25927             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25928                 width = this.thumbEl.getWidth();
25929                 this.baseScale = width / this.imageEl.OriginWidth;
25930             }
25931
25932             return;
25933         }
25934         
25935         if(this.baseRotate == 6 || this.baseRotate == 8){
25936             
25937             width = this.thumbEl.getHeight();
25938             this.baseScale = width / this.imageEl.OriginHeight;
25939             
25940             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25941                 height = this.thumbEl.getWidth();
25942                 this.baseScale = height / this.imageEl.OriginHeight;
25943             }
25944             
25945             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25946                 height = this.thumbEl.getWidth();
25947                 this.baseScale = height / this.imageEl.OriginHeight;
25948                 
25949                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25950                     width = this.thumbEl.getHeight();
25951                     this.baseScale = width / this.imageEl.OriginWidth;
25952                 }
25953             }
25954             
25955             return;
25956         }
25957         
25958         width = this.thumbEl.getWidth();
25959         this.baseScale = width / this.imageEl.OriginWidth;
25960         
25961         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25962             height = this.thumbEl.getHeight();
25963             this.baseScale = height / this.imageEl.OriginHeight;
25964         }
25965         
25966         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25967             
25968             height = this.thumbEl.getHeight();
25969             this.baseScale = height / this.imageEl.OriginHeight;
25970             
25971             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25972                 width = this.thumbEl.getWidth();
25973                 this.baseScale = width / this.imageEl.OriginWidth;
25974             }
25975             
25976         }
25977         
25978         return;
25979     },
25980     
25981     getScaleLevel : function()
25982     {
25983         return this.baseScale * Math.pow(1.1, this.scale);
25984     },
25985     
25986     onTouchStart : function(e)
25987     {
25988         if(!this.canvasLoaded){
25989             this.beforeSelectFile(e);
25990             return;
25991         }
25992         
25993         var touches = e.browserEvent.touches;
25994         
25995         if(!touches){
25996             return;
25997         }
25998         
25999         if(touches.length == 1){
26000             this.onMouseDown(e);
26001             return;
26002         }
26003         
26004         if(touches.length != 2){
26005             return;
26006         }
26007         
26008         var coords = [];
26009         
26010         for(var i = 0, finger; finger = touches[i]; i++){
26011             coords.push(finger.pageX, finger.pageY);
26012         }
26013         
26014         var x = Math.pow(coords[0] - coords[2], 2);
26015         var y = Math.pow(coords[1] - coords[3], 2);
26016         
26017         this.startDistance = Math.sqrt(x + y);
26018         
26019         this.startScale = this.scale;
26020         
26021         this.pinching = true;
26022         this.dragable = false;
26023         
26024     },
26025     
26026     onTouchMove : function(e)
26027     {
26028         if(!this.pinching && !this.dragable){
26029             return;
26030         }
26031         
26032         var touches = e.browserEvent.touches;
26033         
26034         if(!touches){
26035             return;
26036         }
26037         
26038         if(this.dragable){
26039             this.onMouseMove(e);
26040             return;
26041         }
26042         
26043         var coords = [];
26044         
26045         for(var i = 0, finger; finger = touches[i]; i++){
26046             coords.push(finger.pageX, finger.pageY);
26047         }
26048         
26049         var x = Math.pow(coords[0] - coords[2], 2);
26050         var y = Math.pow(coords[1] - coords[3], 2);
26051         
26052         this.endDistance = Math.sqrt(x + y);
26053         
26054         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26055         
26056         if(!this.zoomable()){
26057             this.scale = this.startScale;
26058             return;
26059         }
26060         
26061         this.draw();
26062         
26063     },
26064     
26065     onTouchEnd : function(e)
26066     {
26067         this.pinching = false;
26068         this.dragable = false;
26069         
26070     },
26071     
26072     process : function(file, crop)
26073     {
26074         if(this.loadMask){
26075             this.maskEl.mask(this.loadingText);
26076         }
26077         
26078         this.xhr = new XMLHttpRequest();
26079         
26080         file.xhr = this.xhr;
26081
26082         this.xhr.open(this.method, this.url, true);
26083         
26084         var headers = {
26085             "Accept": "application/json",
26086             "Cache-Control": "no-cache",
26087             "X-Requested-With": "XMLHttpRequest"
26088         };
26089         
26090         for (var headerName in headers) {
26091             var headerValue = headers[headerName];
26092             if (headerValue) {
26093                 this.xhr.setRequestHeader(headerName, headerValue);
26094             }
26095         }
26096         
26097         var _this = this;
26098         
26099         this.xhr.onload = function()
26100         {
26101             _this.xhrOnLoad(_this.xhr);
26102         }
26103         
26104         this.xhr.onerror = function()
26105         {
26106             _this.xhrOnError(_this.xhr);
26107         }
26108         
26109         var formData = new FormData();
26110
26111         formData.append('returnHTML', 'NO');
26112         
26113         if(crop){
26114             formData.append('crop', crop);
26115         }
26116         
26117         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26118             formData.append(this.paramName, file, file.name);
26119         }
26120         
26121         if(typeof(file.filename) != 'undefined'){
26122             formData.append('filename', file.filename);
26123         }
26124         
26125         if(typeof(file.mimetype) != 'undefined'){
26126             formData.append('mimetype', file.mimetype);
26127         }
26128         
26129         if(this.fireEvent('arrange', this, formData) != false){
26130             this.xhr.send(formData);
26131         };
26132     },
26133     
26134     xhrOnLoad : function(xhr)
26135     {
26136         if(this.loadMask){
26137             this.maskEl.unmask();
26138         }
26139         
26140         if (xhr.readyState !== 4) {
26141             this.fireEvent('exception', this, xhr);
26142             return;
26143         }
26144
26145         var response = Roo.decode(xhr.responseText);
26146         
26147         if(!response.success){
26148             this.fireEvent('exception', this, xhr);
26149             return;
26150         }
26151         
26152         var response = Roo.decode(xhr.responseText);
26153         
26154         this.fireEvent('upload', this, response);
26155         
26156     },
26157     
26158     xhrOnError : function()
26159     {
26160         if(this.loadMask){
26161             this.maskEl.unmask();
26162         }
26163         
26164         Roo.log('xhr on error');
26165         
26166         var response = Roo.decode(xhr.responseText);
26167           
26168         Roo.log(response);
26169         
26170     },
26171     
26172     prepare : function(file)
26173     {   
26174         if(this.loadMask){
26175             this.maskEl.mask(this.loadingText);
26176         }
26177         
26178         this.file = false;
26179         this.exif = {};
26180         
26181         if(typeof(file) === 'string'){
26182             this.loadCanvas(file);
26183             return;
26184         }
26185         
26186         if(!file || !this.urlAPI){
26187             return;
26188         }
26189         
26190         this.file = file;
26191         this.cropType = file.type;
26192         
26193         var _this = this;
26194         
26195         if(this.fireEvent('prepare', this, this.file) != false){
26196             
26197             var reader = new FileReader();
26198             
26199             reader.onload = function (e) {
26200                 if (e.target.error) {
26201                     Roo.log(e.target.error);
26202                     return;
26203                 }
26204                 
26205                 var buffer = e.target.result,
26206                     dataView = new DataView(buffer),
26207                     offset = 2,
26208                     maxOffset = dataView.byteLength - 4,
26209                     markerBytes,
26210                     markerLength;
26211                 
26212                 if (dataView.getUint16(0) === 0xffd8) {
26213                     while (offset < maxOffset) {
26214                         markerBytes = dataView.getUint16(offset);
26215                         
26216                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26217                             markerLength = dataView.getUint16(offset + 2) + 2;
26218                             if (offset + markerLength > dataView.byteLength) {
26219                                 Roo.log('Invalid meta data: Invalid segment size.');
26220                                 break;
26221                             }
26222                             
26223                             if(markerBytes == 0xffe1){
26224                                 _this.parseExifData(
26225                                     dataView,
26226                                     offset,
26227                                     markerLength
26228                                 );
26229                             }
26230                             
26231                             offset += markerLength;
26232                             
26233                             continue;
26234                         }
26235                         
26236                         break;
26237                     }
26238                     
26239                 }
26240                 
26241                 var url = _this.urlAPI.createObjectURL(_this.file);
26242                 
26243                 _this.loadCanvas(url);
26244                 
26245                 return;
26246             }
26247             
26248             reader.readAsArrayBuffer(this.file);
26249             
26250         }
26251         
26252     },
26253     
26254     parseExifData : function(dataView, offset, length)
26255     {
26256         var tiffOffset = offset + 10,
26257             littleEndian,
26258             dirOffset;
26259     
26260         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26261             // No Exif data, might be XMP data instead
26262             return;
26263         }
26264         
26265         // Check for the ASCII code for "Exif" (0x45786966):
26266         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26267             // No Exif data, might be XMP data instead
26268             return;
26269         }
26270         if (tiffOffset + 8 > dataView.byteLength) {
26271             Roo.log('Invalid Exif data: Invalid segment size.');
26272             return;
26273         }
26274         // Check for the two null bytes:
26275         if (dataView.getUint16(offset + 8) !== 0x0000) {
26276             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26277             return;
26278         }
26279         // Check the byte alignment:
26280         switch (dataView.getUint16(tiffOffset)) {
26281         case 0x4949:
26282             littleEndian = true;
26283             break;
26284         case 0x4D4D:
26285             littleEndian = false;
26286             break;
26287         default:
26288             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26289             return;
26290         }
26291         // Check for the TIFF tag marker (0x002A):
26292         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26293             Roo.log('Invalid Exif data: Missing TIFF marker.');
26294             return;
26295         }
26296         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26297         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26298         
26299         this.parseExifTags(
26300             dataView,
26301             tiffOffset,
26302             tiffOffset + dirOffset,
26303             littleEndian
26304         );
26305     },
26306     
26307     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26308     {
26309         var tagsNumber,
26310             dirEndOffset,
26311             i;
26312         if (dirOffset + 6 > dataView.byteLength) {
26313             Roo.log('Invalid Exif data: Invalid directory offset.');
26314             return;
26315         }
26316         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26317         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26318         if (dirEndOffset + 4 > dataView.byteLength) {
26319             Roo.log('Invalid Exif data: Invalid directory size.');
26320             return;
26321         }
26322         for (i = 0; i < tagsNumber; i += 1) {
26323             this.parseExifTag(
26324                 dataView,
26325                 tiffOffset,
26326                 dirOffset + 2 + 12 * i, // tag offset
26327                 littleEndian
26328             );
26329         }
26330         // Return the offset to the next directory:
26331         return dataView.getUint32(dirEndOffset, littleEndian);
26332     },
26333     
26334     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26335     {
26336         var tag = dataView.getUint16(offset, littleEndian);
26337         
26338         this.exif[tag] = this.getExifValue(
26339             dataView,
26340             tiffOffset,
26341             offset,
26342             dataView.getUint16(offset + 2, littleEndian), // tag type
26343             dataView.getUint32(offset + 4, littleEndian), // tag length
26344             littleEndian
26345         );
26346     },
26347     
26348     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26349     {
26350         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26351             tagSize,
26352             dataOffset,
26353             values,
26354             i,
26355             str,
26356             c;
26357     
26358         if (!tagType) {
26359             Roo.log('Invalid Exif data: Invalid tag type.');
26360             return;
26361         }
26362         
26363         tagSize = tagType.size * length;
26364         // Determine if the value is contained in the dataOffset bytes,
26365         // or if the value at the dataOffset is a pointer to the actual data:
26366         dataOffset = tagSize > 4 ?
26367                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26368         if (dataOffset + tagSize > dataView.byteLength) {
26369             Roo.log('Invalid Exif data: Invalid data offset.');
26370             return;
26371         }
26372         if (length === 1) {
26373             return tagType.getValue(dataView, dataOffset, littleEndian);
26374         }
26375         values = [];
26376         for (i = 0; i < length; i += 1) {
26377             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26378         }
26379         
26380         if (tagType.ascii) {
26381             str = '';
26382             // Concatenate the chars:
26383             for (i = 0; i < values.length; i += 1) {
26384                 c = values[i];
26385                 // Ignore the terminating NULL byte(s):
26386                 if (c === '\u0000') {
26387                     break;
26388                 }
26389                 str += c;
26390             }
26391             return str;
26392         }
26393         return values;
26394     }
26395     
26396 });
26397
26398 Roo.apply(Roo.bootstrap.UploadCropbox, {
26399     tags : {
26400         'Orientation': 0x0112
26401     },
26402     
26403     Orientation: {
26404             1: 0, //'top-left',
26405 //            2: 'top-right',
26406             3: 180, //'bottom-right',
26407 //            4: 'bottom-left',
26408 //            5: 'left-top',
26409             6: 90, //'right-top',
26410 //            7: 'right-bottom',
26411             8: 270 //'left-bottom'
26412     },
26413     
26414     exifTagTypes : {
26415         // byte, 8-bit unsigned int:
26416         1: {
26417             getValue: function (dataView, dataOffset) {
26418                 return dataView.getUint8(dataOffset);
26419             },
26420             size: 1
26421         },
26422         // ascii, 8-bit byte:
26423         2: {
26424             getValue: function (dataView, dataOffset) {
26425                 return String.fromCharCode(dataView.getUint8(dataOffset));
26426             },
26427             size: 1,
26428             ascii: true
26429         },
26430         // short, 16 bit int:
26431         3: {
26432             getValue: function (dataView, dataOffset, littleEndian) {
26433                 return dataView.getUint16(dataOffset, littleEndian);
26434             },
26435             size: 2
26436         },
26437         // long, 32 bit int:
26438         4: {
26439             getValue: function (dataView, dataOffset, littleEndian) {
26440                 return dataView.getUint32(dataOffset, littleEndian);
26441             },
26442             size: 4
26443         },
26444         // rational = two long values, first is numerator, second is denominator:
26445         5: {
26446             getValue: function (dataView, dataOffset, littleEndian) {
26447                 return dataView.getUint32(dataOffset, littleEndian) /
26448                     dataView.getUint32(dataOffset + 4, littleEndian);
26449             },
26450             size: 8
26451         },
26452         // slong, 32 bit signed int:
26453         9: {
26454             getValue: function (dataView, dataOffset, littleEndian) {
26455                 return dataView.getInt32(dataOffset, littleEndian);
26456             },
26457             size: 4
26458         },
26459         // srational, two slongs, first is numerator, second is denominator:
26460         10: {
26461             getValue: function (dataView, dataOffset, littleEndian) {
26462                 return dataView.getInt32(dataOffset, littleEndian) /
26463                     dataView.getInt32(dataOffset + 4, littleEndian);
26464             },
26465             size: 8
26466         }
26467     },
26468     
26469     footer : {
26470         STANDARD : [
26471             {
26472                 tag : 'div',
26473                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26474                 action : 'rotate-left',
26475                 cn : [
26476                     {
26477                         tag : 'button',
26478                         cls : 'btn btn-default',
26479                         html : '<i class="fa fa-undo"></i>'
26480                     }
26481                 ]
26482             },
26483             {
26484                 tag : 'div',
26485                 cls : 'btn-group roo-upload-cropbox-picture',
26486                 action : 'picture',
26487                 cn : [
26488                     {
26489                         tag : 'button',
26490                         cls : 'btn btn-default',
26491                         html : '<i class="fa fa-picture-o"></i>'
26492                     }
26493                 ]
26494             },
26495             {
26496                 tag : 'div',
26497                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26498                 action : 'rotate-right',
26499                 cn : [
26500                     {
26501                         tag : 'button',
26502                         cls : 'btn btn-default',
26503                         html : '<i class="fa fa-repeat"></i>'
26504                     }
26505                 ]
26506             }
26507         ],
26508         DOCUMENT : [
26509             {
26510                 tag : 'div',
26511                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26512                 action : 'rotate-left',
26513                 cn : [
26514                     {
26515                         tag : 'button',
26516                         cls : 'btn btn-default',
26517                         html : '<i class="fa fa-undo"></i>'
26518                     }
26519                 ]
26520             },
26521             {
26522                 tag : 'div',
26523                 cls : 'btn-group roo-upload-cropbox-download',
26524                 action : 'download',
26525                 cn : [
26526                     {
26527                         tag : 'button',
26528                         cls : 'btn btn-default',
26529                         html : '<i class="fa fa-download"></i>'
26530                     }
26531                 ]
26532             },
26533             {
26534                 tag : 'div',
26535                 cls : 'btn-group roo-upload-cropbox-crop',
26536                 action : 'crop',
26537                 cn : [
26538                     {
26539                         tag : 'button',
26540                         cls : 'btn btn-default',
26541                         html : '<i class="fa fa-crop"></i>'
26542                     }
26543                 ]
26544             },
26545             {
26546                 tag : 'div',
26547                 cls : 'btn-group roo-upload-cropbox-trash',
26548                 action : 'trash',
26549                 cn : [
26550                     {
26551                         tag : 'button',
26552                         cls : 'btn btn-default',
26553                         html : '<i class="fa fa-trash"></i>'
26554                     }
26555                 ]
26556             },
26557             {
26558                 tag : 'div',
26559                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26560                 action : 'rotate-right',
26561                 cn : [
26562                     {
26563                         tag : 'button',
26564                         cls : 'btn btn-default',
26565                         html : '<i class="fa fa-repeat"></i>'
26566                     }
26567                 ]
26568             }
26569         ],
26570         ROTATOR : [
26571             {
26572                 tag : 'div',
26573                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26574                 action : 'rotate-left',
26575                 cn : [
26576                     {
26577                         tag : 'button',
26578                         cls : 'btn btn-default',
26579                         html : '<i class="fa fa-undo"></i>'
26580                     }
26581                 ]
26582             },
26583             {
26584                 tag : 'div',
26585                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26586                 action : 'rotate-right',
26587                 cn : [
26588                     {
26589                         tag : 'button',
26590                         cls : 'btn btn-default',
26591                         html : '<i class="fa fa-repeat"></i>'
26592                     }
26593                 ]
26594             }
26595         ]
26596     }
26597 });
26598
26599 /*
26600 * Licence: LGPL
26601 */
26602
26603 /**
26604  * @class Roo.bootstrap.DocumentManager
26605  * @extends Roo.bootstrap.Component
26606  * Bootstrap DocumentManager class
26607  * @cfg {String} paramName default 'imageUpload'
26608  * @cfg {String} method default POST
26609  * @cfg {String} url action url
26610  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26611  * @cfg {Boolean} multiple multiple upload default true
26612  * @cfg {Number} thumbSize default 300
26613  * @cfg {String} fieldLabel
26614  * @cfg {Number} labelWidth default 4
26615  * @cfg {String} labelAlign (left|top) default left
26616  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26617  * 
26618  * @constructor
26619  * Create a new DocumentManager
26620  * @param {Object} config The config object
26621  */
26622
26623 Roo.bootstrap.DocumentManager = function(config){
26624     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26625     
26626     this.addEvents({
26627         /**
26628          * @event initial
26629          * Fire when initial the DocumentManager
26630          * @param {Roo.bootstrap.DocumentManager} this
26631          */
26632         "initial" : true,
26633         /**
26634          * @event inspect
26635          * inspect selected file
26636          * @param {Roo.bootstrap.DocumentManager} this
26637          * @param {File} file
26638          */
26639         "inspect" : true,
26640         /**
26641          * @event exception
26642          * Fire when xhr load exception
26643          * @param {Roo.bootstrap.DocumentManager} this
26644          * @param {XMLHttpRequest} xhr
26645          */
26646         "exception" : true,
26647         /**
26648          * @event prepare
26649          * prepare the form data
26650          * @param {Roo.bootstrap.DocumentManager} this
26651          * @param {Object} formData
26652          */
26653         "prepare" : true,
26654         /**
26655          * @event remove
26656          * Fire when remove the file
26657          * @param {Roo.bootstrap.DocumentManager} this
26658          * @param {Object} file
26659          */
26660         "remove" : true,
26661         /**
26662          * @event refresh
26663          * Fire after refresh the file
26664          * @param {Roo.bootstrap.DocumentManager} this
26665          */
26666         "refresh" : true,
26667         /**
26668          * @event click
26669          * Fire after click the image
26670          * @param {Roo.bootstrap.DocumentManager} this
26671          * @param {Object} file
26672          */
26673         "click" : true,
26674         /**
26675          * @event edit
26676          * Fire when upload a image and editable set to true
26677          * @param {Roo.bootstrap.DocumentManager} this
26678          * @param {Object} file
26679          */
26680         "edit" : true,
26681         /**
26682          * @event beforeselectfile
26683          * Fire before select file
26684          * @param {Roo.bootstrap.DocumentManager} this
26685          */
26686         "beforeselectfile" : true,
26687         /**
26688          * @event process
26689          * Fire before process file
26690          * @param {Roo.bootstrap.DocumentManager} this
26691          * @param {Object} file
26692          */
26693         "process" : true
26694         
26695     });
26696 };
26697
26698 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26699     
26700     boxes : 0,
26701     inputName : '',
26702     thumbSize : 300,
26703     multiple : true,
26704     files : [],
26705     method : 'POST',
26706     url : '',
26707     paramName : 'imageUpload',
26708     fieldLabel : '',
26709     labelWidth : 4,
26710     labelAlign : 'left',
26711     editable : true,
26712     delegates : [],
26713     
26714     
26715     xhr : false, 
26716     
26717     getAutoCreate : function()
26718     {   
26719         var managerWidget = {
26720             tag : 'div',
26721             cls : 'roo-document-manager',
26722             cn : [
26723                 {
26724                     tag : 'input',
26725                     cls : 'roo-document-manager-selector',
26726                     type : 'file'
26727                 },
26728                 {
26729                     tag : 'div',
26730                     cls : 'roo-document-manager-uploader',
26731                     cn : [
26732                         {
26733                             tag : 'div',
26734                             cls : 'roo-document-manager-upload-btn',
26735                             html : '<i class="fa fa-plus"></i>'
26736                         }
26737                     ]
26738                     
26739                 }
26740             ]
26741         };
26742         
26743         var content = [
26744             {
26745                 tag : 'div',
26746                 cls : 'column col-md-12',
26747                 cn : managerWidget
26748             }
26749         ];
26750         
26751         if(this.fieldLabel.length){
26752             
26753             content = [
26754                 {
26755                     tag : 'div',
26756                     cls : 'column col-md-12',
26757                     html : this.fieldLabel
26758                 },
26759                 {
26760                     tag : 'div',
26761                     cls : 'column col-md-12',
26762                     cn : managerWidget
26763                 }
26764             ];
26765
26766             if(this.labelAlign == 'left'){
26767                 content = [
26768                     {
26769                         tag : 'div',
26770                         cls : 'column col-md-' + this.labelWidth,
26771                         html : this.fieldLabel
26772                     },
26773                     {
26774                         tag : 'div',
26775                         cls : 'column col-md-' + (12 - this.labelWidth),
26776                         cn : managerWidget
26777                     }
26778                 ];
26779                 
26780             }
26781         }
26782         
26783         var cfg = {
26784             tag : 'div',
26785             cls : 'row clearfix',
26786             cn : content
26787         };
26788         
26789         return cfg;
26790         
26791     },
26792     
26793     initEvents : function()
26794     {
26795         this.managerEl = this.el.select('.roo-document-manager', true).first();
26796         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26797         
26798         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26799         this.selectorEl.hide();
26800         
26801         if(this.multiple){
26802             this.selectorEl.attr('multiple', 'multiple');
26803         }
26804         
26805         this.selectorEl.on('change', this.onFileSelected, this);
26806         
26807         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26808         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26809         
26810         this.uploader.on('click', this.onUploaderClick, this);
26811         
26812         this.renderProgressDialog();
26813         
26814         var _this = this;
26815         
26816         window.addEventListener("resize", function() { _this.refresh(); } );
26817         
26818         this.fireEvent('initial', this);
26819     },
26820     
26821     renderProgressDialog : function()
26822     {
26823         var _this = this;
26824         
26825         this.progressDialog = new Roo.bootstrap.Modal({
26826             cls : 'roo-document-manager-progress-dialog',
26827             allow_close : false,
26828             title : '',
26829             buttons : [
26830                 {
26831                     name  :'cancel',
26832                     weight : 'danger',
26833                     html : 'Cancel'
26834                 }
26835             ], 
26836             listeners : { 
26837                 btnclick : function() {
26838                     _this.uploadCancel();
26839                     this.hide();
26840                 }
26841             }
26842         });
26843          
26844         this.progressDialog.render(Roo.get(document.body));
26845          
26846         this.progress = new Roo.bootstrap.Progress({
26847             cls : 'roo-document-manager-progress',
26848             active : true,
26849             striped : true
26850         });
26851         
26852         this.progress.render(this.progressDialog.getChildContainer());
26853         
26854         this.progressBar = new Roo.bootstrap.ProgressBar({
26855             cls : 'roo-document-manager-progress-bar',
26856             aria_valuenow : 0,
26857             aria_valuemin : 0,
26858             aria_valuemax : 12,
26859             panel : 'success'
26860         });
26861         
26862         this.progressBar.render(this.progress.getChildContainer());
26863     },
26864     
26865     onUploaderClick : function(e)
26866     {
26867         e.preventDefault();
26868      
26869         if(this.fireEvent('beforeselectfile', this) != false){
26870             this.selectorEl.dom.click();
26871         }
26872         
26873     },
26874     
26875     onFileSelected : function(e)
26876     {
26877         e.preventDefault();
26878         
26879         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26880             return;
26881         }
26882         
26883         Roo.each(this.selectorEl.dom.files, function(file){
26884             if(this.fireEvent('inspect', this, file) != false){
26885                 this.files.push(file);
26886             }
26887         }, this);
26888         
26889         this.queue();
26890         
26891     },
26892     
26893     queue : function()
26894     {
26895         this.selectorEl.dom.value = '';
26896         
26897         if(!this.files.length){
26898             return;
26899         }
26900         
26901         if(this.boxes > 0 && this.files.length > this.boxes){
26902             this.files = this.files.slice(0, this.boxes);
26903         }
26904         
26905         this.uploader.show();
26906         
26907         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26908             this.uploader.hide();
26909         }
26910         
26911         var _this = this;
26912         
26913         var files = [];
26914         
26915         var docs = [];
26916         
26917         Roo.each(this.files, function(file){
26918             
26919             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26920                 var f = this.renderPreview(file);
26921                 files.push(f);
26922                 return;
26923             }
26924             
26925             if(file.type.indexOf('image') != -1){
26926                 this.delegates.push(
26927                     (function(){
26928                         _this.process(file);
26929                     }).createDelegate(this)
26930                 );
26931         
26932                 return;
26933             }
26934             
26935             docs.push(
26936                 (function(){
26937                     _this.process(file);
26938                 }).createDelegate(this)
26939             );
26940             
26941         }, this);
26942         
26943         this.files = files;
26944         
26945         this.delegates = this.delegates.concat(docs);
26946         
26947         if(!this.delegates.length){
26948             this.refresh();
26949             return;
26950         }
26951         
26952         this.progressBar.aria_valuemax = this.delegates.length;
26953         
26954         this.arrange();
26955         
26956         return;
26957     },
26958     
26959     arrange : function()
26960     {
26961         if(!this.delegates.length){
26962             this.progressDialog.hide();
26963             this.refresh();
26964             return;
26965         }
26966         
26967         var delegate = this.delegates.shift();
26968         
26969         this.progressDialog.show();
26970         
26971         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26972         
26973         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26974         
26975         delegate();
26976     },
26977     
26978     refresh : function()
26979     {
26980         this.uploader.show();
26981         
26982         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26983             this.uploader.hide();
26984         }
26985         
26986         Roo.isTouch ? this.closable(false) : this.closable(true);
26987         
26988         this.fireEvent('refresh', this);
26989     },
26990     
26991     onRemove : function(e, el, o)
26992     {
26993         e.preventDefault();
26994         
26995         this.fireEvent('remove', this, o);
26996         
26997     },
26998     
26999     remove : function(o)
27000     {
27001         var files = [];
27002         
27003         Roo.each(this.files, function(file){
27004             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27005                 files.push(file);
27006                 return;
27007             }
27008
27009             o.target.remove();
27010
27011         }, this);
27012         
27013         this.files = files;
27014         
27015         this.refresh();
27016     },
27017     
27018     clear : function()
27019     {
27020         Roo.each(this.files, function(file){
27021             if(!file.target){
27022                 return;
27023             }
27024             
27025             file.target.remove();
27026
27027         }, this);
27028         
27029         this.files = [];
27030         
27031         this.refresh();
27032     },
27033     
27034     onClick : function(e, el, o)
27035     {
27036         e.preventDefault();
27037         
27038         this.fireEvent('click', this, o);
27039         
27040     },
27041     
27042     closable : function(closable)
27043     {
27044         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27045             
27046             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27047             
27048             if(closable){
27049                 el.show();
27050                 return;
27051             }
27052             
27053             el.hide();
27054             
27055         }, this);
27056     },
27057     
27058     xhrOnLoad : function(xhr)
27059     {
27060         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27061             el.remove();
27062         }, this);
27063         
27064         if (xhr.readyState !== 4) {
27065             this.arrange();
27066             this.fireEvent('exception', this, xhr);
27067             return;
27068         }
27069
27070         var response = Roo.decode(xhr.responseText);
27071         
27072         if(!response.success){
27073             this.arrange();
27074             this.fireEvent('exception', this, xhr);
27075             return;
27076         }
27077         
27078         var file = this.renderPreview(response.data);
27079         
27080         this.files.push(file);
27081         
27082         this.arrange();
27083         
27084     },
27085     
27086     xhrOnError : function(xhr)
27087     {
27088         Roo.log('xhr on error');
27089         
27090         var response = Roo.decode(xhr.responseText);
27091           
27092         Roo.log(response);
27093         
27094         this.arrange();
27095     },
27096     
27097     process : function(file)
27098     {
27099         if(this.fireEvent('process', this, file) !== false){
27100             if(this.editable && file.type.indexOf('image') != -1){
27101                 this.fireEvent('edit', this, file);
27102                 return;
27103             }
27104
27105             this.uploadStart(file, false);
27106
27107             return;
27108         }
27109         
27110     },
27111     
27112     uploadStart : function(file, crop)
27113     {
27114         this.xhr = new XMLHttpRequest();
27115         
27116         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27117             this.arrange();
27118             return;
27119         }
27120         
27121         file.xhr = this.xhr;
27122             
27123         this.managerEl.createChild({
27124             tag : 'div',
27125             cls : 'roo-document-manager-loading',
27126             cn : [
27127                 {
27128                     tag : 'div',
27129                     tooltip : file.name,
27130                     cls : 'roo-document-manager-thumb',
27131                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27132                 }
27133             ]
27134
27135         });
27136
27137         this.xhr.open(this.method, this.url, true);
27138         
27139         var headers = {
27140             "Accept": "application/json",
27141             "Cache-Control": "no-cache",
27142             "X-Requested-With": "XMLHttpRequest"
27143         };
27144         
27145         for (var headerName in headers) {
27146             var headerValue = headers[headerName];
27147             if (headerValue) {
27148                 this.xhr.setRequestHeader(headerName, headerValue);
27149             }
27150         }
27151         
27152         var _this = this;
27153         
27154         this.xhr.onload = function()
27155         {
27156             _this.xhrOnLoad(_this.xhr);
27157         }
27158         
27159         this.xhr.onerror = function()
27160         {
27161             _this.xhrOnError(_this.xhr);
27162         }
27163         
27164         var formData = new FormData();
27165
27166         formData.append('returnHTML', 'NO');
27167         
27168         if(crop){
27169             formData.append('crop', crop);
27170         }
27171         
27172         formData.append(this.paramName, file, file.name);
27173         
27174         if(this.fireEvent('prepare', this, formData) != false){
27175             this.xhr.send(formData);
27176         };
27177     },
27178     
27179     uploadCancel : function()
27180     {
27181         if (this.xhr) {
27182             this.xhr.abort();
27183         }
27184         
27185         
27186         this.delegates = [];
27187         
27188         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27189             el.remove();
27190         }, this);
27191         
27192         this.arrange();
27193     },
27194     
27195     renderPreview : function(file)
27196     {
27197         if(typeof(file.target) != 'undefined' && file.target){
27198             return file;
27199         }
27200         
27201         var previewEl = this.managerEl.createChild({
27202             tag : 'div',
27203             cls : 'roo-document-manager-preview',
27204             cn : [
27205                 {
27206                     tag : 'div',
27207                     tooltip : file.filename,
27208                     cls : 'roo-document-manager-thumb',
27209                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27210                 },
27211                 {
27212                     tag : 'button',
27213                     cls : 'close',
27214                     html : '<i class="fa fa-times-circle"></i>'
27215                 }
27216             ]
27217         });
27218
27219         var close = previewEl.select('button.close', true).first();
27220
27221         close.on('click', this.onRemove, this, file);
27222
27223         file.target = previewEl;
27224
27225         var image = previewEl.select('img', true).first();
27226         
27227         var _this = this;
27228         
27229         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27230         
27231         image.on('click', this.onClick, this, file);
27232         
27233         return file;
27234         
27235     },
27236     
27237     onPreviewLoad : function(file, image)
27238     {
27239         if(typeof(file.target) == 'undefined' || !file.target){
27240             return;
27241         }
27242         
27243         var width = image.dom.naturalWidth || image.dom.width;
27244         var height = image.dom.naturalHeight || image.dom.height;
27245         
27246         if(width > height){
27247             file.target.addClass('wide');
27248             return;
27249         }
27250         
27251         file.target.addClass('tall');
27252         return;
27253         
27254     },
27255     
27256     uploadFromSource : function(file, crop)
27257     {
27258         this.xhr = new XMLHttpRequest();
27259         
27260         this.managerEl.createChild({
27261             tag : 'div',
27262             cls : 'roo-document-manager-loading',
27263             cn : [
27264                 {
27265                     tag : 'div',
27266                     tooltip : file.name,
27267                     cls : 'roo-document-manager-thumb',
27268                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27269                 }
27270             ]
27271
27272         });
27273
27274         this.xhr.open(this.method, this.url, true);
27275         
27276         var headers = {
27277             "Accept": "application/json",
27278             "Cache-Control": "no-cache",
27279             "X-Requested-With": "XMLHttpRequest"
27280         };
27281         
27282         for (var headerName in headers) {
27283             var headerValue = headers[headerName];
27284             if (headerValue) {
27285                 this.xhr.setRequestHeader(headerName, headerValue);
27286             }
27287         }
27288         
27289         var _this = this;
27290         
27291         this.xhr.onload = function()
27292         {
27293             _this.xhrOnLoad(_this.xhr);
27294         }
27295         
27296         this.xhr.onerror = function()
27297         {
27298             _this.xhrOnError(_this.xhr);
27299         }
27300         
27301         var formData = new FormData();
27302
27303         formData.append('returnHTML', 'NO');
27304         
27305         formData.append('crop', crop);
27306         
27307         if(typeof(file.filename) != 'undefined'){
27308             formData.append('filename', file.filename);
27309         }
27310         
27311         if(typeof(file.mimetype) != 'undefined'){
27312             formData.append('mimetype', file.mimetype);
27313         }
27314         
27315         if(this.fireEvent('prepare', this, formData) != false){
27316             this.xhr.send(formData);
27317         };
27318     }
27319 });
27320
27321 /*
27322 * Licence: LGPL
27323 */
27324
27325 /**
27326  * @class Roo.bootstrap.DocumentViewer
27327  * @extends Roo.bootstrap.Component
27328  * Bootstrap DocumentViewer class
27329  * 
27330  * @constructor
27331  * Create a new DocumentViewer
27332  * @param {Object} config The config object
27333  */
27334
27335 Roo.bootstrap.DocumentViewer = function(config){
27336     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27337     
27338     this.addEvents({
27339         /**
27340          * @event initial
27341          * Fire after initEvent
27342          * @param {Roo.bootstrap.DocumentViewer} this
27343          */
27344         "initial" : true,
27345         /**
27346          * @event click
27347          * Fire after click
27348          * @param {Roo.bootstrap.DocumentViewer} this
27349          */
27350         "click" : true,
27351         /**
27352          * @event trash
27353          * Fire after trash button
27354          * @param {Roo.bootstrap.DocumentViewer} this
27355          */
27356         "trash" : true
27357         
27358     });
27359 };
27360
27361 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27362     
27363     getAutoCreate : function()
27364     {
27365         var cfg = {
27366             tag : 'div',
27367             cls : 'roo-document-viewer',
27368             cn : [
27369                 {
27370                     tag : 'div',
27371                     cls : 'roo-document-viewer-body',
27372                     cn : [
27373                         {
27374                             tag : 'div',
27375                             cls : 'roo-document-viewer-thumb',
27376                             cn : [
27377                                 {
27378                                     tag : 'img',
27379                                     cls : 'roo-document-viewer-image'
27380                                 }
27381                             ]
27382                         }
27383                     ]
27384                 },
27385                 {
27386                     tag : 'div',
27387                     cls : 'roo-document-viewer-footer',
27388                     cn : {
27389                         tag : 'div',
27390                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27391                         cn : [
27392                             {
27393                                 tag : 'div',
27394                                 cls : 'btn-group',
27395                                 cn : [
27396                                     {
27397                                         tag : 'button',
27398                                         cls : 'btn btn-default roo-document-viewer-trash',
27399                                         html : '<i class="fa fa-trash"></i>'
27400                                     }
27401                                 ]
27402                             }
27403                         ]
27404                     }
27405                 }
27406             ]
27407         };
27408         
27409         return cfg;
27410     },
27411     
27412     initEvents : function()
27413     {
27414         
27415         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27416         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27417         
27418         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27419         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27420         
27421         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27422         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27423         
27424         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27425         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27426         
27427         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27428         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27429         
27430         this.bodyEl.on('click', this.onClick, this);
27431         
27432         this.trashBtn.on('click', this.onTrash, this);
27433         
27434     },
27435     
27436     initial : function()
27437     {
27438 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27439         
27440         
27441         this.fireEvent('initial', this);
27442         
27443     },
27444     
27445     onClick : function(e)
27446     {
27447         e.preventDefault();
27448         
27449         this.fireEvent('click', this);
27450     },
27451     
27452     onTrash : function(e)
27453     {
27454         e.preventDefault();
27455         
27456         this.fireEvent('trash', this);
27457     }
27458     
27459 });
27460 /*
27461  * - LGPL
27462  *
27463  * nav progress bar
27464  * 
27465  */
27466
27467 /**
27468  * @class Roo.bootstrap.NavProgressBar
27469  * @extends Roo.bootstrap.Component
27470  * Bootstrap NavProgressBar class
27471  * 
27472  * @constructor
27473  * Create a new nav progress bar
27474  * @param {Object} config The config object
27475  */
27476
27477 Roo.bootstrap.NavProgressBar = function(config){
27478     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27479
27480     this.bullets = this.bullets || [];
27481    
27482 //    Roo.bootstrap.NavProgressBar.register(this);
27483      this.addEvents({
27484         /**
27485              * @event changed
27486              * Fires when the active item changes
27487              * @param {Roo.bootstrap.NavProgressBar} this
27488              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27489              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27490          */
27491         'changed': true
27492      });
27493     
27494 };
27495
27496 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27497     
27498     bullets : [],
27499     barItems : [],
27500     
27501     getAutoCreate : function()
27502     {
27503         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27504         
27505         cfg = {
27506             tag : 'div',
27507             cls : 'roo-navigation-bar-group',
27508             cn : [
27509                 {
27510                     tag : 'div',
27511                     cls : 'roo-navigation-top-bar'
27512                 },
27513                 {
27514                     tag : 'div',
27515                     cls : 'roo-navigation-bullets-bar',
27516                     cn : [
27517                         {
27518                             tag : 'ul',
27519                             cls : 'roo-navigation-bar'
27520                         }
27521                     ]
27522                 },
27523                 
27524                 {
27525                     tag : 'div',
27526                     cls : 'roo-navigation-bottom-bar'
27527                 }
27528             ]
27529             
27530         };
27531         
27532         return cfg;
27533         
27534     },
27535     
27536     initEvents: function() 
27537     {
27538         
27539     },
27540     
27541     onRender : function(ct, position) 
27542     {
27543         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27544         
27545         if(this.bullets.length){
27546             Roo.each(this.bullets, function(b){
27547                this.addItem(b);
27548             }, this);
27549         }
27550         
27551         this.format();
27552         
27553     },
27554     
27555     addItem : function(cfg)
27556     {
27557         var item = new Roo.bootstrap.NavProgressItem(cfg);
27558         
27559         item.parentId = this.id;
27560         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27561         
27562         if(cfg.html){
27563             var top = new Roo.bootstrap.Element({
27564                 tag : 'div',
27565                 cls : 'roo-navigation-bar-text'
27566             });
27567             
27568             var bottom = new Roo.bootstrap.Element({
27569                 tag : 'div',
27570                 cls : 'roo-navigation-bar-text'
27571             });
27572             
27573             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27574             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27575             
27576             var topText = new Roo.bootstrap.Element({
27577                 tag : 'span',
27578                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27579             });
27580             
27581             var bottomText = new Roo.bootstrap.Element({
27582                 tag : 'span',
27583                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27584             });
27585             
27586             topText.onRender(top.el, null);
27587             bottomText.onRender(bottom.el, null);
27588             
27589             item.topEl = top;
27590             item.bottomEl = bottom;
27591         }
27592         
27593         this.barItems.push(item);
27594         
27595         return item;
27596     },
27597     
27598     getActive : function()
27599     {
27600         var active = false;
27601         
27602         Roo.each(this.barItems, function(v){
27603             
27604             if (!v.isActive()) {
27605                 return;
27606             }
27607             
27608             active = v;
27609             return false;
27610             
27611         });
27612         
27613         return active;
27614     },
27615     
27616     setActiveItem : function(item)
27617     {
27618         var prev = false;
27619         
27620         Roo.each(this.barItems, function(v){
27621             if (v.rid == item.rid) {
27622                 return ;
27623             }
27624             
27625             if (v.isActive()) {
27626                 v.setActive(false);
27627                 prev = v;
27628             }
27629         });
27630
27631         item.setActive(true);
27632         
27633         this.fireEvent('changed', this, item, prev);
27634     },
27635     
27636     getBarItem: function(rid)
27637     {
27638         var ret = false;
27639         
27640         Roo.each(this.barItems, function(e) {
27641             if (e.rid != rid) {
27642                 return;
27643             }
27644             
27645             ret =  e;
27646             return false;
27647         });
27648         
27649         return ret;
27650     },
27651     
27652     indexOfItem : function(item)
27653     {
27654         var index = false;
27655         
27656         Roo.each(this.barItems, function(v, i){
27657             
27658             if (v.rid != item.rid) {
27659                 return;
27660             }
27661             
27662             index = i;
27663             return false
27664         });
27665         
27666         return index;
27667     },
27668     
27669     setActiveNext : function()
27670     {
27671         var i = this.indexOfItem(this.getActive());
27672         
27673         if (i > this.barItems.length) {
27674             return;
27675         }
27676         
27677         this.setActiveItem(this.barItems[i+1]);
27678     },
27679     
27680     setActivePrev : function()
27681     {
27682         var i = this.indexOfItem(this.getActive());
27683         
27684         if (i  < 1) {
27685             return;
27686         }
27687         
27688         this.setActiveItem(this.barItems[i-1]);
27689     },
27690     
27691     format : function()
27692     {
27693         if(!this.barItems.length){
27694             return;
27695         }
27696      
27697         var width = 100 / this.barItems.length;
27698         
27699         Roo.each(this.barItems, function(i){
27700             i.el.setStyle('width', width + '%');
27701             i.topEl.el.setStyle('width', width + '%');
27702             i.bottomEl.el.setStyle('width', width + '%');
27703         }, this);
27704         
27705     }
27706     
27707 });
27708 /*
27709  * - LGPL
27710  *
27711  * Nav Progress Item
27712  * 
27713  */
27714
27715 /**
27716  * @class Roo.bootstrap.NavProgressItem
27717  * @extends Roo.bootstrap.Component
27718  * Bootstrap NavProgressItem class
27719  * @cfg {String} rid the reference id
27720  * @cfg {Boolean} active (true|false) Is item active default false
27721  * @cfg {Boolean} disabled (true|false) Is item active default false
27722  * @cfg {String} html
27723  * @cfg {String} position (top|bottom) text position default bottom
27724  * @cfg {String} icon show icon instead of number
27725  * 
27726  * @constructor
27727  * Create a new NavProgressItem
27728  * @param {Object} config The config object
27729  */
27730 Roo.bootstrap.NavProgressItem = function(config){
27731     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27732     this.addEvents({
27733         // raw events
27734         /**
27735          * @event click
27736          * The raw click event for the entire grid.
27737          * @param {Roo.bootstrap.NavProgressItem} this
27738          * @param {Roo.EventObject} e
27739          */
27740         "click" : true
27741     });
27742    
27743 };
27744
27745 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27746     
27747     rid : '',
27748     active : false,
27749     disabled : false,
27750     html : '',
27751     position : 'bottom',
27752     icon : false,
27753     
27754     getAutoCreate : function()
27755     {
27756         var iconCls = 'roo-navigation-bar-item-icon';
27757         
27758         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27759         
27760         var cfg = {
27761             tag: 'li',
27762             cls: 'roo-navigation-bar-item',
27763             cn : [
27764                 {
27765                     tag : 'i',
27766                     cls : iconCls
27767                 }
27768             ]
27769         };
27770         
27771         if(this.active){
27772             cfg.cls += ' active';
27773         }
27774         if(this.disabled){
27775             cfg.cls += ' disabled';
27776         }
27777         
27778         return cfg;
27779     },
27780     
27781     disable : function()
27782     {
27783         this.setDisabled(true);
27784     },
27785     
27786     enable : function()
27787     {
27788         this.setDisabled(false);
27789     },
27790     
27791     initEvents: function() 
27792     {
27793         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27794         
27795         this.iconEl.on('click', this.onClick, this);
27796     },
27797     
27798     onClick : function(e)
27799     {
27800         e.preventDefault();
27801         
27802         if(this.disabled){
27803             return;
27804         }
27805         
27806         if(this.fireEvent('click', this, e) === false){
27807             return;
27808         };
27809         
27810         this.parent().setActiveItem(this);
27811     },
27812     
27813     isActive: function () 
27814     {
27815         return this.active;
27816     },
27817     
27818     setActive : function(state)
27819     {
27820         if(this.active == state){
27821             return;
27822         }
27823         
27824         this.active = state;
27825         
27826         if (state) {
27827             this.el.addClass('active');
27828             return;
27829         }
27830         
27831         this.el.removeClass('active');
27832         
27833         return;
27834     },
27835     
27836     setDisabled : function(state)
27837     {
27838         if(this.disabled == state){
27839             return;
27840         }
27841         
27842         this.disabled = state;
27843         
27844         if (state) {
27845             this.el.addClass('disabled');
27846             return;
27847         }
27848         
27849         this.el.removeClass('disabled');
27850     },
27851     
27852     tooltipEl : function()
27853     {
27854         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27855     }
27856 });
27857  
27858
27859  /*
27860  * - LGPL
27861  *
27862  * FieldLabel
27863  * 
27864  */
27865
27866 /**
27867  * @class Roo.bootstrap.FieldLabel
27868  * @extends Roo.bootstrap.Component
27869  * Bootstrap FieldLabel class
27870  * @cfg {String} html contents of the element
27871  * @cfg {String} tag tag of the element default label
27872  * @cfg {String} cls class of the element
27873  * @cfg {String} target label target 
27874  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27875  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27876  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27877  * @cfg {String} iconTooltip default "This field is required"
27878  * 
27879  * @constructor
27880  * Create a new FieldLabel
27881  * @param {Object} config The config object
27882  */
27883
27884 Roo.bootstrap.FieldLabel = function(config){
27885     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27886     
27887     this.addEvents({
27888             /**
27889              * @event invalid
27890              * Fires after the field has been marked as invalid.
27891              * @param {Roo.form.FieldLabel} this
27892              * @param {String} msg The validation message
27893              */
27894             invalid : true,
27895             /**
27896              * @event valid
27897              * Fires after the field has been validated with no errors.
27898              * @param {Roo.form.FieldLabel} this
27899              */
27900             valid : true
27901         });
27902 };
27903
27904 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27905     
27906     tag: 'label',
27907     cls: '',
27908     html: '',
27909     target: '',
27910     allowBlank : true,
27911     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27912     validClass : 'text-success fa fa-lg fa-check',
27913     iconTooltip : 'This field is required',
27914     
27915     getAutoCreate : function(){
27916         
27917         var cfg = {
27918             tag : this.tag,
27919             cls : 'roo-bootstrap-field-label ' + this.cls,
27920             for : this.target,
27921             cn : [
27922                 {
27923                     tag : 'i',
27924                     cls : '',
27925                     tooltip : this.iconTooltip
27926                 },
27927                 {
27928                     tag : 'span',
27929                     html : this.html
27930                 }
27931             ] 
27932         };
27933         
27934         return cfg;
27935     },
27936     
27937     initEvents: function() 
27938     {
27939         Roo.bootstrap.Element.superclass.initEvents.call(this);
27940         
27941         this.iconEl = this.el.select('i', true).first();
27942         
27943         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27944         
27945         Roo.bootstrap.FieldLabel.register(this);
27946     },
27947     
27948     /**
27949      * Mark this field as valid
27950      */
27951     markValid : function()
27952     {
27953         this.iconEl.show();
27954         
27955         this.iconEl.removeClass(this.invalidClass);
27956         
27957         this.iconEl.addClass(this.validClass);
27958         
27959         this.fireEvent('valid', this);
27960     },
27961     
27962     /**
27963      * Mark this field as invalid
27964      * @param {String} msg The validation message
27965      */
27966     markInvalid : function(msg)
27967     {
27968         this.iconEl.show();
27969         
27970         this.iconEl.removeClass(this.validClass);
27971         
27972         this.iconEl.addClass(this.invalidClass);
27973         
27974         this.fireEvent('invalid', this, msg);
27975     }
27976     
27977    
27978 });
27979
27980 Roo.apply(Roo.bootstrap.FieldLabel, {
27981     
27982     groups: {},
27983     
27984      /**
27985     * register a FieldLabel Group
27986     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27987     */
27988     register : function(label)
27989     {
27990         if(this.groups.hasOwnProperty(label.target)){
27991             return;
27992         }
27993      
27994         this.groups[label.target] = label;
27995         
27996     },
27997     /**
27998     * fetch a FieldLabel Group based on the target
27999     * @param {string} target
28000     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28001     */
28002     get: function(target) {
28003         if (typeof(this.groups[target]) == 'undefined') {
28004             return false;
28005         }
28006         
28007         return this.groups[target] ;
28008     }
28009 });
28010
28011  
28012
28013  /*
28014  * - LGPL
28015  *
28016  * page DateSplitField.
28017  * 
28018  */
28019
28020
28021 /**
28022  * @class Roo.bootstrap.DateSplitField
28023  * @extends Roo.bootstrap.Component
28024  * Bootstrap DateSplitField class
28025  * @cfg {string} fieldLabel - the label associated
28026  * @cfg {Number} labelWidth set the width of label (0-12)
28027  * @cfg {String} labelAlign (top|left)
28028  * @cfg {Boolean} dayAllowBlank (true|false) default false
28029  * @cfg {Boolean} monthAllowBlank (true|false) default false
28030  * @cfg {Boolean} yearAllowBlank (true|false) default false
28031  * @cfg {string} dayPlaceholder 
28032  * @cfg {string} monthPlaceholder
28033  * @cfg {string} yearPlaceholder
28034  * @cfg {string} dayFormat default 'd'
28035  * @cfg {string} monthFormat default 'm'
28036  * @cfg {string} yearFormat default 'Y'
28037
28038  *     
28039  * @constructor
28040  * Create a new DateSplitField
28041  * @param {Object} config The config object
28042  */
28043
28044 Roo.bootstrap.DateSplitField = function(config){
28045     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28046     
28047     this.addEvents({
28048         // raw events
28049          /**
28050          * @event years
28051          * getting the data of years
28052          * @param {Roo.bootstrap.DateSplitField} this
28053          * @param {Object} years
28054          */
28055         "years" : true,
28056         /**
28057          * @event days
28058          * getting the data of days
28059          * @param {Roo.bootstrap.DateSplitField} this
28060          * @param {Object} days
28061          */
28062         "days" : true,
28063         /**
28064          * @event invalid
28065          * Fires after the field has been marked as invalid.
28066          * @param {Roo.form.Field} this
28067          * @param {String} msg The validation message
28068          */
28069         invalid : true,
28070        /**
28071          * @event valid
28072          * Fires after the field has been validated with no errors.
28073          * @param {Roo.form.Field} this
28074          */
28075         valid : true
28076     });
28077 };
28078
28079 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28080     
28081     fieldLabel : '',
28082     labelAlign : 'top',
28083     labelWidth : 3,
28084     dayAllowBlank : false,
28085     monthAllowBlank : false,
28086     yearAllowBlank : false,
28087     dayPlaceholder : '',
28088     monthPlaceholder : '',
28089     yearPlaceholder : '',
28090     dayFormat : 'd',
28091     monthFormat : 'm',
28092     yearFormat : 'Y',
28093     isFormField : true,
28094     
28095     getAutoCreate : function()
28096     {
28097         var cfg = {
28098             tag : 'div',
28099             cls : 'row roo-date-split-field-group',
28100             cn : [
28101                 {
28102                     tag : 'input',
28103                     type : 'hidden',
28104                     cls : 'form-hidden-field roo-date-split-field-group-value',
28105                     name : this.name
28106                 }
28107             ]
28108         };
28109         
28110         if(this.fieldLabel){
28111             cfg.cn.push({
28112                 tag : 'div',
28113                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28114                 cn : [
28115                     {
28116                         tag : 'label',
28117                         html : this.fieldLabel
28118                     }
28119                 ]
28120             });
28121         }
28122         
28123         Roo.each(['day', 'month', 'year'], function(t){
28124             cfg.cn.push({
28125                 tag : 'div',
28126                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28127             });
28128         }, this);
28129         
28130         return cfg;
28131     },
28132     
28133     inputEl: function ()
28134     {
28135         return this.el.select('.roo-date-split-field-group-value', true).first();
28136     },
28137     
28138     onRender : function(ct, position) 
28139     {
28140         var _this = this;
28141         
28142         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28143         
28144         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28145         
28146         this.dayField = new Roo.bootstrap.ComboBox({
28147             allowBlank : this.dayAllowBlank,
28148             alwaysQuery : true,
28149             displayField : 'value',
28150             editable : false,
28151             fieldLabel : '',
28152             forceSelection : true,
28153             mode : 'local',
28154             placeholder : this.dayPlaceholder,
28155             selectOnFocus : true,
28156             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28157             triggerAction : 'all',
28158             typeAhead : true,
28159             valueField : 'value',
28160             store : new Roo.data.SimpleStore({
28161                 data : (function() {    
28162                     var days = [];
28163                     _this.fireEvent('days', _this, days);
28164                     return days;
28165                 })(),
28166                 fields : [ 'value' ]
28167             }),
28168             listeners : {
28169                 select : function (_self, record, index)
28170                 {
28171                     _this.setValue(_this.getValue());
28172                 }
28173             }
28174         });
28175
28176         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28177         
28178         this.monthField = new Roo.bootstrap.MonthField({
28179             after : '<i class=\"fa fa-calendar\"></i>',
28180             allowBlank : this.monthAllowBlank,
28181             placeholder : this.monthPlaceholder,
28182             readOnly : true,
28183             listeners : {
28184                 render : function (_self)
28185                 {
28186                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28187                         e.preventDefault();
28188                         _self.focus();
28189                     });
28190                 },
28191                 select : function (_self, oldvalue, newvalue)
28192                 {
28193                     _this.setValue(_this.getValue());
28194                 }
28195             }
28196         });
28197         
28198         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28199         
28200         this.yearField = new Roo.bootstrap.ComboBox({
28201             allowBlank : this.yearAllowBlank,
28202             alwaysQuery : true,
28203             displayField : 'value',
28204             editable : false,
28205             fieldLabel : '',
28206             forceSelection : true,
28207             mode : 'local',
28208             placeholder : this.yearPlaceholder,
28209             selectOnFocus : true,
28210             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28211             triggerAction : 'all',
28212             typeAhead : true,
28213             valueField : 'value',
28214             store : new Roo.data.SimpleStore({
28215                 data : (function() {
28216                     var years = [];
28217                     _this.fireEvent('years', _this, years);
28218                     return years;
28219                 })(),
28220                 fields : [ 'value' ]
28221             }),
28222             listeners : {
28223                 select : function (_self, record, index)
28224                 {
28225                     _this.setValue(_this.getValue());
28226                 }
28227             }
28228         });
28229
28230         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28231     },
28232     
28233     setValue : function(v, format)
28234     {
28235         this.inputEl.dom.value = v;
28236         
28237         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28238         
28239         var d = Date.parseDate(v, f);
28240         
28241         if(!d){
28242             this.validate();
28243             return;
28244         }
28245         
28246         this.setDay(d.format(this.dayFormat));
28247         this.setMonth(d.format(this.monthFormat));
28248         this.setYear(d.format(this.yearFormat));
28249         
28250         this.validate();
28251         
28252         return;
28253     },
28254     
28255     setDay : function(v)
28256     {
28257         this.dayField.setValue(v);
28258         this.inputEl.dom.value = this.getValue();
28259         this.validate();
28260         return;
28261     },
28262     
28263     setMonth : function(v)
28264     {
28265         this.monthField.setValue(v, true);
28266         this.inputEl.dom.value = this.getValue();
28267         this.validate();
28268         return;
28269     },
28270     
28271     setYear : function(v)
28272     {
28273         this.yearField.setValue(v);
28274         this.inputEl.dom.value = this.getValue();
28275         this.validate();
28276         return;
28277     },
28278     
28279     getDay : function()
28280     {
28281         return this.dayField.getValue();
28282     },
28283     
28284     getMonth : function()
28285     {
28286         return this.monthField.getValue();
28287     },
28288     
28289     getYear : function()
28290     {
28291         return this.yearField.getValue();
28292     },
28293     
28294     getValue : function()
28295     {
28296         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28297         
28298         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28299         
28300         return date;
28301     },
28302     
28303     reset : function()
28304     {
28305         this.setDay('');
28306         this.setMonth('');
28307         this.setYear('');
28308         this.inputEl.dom.value = '';
28309         this.validate();
28310         return;
28311     },
28312     
28313     validate : function()
28314     {
28315         var d = this.dayField.validate();
28316         var m = this.monthField.validate();
28317         var y = this.yearField.validate();
28318         
28319         var valid = true;
28320         
28321         if(
28322                 (!this.dayAllowBlank && !d) ||
28323                 (!this.monthAllowBlank && !m) ||
28324                 (!this.yearAllowBlank && !y)
28325         ){
28326             valid = false;
28327         }
28328         
28329         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28330             return valid;
28331         }
28332         
28333         if(valid){
28334             this.markValid();
28335             return valid;
28336         }
28337         
28338         this.markInvalid();
28339         
28340         return valid;
28341     },
28342     
28343     markValid : function()
28344     {
28345         
28346         var label = this.el.select('label', true).first();
28347         var icon = this.el.select('i.fa-star', true).first();
28348
28349         if(label && icon){
28350             icon.remove();
28351         }
28352         
28353         this.fireEvent('valid', this);
28354     },
28355     
28356      /**
28357      * Mark this field as invalid
28358      * @param {String} msg The validation message
28359      */
28360     markInvalid : function(msg)
28361     {
28362         
28363         var label = this.el.select('label', true).first();
28364         var icon = this.el.select('i.fa-star', true).first();
28365
28366         if(label && !icon){
28367             this.el.select('.roo-date-split-field-label', true).createChild({
28368                 tag : 'i',
28369                 cls : 'text-danger fa fa-lg fa-star',
28370                 tooltip : 'This field is required',
28371                 style : 'margin-right:5px;'
28372             }, label, true);
28373         }
28374         
28375         this.fireEvent('invalid', this, msg);
28376     },
28377     
28378     clearInvalid : function()
28379     {
28380         var label = this.el.select('label', true).first();
28381         var icon = this.el.select('i.fa-star', true).first();
28382
28383         if(label && icon){
28384             icon.remove();
28385         }
28386         
28387         this.fireEvent('valid', this);
28388     },
28389     
28390     getName: function()
28391     {
28392         return this.name;
28393     }
28394     
28395 });
28396
28397  /**
28398  *
28399  * This is based on 
28400  * http://masonry.desandro.com
28401  *
28402  * The idea is to render all the bricks based on vertical width...
28403  *
28404  * The original code extends 'outlayer' - we might need to use that....
28405  * 
28406  */
28407
28408
28409 /**
28410  * @class Roo.bootstrap.LayoutMasonry
28411  * @extends Roo.bootstrap.Component
28412  * Bootstrap Layout Masonry class
28413  * 
28414  * @constructor
28415  * Create a new Element
28416  * @param {Object} config The config object
28417  */
28418
28419 Roo.bootstrap.LayoutMasonry = function(config){
28420     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28421     
28422     this.bricks = [];
28423     
28424 };
28425
28426 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28427     
28428     /**
28429      * @cfg {Boolean} isLayoutInstant = no animation?
28430      */   
28431     isLayoutInstant : false, // needed?
28432    
28433     /**
28434      * @cfg {Number} boxWidth  width of the columns
28435      */   
28436     boxWidth : 450,
28437     
28438       /**
28439      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28440      */   
28441     boxHeight : 0,
28442     
28443     /**
28444      * @cfg {Number} padWidth padding below box..
28445      */   
28446     padWidth : 10, 
28447     
28448     /**
28449      * @cfg {Number} gutter gutter width..
28450      */   
28451     gutter : 10, 
28452     
28453     /**
28454      * @cfg {Boolean} isAutoInitial defalut true
28455      */   
28456     isAutoInitial : true, 
28457     
28458     containerWidth: 0,
28459     
28460     /**
28461      * @cfg {Boolean} isHorizontal defalut false
28462      */   
28463     isHorizontal : false, 
28464
28465     currentSize : null,
28466     
28467     tag: 'div',
28468     
28469     cls: '',
28470     
28471     bricks: null, //CompositeElement
28472     
28473     cols : 1,
28474     
28475     _isLayoutInited : false,
28476     
28477 //    isAlternative : false, // only use for vertical layout...
28478     
28479     /**
28480      * @cfg {Number} alternativePadWidth padding below box..
28481      */   
28482     alternativePadWidth : 50, 
28483     
28484     getAutoCreate : function(){
28485         
28486         var cfg = {
28487             tag: this.tag,
28488             cls: 'blog-masonary-wrapper ' + this.cls,
28489             cn : {
28490                 cls : 'mas-boxes masonary'
28491             }
28492         };
28493         
28494         return cfg;
28495     },
28496     
28497     getChildContainer: function( )
28498     {
28499         if (this.boxesEl) {
28500             return this.boxesEl;
28501         }
28502         
28503         this.boxesEl = this.el.select('.mas-boxes').first();
28504         
28505         return this.boxesEl;
28506     },
28507     
28508     
28509     initEvents : function()
28510     {
28511         var _this = this;
28512         
28513         if(this.isAutoInitial){
28514             Roo.log('hook children rendered');
28515             this.on('childrenrendered', function() {
28516                 Roo.log('children rendered');
28517                 _this.initial();
28518             } ,this);
28519         }
28520     },
28521     
28522     initial : function()
28523     {
28524         this.currentSize = this.el.getBox(true);
28525         
28526         Roo.EventManager.onWindowResize(this.resize, this); 
28527
28528         if(!this.isAutoInitial){
28529             this.layout();
28530             return;
28531         }
28532         
28533         this.layout();
28534         
28535         return;
28536         //this.layout.defer(500,this);
28537         
28538     },
28539     
28540     resize : function()
28541     {
28542         Roo.log('resize');
28543         
28544         var cs = this.el.getBox(true);
28545         
28546         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28547             Roo.log("no change in with or X");
28548             return;
28549         }
28550         
28551         this.currentSize = cs;
28552         
28553         this.layout();
28554         
28555     },
28556     
28557     layout : function()
28558     {   
28559         this._resetLayout();
28560         
28561         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28562         
28563         this.layoutItems( isInstant );
28564       
28565         this._isLayoutInited = true;
28566         
28567     },
28568     
28569     _resetLayout : function()
28570     {
28571         if(this.isHorizontal){
28572             this.horizontalMeasureColumns();
28573             return;
28574         }
28575         
28576         this.verticalMeasureColumns();
28577         
28578     },
28579     
28580     verticalMeasureColumns : function()
28581     {
28582         this.getContainerWidth();
28583         
28584 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28585 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28586 //            return;
28587 //        }
28588         
28589         var boxWidth = this.boxWidth + this.padWidth;
28590         
28591         if(this.containerWidth < this.boxWidth){
28592             boxWidth = this.containerWidth
28593         }
28594         
28595         var containerWidth = this.containerWidth;
28596         
28597         var cols = Math.floor(containerWidth / boxWidth);
28598         
28599         this.cols = Math.max( cols, 1 );
28600         
28601         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28602         
28603         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28604         
28605         this.colWidth = boxWidth + avail - this.padWidth;
28606         
28607         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28608         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28609     },
28610     
28611     horizontalMeasureColumns : function()
28612     {
28613         this.getContainerWidth();
28614         
28615         var boxWidth = this.boxWidth;
28616         
28617         if(this.containerWidth < boxWidth){
28618             boxWidth = this.containerWidth;
28619         }
28620         
28621         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28622         
28623         this.el.setHeight(boxWidth);
28624         
28625     },
28626     
28627     getContainerWidth : function()
28628     {
28629         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28630     },
28631     
28632     layoutItems : function( isInstant )
28633     {
28634         var items = Roo.apply([], this.bricks);
28635         
28636         if(this.isHorizontal){
28637             this._horizontalLayoutItems( items , isInstant );
28638             return;
28639         }
28640         
28641 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28642 //            this._verticalAlternativeLayoutItems( items , isInstant );
28643 //            return;
28644 //        }
28645         
28646         this._verticalLayoutItems( items , isInstant );
28647         
28648     },
28649     
28650     _verticalLayoutItems : function ( items , isInstant)
28651     {
28652         if ( !items || !items.length ) {
28653             return;
28654         }
28655         
28656         var standard = [
28657             ['xs', 'xs', 'xs', 'tall'],
28658             ['xs', 'xs', 'tall'],
28659             ['xs', 'xs', 'sm'],
28660             ['xs', 'xs', 'xs'],
28661             ['xs', 'tall'],
28662             ['xs', 'sm'],
28663             ['xs', 'xs'],
28664             ['xs'],
28665             
28666             ['sm', 'xs', 'xs'],
28667             ['sm', 'xs'],
28668             ['sm'],
28669             
28670             ['tall', 'xs', 'xs', 'xs'],
28671             ['tall', 'xs', 'xs'],
28672             ['tall', 'xs'],
28673             ['tall']
28674             
28675         ];
28676         
28677         var queue = [];
28678         
28679         var boxes = [];
28680         
28681         var box = [];
28682         
28683         Roo.each(items, function(item, k){
28684             
28685             switch (item.size) {
28686                 // these layouts take up a full box,
28687                 case 'md' :
28688                 case 'md-left' :
28689                 case 'md-right' :
28690                 case 'wide' :
28691                     
28692                     if(box.length){
28693                         boxes.push(box);
28694                         box = [];
28695                     }
28696                     
28697                     boxes.push([item]);
28698                     
28699                     break;
28700                     
28701                 case 'xs' :
28702                 case 'sm' :
28703                 case 'tall' :
28704                     
28705                     box.push(item);
28706                     
28707                     break;
28708                 default :
28709                     break;
28710                     
28711             }
28712             
28713         }, this);
28714         
28715         if(box.length){
28716             boxes.push(box);
28717             box = [];
28718         }
28719         
28720         var filterPattern = function(box, length)
28721         {
28722             if(!box.length){
28723                 return;
28724             }
28725             
28726             var match = false;
28727             
28728             var pattern = box.slice(0, length);
28729             
28730             var format = [];
28731             
28732             Roo.each(pattern, function(i){
28733                 format.push(i.size);
28734             }, this);
28735             
28736             Roo.each(standard, function(s){
28737                 
28738                 if(String(s) != String(format)){
28739                     return;
28740                 }
28741                 
28742                 match = true;
28743                 return false;
28744                 
28745             }, this);
28746             
28747             if(!match && length == 1){
28748                 return;
28749             }
28750             
28751             if(!match){
28752                 filterPattern(box, length - 1);
28753                 return;
28754             }
28755                 
28756             queue.push(pattern);
28757
28758             box = box.slice(length, box.length);
28759
28760             filterPattern(box, 4);
28761
28762             return;
28763             
28764         }
28765         
28766         Roo.each(boxes, function(box, k){
28767             
28768             if(!box.length){
28769                 return;
28770             }
28771             
28772             if(box.length == 1){
28773                 queue.push(box);
28774                 return;
28775             }
28776             
28777             filterPattern(box, 4);
28778             
28779         }, this);
28780         
28781         this._processVerticalLayoutQueue( queue, isInstant );
28782         
28783     },
28784     
28785 //    _verticalAlternativeLayoutItems : function( items , isInstant )
28786 //    {
28787 //        if ( !items || !items.length ) {
28788 //            return;
28789 //        }
28790 //
28791 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
28792 //        
28793 //    },
28794     
28795     _horizontalLayoutItems : function ( items , isInstant)
28796     {
28797         if ( !items || !items.length || items.length < 3) {
28798             return;
28799         }
28800         
28801         items.reverse();
28802         
28803         var eItems = items.slice(0, 3);
28804         
28805         items = items.slice(3, items.length);
28806         
28807         var standard = [
28808             ['xs', 'xs', 'xs', 'wide'],
28809             ['xs', 'xs', 'wide'],
28810             ['xs', 'xs', 'sm'],
28811             ['xs', 'xs', 'xs'],
28812             ['xs', 'wide'],
28813             ['xs', 'sm'],
28814             ['xs', 'xs'],
28815             ['xs'],
28816             
28817             ['sm', 'xs', 'xs'],
28818             ['sm', 'xs'],
28819             ['sm'],
28820             
28821             ['wide', 'xs', 'xs', 'xs'],
28822             ['wide', 'xs', 'xs'],
28823             ['wide', 'xs'],
28824             ['wide'],
28825             
28826             ['wide-thin']
28827         ];
28828         
28829         var queue = [];
28830         
28831         var boxes = [];
28832         
28833         var box = [];
28834         
28835         Roo.each(items, function(item, k){
28836             
28837             switch (item.size) {
28838                 case 'md' :
28839                 case 'md-left' :
28840                 case 'md-right' :
28841                 case 'tall' :
28842                     
28843                     if(box.length){
28844                         boxes.push(box);
28845                         box = [];
28846                     }
28847                     
28848                     boxes.push([item]);
28849                     
28850                     break;
28851                     
28852                 case 'xs' :
28853                 case 'sm' :
28854                 case 'wide' :
28855                 case 'wide-thin' :
28856                     
28857                     box.push(item);
28858                     
28859                     break;
28860                 default :
28861                     break;
28862                     
28863             }
28864             
28865         }, this);
28866         
28867         if(box.length){
28868             boxes.push(box);
28869             box = [];
28870         }
28871         
28872         var filterPattern = function(box, length)
28873         {
28874             if(!box.length){
28875                 return;
28876             }
28877             
28878             var match = false;
28879             
28880             var pattern = box.slice(0, length);
28881             
28882             var format = [];
28883             
28884             Roo.each(pattern, function(i){
28885                 format.push(i.size);
28886             }, this);
28887             
28888             Roo.each(standard, function(s){
28889                 
28890                 if(String(s) != String(format)){
28891                     return;
28892                 }
28893                 
28894                 match = true;
28895                 return false;
28896                 
28897             }, this);
28898             
28899             if(!match && length == 1){
28900                 return;
28901             }
28902             
28903             if(!match){
28904                 filterPattern(box, length - 1);
28905                 return;
28906             }
28907                 
28908             queue.push(pattern);
28909
28910             box = box.slice(length, box.length);
28911
28912             filterPattern(box, 4);
28913
28914             return;
28915             
28916         }
28917         
28918         Roo.each(boxes, function(box, k){
28919             
28920             if(!box.length){
28921                 return;
28922             }
28923             
28924             if(box.length == 1){
28925                 queue.push(box);
28926                 return;
28927             }
28928             
28929             filterPattern(box, 4);
28930             
28931         }, this);
28932         
28933         
28934         var prune = [];
28935         
28936         var pos = this.el.getBox(true);
28937         
28938         var minX = pos.x;
28939         
28940         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
28941         
28942         var hit_end = false;
28943         
28944         Roo.each(queue, function(box){
28945             
28946             if(hit_end){
28947                 
28948                 Roo.each(box, function(b){
28949                 
28950                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
28951                     b.el.hide();
28952
28953                 }, this);
28954
28955                 return;
28956             }
28957             
28958             var mx = 0;
28959             
28960             Roo.each(box, function(b){
28961                 
28962                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
28963                 b.el.show();
28964
28965                 mx = Math.max(mx, b.x);
28966                 
28967             }, this);
28968             
28969             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
28970             
28971             if(maxX < minX){
28972                 
28973                 Roo.each(box, function(b){
28974                 
28975                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
28976                     b.el.hide();
28977                     
28978                 }, this);
28979                 
28980                 hit_end = true;
28981                 
28982                 return;
28983             }
28984             
28985             prune.push(box);
28986             
28987         }, this);
28988         
28989         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
28990     },
28991     
28992     /** Sets position of item in DOM
28993     * @param {Element} item
28994     * @param {Number} x - horizontal position
28995     * @param {Number} y - vertical position
28996     * @param {Boolean} isInstant - disables transitions
28997     */
28998     _processVerticalLayoutQueue : function( queue, isInstant )
28999     {
29000         var pos = this.el.getBox(true);
29001         var x = pos.x;
29002         var y = pos.y;
29003         var maxY = [];
29004         
29005         for (var i = 0; i < this.cols; i++){
29006             maxY[i] = pos.y;
29007         }
29008         
29009         Roo.each(queue, function(box, k){
29010             
29011             var col = k % this.cols;
29012             
29013             Roo.each(box, function(b,kk){
29014                 
29015                 b.el.position('absolute');
29016                 
29017                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29018                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29019                 
29020                 if(b.size == 'md-left' || b.size == 'md-right'){
29021                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29022                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29023                 }
29024                 
29025                 b.el.setWidth(width);
29026                 b.el.setHeight(height);
29027                 
29028             }, this);
29029             
29030             for (var i = 0; i < this.cols; i++){
29031                 
29032                 if(maxY[i] < maxY[col]){
29033                     col = i;
29034                     continue;
29035                 }
29036                 
29037                 col = Math.min(col, i);
29038                 
29039             }
29040             
29041             x = pos.x + col * (this.colWidth + this.padWidth);
29042             
29043             y = maxY[col];
29044             
29045             var positions = [];
29046             
29047             switch (box.length){
29048                 case 1 :
29049                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29050                     break;
29051                 case 2 :
29052                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29053                     break;
29054                 case 3 :
29055                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29056                     break;
29057                 case 4 :
29058                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29059                     break;
29060                 default :
29061                     break;
29062             }
29063             
29064             Roo.each(box, function(b,kk){
29065                 
29066                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29067                 
29068                 var sz = b.el.getSize();
29069                 
29070                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29071                 
29072             }, this);
29073             
29074         }, this);
29075         
29076         var mY = 0;
29077         
29078         for (var i = 0; i < this.cols; i++){
29079             mY = Math.max(mY, maxY[i]);
29080         }
29081         
29082         this.el.setHeight(mY - pos.y);
29083         
29084     },
29085     
29086 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29087 //    {
29088 //        var pos = this.el.getBox(true);
29089 //        var x = pos.x;
29090 //        var y = pos.y;
29091 //        var maxX = pos.right;
29092 //        
29093 //        var maxHeight = 0;
29094 //        
29095 //        Roo.each(items, function(item, k){
29096 //            
29097 //            var c = k % 2;
29098 //            
29099 //            item.el.position('absolute');
29100 //                
29101 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29102 //
29103 //            item.el.setWidth(width);
29104 //
29105 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29106 //
29107 //            item.el.setHeight(height);
29108 //            
29109 //            if(c == 0){
29110 //                item.el.setXY([x, y], isInstant ? false : true);
29111 //            } else {
29112 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29113 //            }
29114 //            
29115 //            y = y + height + this.alternativePadWidth;
29116 //            
29117 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29118 //            
29119 //        }, this);
29120 //        
29121 //        this.el.setHeight(maxHeight);
29122 //        
29123 //    },
29124     
29125     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29126     {
29127         var pos = this.el.getBox(true);
29128         
29129         var minX = pos.x;
29130         var minY = pos.y;
29131         
29132         var maxX = pos.right;
29133         
29134         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29135         
29136         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29137         
29138         Roo.each(queue, function(box, k){
29139             
29140             Roo.each(box, function(b, kk){
29141                 
29142                 b.el.position('absolute');
29143                 
29144                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29145                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29146                 
29147                 if(b.size == 'md-left' || b.size == 'md-right'){
29148                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29149                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29150                 }
29151                 
29152                 b.el.setWidth(width);
29153                 b.el.setHeight(height);
29154                 
29155             }, this);
29156             
29157             if(!box.length){
29158                 return;
29159             }
29160             
29161             var positions = [];
29162             
29163             switch (box.length){
29164                 case 1 :
29165                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29166                     break;
29167                 case 2 :
29168                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29169                     break;
29170                 case 3 :
29171                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29172                     break;
29173                 case 4 :
29174                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29175                     break;
29176                 default :
29177                     break;
29178             }
29179             
29180             Roo.each(box, function(b,kk){
29181                 
29182                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29183                 
29184                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29185                 
29186             }, this);
29187             
29188         }, this);
29189         
29190     },
29191     
29192     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29193     {
29194         Roo.each(eItems, function(b,k){
29195             
29196             b.size = (k == 0) ? 'sm' : 'xs';
29197             b.x = (k == 0) ? 2 : 1;
29198             b.y = (k == 0) ? 2 : 1;
29199             
29200             b.el.position('absolute');
29201             
29202             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29203                 
29204             b.el.setWidth(width);
29205             
29206             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29207             
29208             b.el.setHeight(height);
29209             
29210         }, this);
29211
29212         var positions = [];
29213         
29214         positions.push({
29215             x : maxX - this.unitWidth * 2 - this.gutter,
29216             y : minY
29217         });
29218         
29219         positions.push({
29220             x : maxX - this.unitWidth,
29221             y : minY + (this.unitWidth + this.gutter) * 2
29222         });
29223         
29224         positions.push({
29225             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29226             y : minY
29227         });
29228         
29229         Roo.each(eItems, function(b,k){
29230             
29231             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29232
29233         }, this);
29234         
29235     },
29236     
29237     getVerticalOneBoxColPositions : function(x, y, box)
29238     {
29239         var pos = [];
29240         
29241         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29242         
29243         if(box[0].size == 'md-left'){
29244             rand = 0;
29245         }
29246         
29247         if(box[0].size == 'md-right'){
29248             rand = 1;
29249         }
29250         
29251         pos.push({
29252             x : x + (this.unitWidth + this.gutter) * rand,
29253             y : y
29254         });
29255         
29256         return pos;
29257     },
29258     
29259     getVerticalTwoBoxColPositions : function(x, y, box)
29260     {
29261         var pos = [];
29262         
29263         if(box[0].size == 'xs'){
29264             
29265             pos.push({
29266                 x : x,
29267                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29268             });
29269
29270             pos.push({
29271                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29272                 y : y
29273             });
29274             
29275             return pos;
29276             
29277         }
29278         
29279         pos.push({
29280             x : x,
29281             y : y
29282         });
29283
29284         pos.push({
29285             x : x + (this.unitWidth + this.gutter) * 2,
29286             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29287         });
29288         
29289         return pos;
29290         
29291     },
29292     
29293     getVerticalThreeBoxColPositions : function(x, y, box)
29294     {
29295         var pos = [];
29296         
29297         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29298             
29299             pos.push({
29300                 x : x,
29301                 y : y
29302             });
29303
29304             pos.push({
29305                 x : x + (this.unitWidth + this.gutter) * 1,
29306                 y : y
29307             });
29308             
29309             pos.push({
29310                 x : x + (this.unitWidth + this.gutter) * 2,
29311                 y : y
29312             });
29313             
29314             return pos;
29315             
29316         }
29317         
29318         if(box[0].size == 'xs' && box[1].size == 'xs'){
29319             
29320             pos.push({
29321                 x : x,
29322                 y : y
29323             });
29324
29325             pos.push({
29326                 x : x,
29327                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29328             });
29329             
29330             pos.push({
29331                 x : x + (this.unitWidth + this.gutter) * 1,
29332                 y : y
29333             });
29334             
29335             return pos;
29336             
29337         }
29338         
29339         pos.push({
29340             x : x,
29341             y : y
29342         });
29343
29344         pos.push({
29345             x : x + (this.unitWidth + this.gutter) * 2,
29346             y : y
29347         });
29348
29349         pos.push({
29350             x : x + (this.unitWidth + this.gutter) * 2,
29351             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29352         });
29353             
29354         return pos;
29355         
29356     },
29357     
29358     getVerticalFourBoxColPositions : function(x, y, box)
29359     {
29360         var pos = [];
29361         
29362         if(box[0].size == 'xs'){
29363             
29364             pos.push({
29365                 x : x,
29366                 y : y
29367             });
29368
29369             pos.push({
29370                 x : x,
29371                 y : y + (this.unitHeight + this.gutter) * 1
29372             });
29373             
29374             pos.push({
29375                 x : x,
29376                 y : y + (this.unitHeight + this.gutter) * 2
29377             });
29378             
29379             pos.push({
29380                 x : x + (this.unitWidth + this.gutter) * 1,
29381                 y : y
29382             });
29383             
29384             return pos;
29385             
29386         }
29387         
29388         pos.push({
29389             x : x,
29390             y : y
29391         });
29392
29393         pos.push({
29394             x : x + (this.unitWidth + this.gutter) * 2,
29395             y : y
29396         });
29397
29398         pos.push({
29399             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29400             y : y + (this.unitHeight + this.gutter) * 1
29401         });
29402
29403         pos.push({
29404             x : x + (this.unitWidth + this.gutter) * 2,
29405             y : y + (this.unitWidth + this.gutter) * 2
29406         });
29407
29408         return pos;
29409         
29410     },
29411     
29412     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29413     {
29414         var pos = [];
29415         
29416         if(box[0].size == 'md-left'){
29417             pos.push({
29418                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29419                 y : minY
29420             });
29421             
29422             return pos;
29423         }
29424         
29425         if(box[0].size == 'md-right'){
29426             pos.push({
29427                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29428                 y : minY + (this.unitWidth + this.gutter) * 1
29429             });
29430             
29431             return pos;
29432         }
29433         
29434         var rand = Math.floor(Math.random() * (4 - box[0].y));
29435         
29436         pos.push({
29437             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29438             y : minY + (this.unitWidth + this.gutter) * rand
29439         });
29440         
29441         return pos;
29442         
29443     },
29444     
29445     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29446     {
29447         var pos = [];
29448         
29449         if(box[0].size == 'xs'){
29450             
29451             pos.push({
29452                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29453                 y : minY
29454             });
29455
29456             pos.push({
29457                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29458                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29459             });
29460             
29461             return pos;
29462             
29463         }
29464         
29465         pos.push({
29466             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29467             y : minY
29468         });
29469
29470         pos.push({
29471             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29472             y : minY + (this.unitWidth + this.gutter) * 2
29473         });
29474         
29475         return pos;
29476         
29477     },
29478     
29479     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29480     {
29481         var pos = [];
29482         
29483         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29484             
29485             pos.push({
29486                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29487                 y : minY
29488             });
29489
29490             pos.push({
29491                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29492                 y : minY + (this.unitWidth + this.gutter) * 1
29493             });
29494             
29495             pos.push({
29496                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29497                 y : minY + (this.unitWidth + this.gutter) * 2
29498             });
29499             
29500             return pos;
29501             
29502         }
29503         
29504         if(box[0].size == 'xs' && box[1].size == 'xs'){
29505             
29506             pos.push({
29507                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29508                 y : minY
29509             });
29510
29511             pos.push({
29512                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29513                 y : minY
29514             });
29515             
29516             pos.push({
29517                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29518                 y : minY + (this.unitWidth + this.gutter) * 1
29519             });
29520             
29521             return pos;
29522             
29523         }
29524         
29525         pos.push({
29526             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29527             y : minY
29528         });
29529
29530         pos.push({
29531             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29532             y : minY + (this.unitWidth + this.gutter) * 2
29533         });
29534
29535         pos.push({
29536             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29537             y : minY + (this.unitWidth + this.gutter) * 2
29538         });
29539             
29540         return pos;
29541         
29542     },
29543     
29544     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29545     {
29546         var pos = [];
29547         
29548         if(box[0].size == 'xs'){
29549             
29550             pos.push({
29551                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29552                 y : minY
29553             });
29554
29555             pos.push({
29556                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29557                 y : minY
29558             });
29559             
29560             pos.push({
29561                 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),
29562                 y : minY
29563             });
29564             
29565             pos.push({
29566                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29567                 y : minY + (this.unitWidth + this.gutter) * 1
29568             });
29569             
29570             return pos;
29571             
29572         }
29573         
29574         pos.push({
29575             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29576             y : minY
29577         });
29578         
29579         pos.push({
29580             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29581             y : minY + (this.unitWidth + this.gutter) * 2
29582         });
29583         
29584         pos.push({
29585             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29586             y : minY + (this.unitWidth + this.gutter) * 2
29587         });
29588         
29589         pos.push({
29590             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),
29591             y : minY + (this.unitWidth + this.gutter) * 2
29592         });
29593
29594         return pos;
29595         
29596     }
29597     
29598 });
29599
29600  
29601
29602  /**
29603  *
29604  * This is based on 
29605  * http://masonry.desandro.com
29606  *
29607  * The idea is to render all the bricks based on vertical width...
29608  *
29609  * The original code extends 'outlayer' - we might need to use that....
29610  * 
29611  */
29612
29613
29614 /**
29615  * @class Roo.bootstrap.LayoutMasonryAuto
29616  * @extends Roo.bootstrap.Component
29617  * Bootstrap Layout Masonry class
29618  * 
29619  * @constructor
29620  * Create a new Element
29621  * @param {Object} config The config object
29622  */
29623
29624 Roo.bootstrap.LayoutMasonryAuto = function(config){
29625     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29626 };
29627
29628 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
29629     
29630       /**
29631      * @cfg {Boolean} isFitWidth  - resize the width..
29632      */   
29633     isFitWidth : false,  // options..
29634     /**
29635      * @cfg {Boolean} isOriginLeft = left align?
29636      */   
29637     isOriginLeft : true,
29638     /**
29639      * @cfg {Boolean} isOriginTop = top align?
29640      */   
29641     isOriginTop : false,
29642     /**
29643      * @cfg {Boolean} isLayoutInstant = no animation?
29644      */   
29645     isLayoutInstant : false, // needed?
29646     /**
29647      * @cfg {Boolean} isResizingContainer = not sure if this is used..
29648      */   
29649     isResizingContainer : true,
29650     /**
29651      * @cfg {Number} columnWidth  width of the columns 
29652      */   
29653     
29654     columnWidth : 0,
29655     /**
29656      * @cfg {Number} padHeight padding below box..
29657      */   
29658     
29659     padHeight : 10, 
29660     
29661     /**
29662      * @cfg {Boolean} isAutoInitial defalut true
29663      */   
29664     
29665     isAutoInitial : true, 
29666     
29667     // private?
29668     gutter : 0,
29669     
29670     containerWidth: 0,
29671     initialColumnWidth : 0,
29672     currentSize : null,
29673     
29674     colYs : null, // array.
29675     maxY : 0,
29676     padWidth: 10,
29677     
29678     
29679     tag: 'div',
29680     cls: '',
29681     bricks: null, //CompositeElement
29682     cols : 0, // array?
29683     // element : null, // wrapped now this.el
29684     _isLayoutInited : null, 
29685     
29686     
29687     getAutoCreate : function(){
29688         
29689         var cfg = {
29690             tag: this.tag,
29691             cls: 'blog-masonary-wrapper ' + this.cls,
29692             cn : {
29693                 cls : 'mas-boxes masonary'
29694             }
29695         };
29696         
29697         return cfg;
29698     },
29699     
29700     getChildContainer: function( )
29701     {
29702         if (this.boxesEl) {
29703             return this.boxesEl;
29704         }
29705         
29706         this.boxesEl = this.el.select('.mas-boxes').first();
29707         
29708         return this.boxesEl;
29709     },
29710     
29711     
29712     initEvents : function()
29713     {
29714         var _this = this;
29715         
29716         if(this.isAutoInitial){
29717             Roo.log('hook children rendered');
29718             this.on('childrenrendered', function() {
29719                 Roo.log('children rendered');
29720                 _this.initial();
29721             } ,this);
29722         }
29723         
29724     },
29725     
29726     initial : function()
29727     {
29728         this.reloadItems();
29729
29730         this.currentSize = this.el.getBox(true);
29731
29732         /// was window resize... - let's see if this works..
29733         Roo.EventManager.onWindowResize(this.resize, this); 
29734
29735         if(!this.isAutoInitial){
29736             this.layout();
29737             return;
29738         }
29739         
29740         this.layout.defer(500,this);
29741     },
29742     
29743     reloadItems: function()
29744     {
29745         this.bricks = this.el.select('.masonry-brick', true);
29746         
29747         this.bricks.each(function(b) {
29748             //Roo.log(b.getSize());
29749             if (!b.attr('originalwidth')) {
29750                 b.attr('originalwidth',  b.getSize().width);
29751             }
29752             
29753         });
29754         
29755         Roo.log(this.bricks.elements.length);
29756     },
29757     
29758     resize : function()
29759     {
29760         Roo.log('resize');
29761         var cs = this.el.getBox(true);
29762         
29763         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29764             Roo.log("no change in with or X");
29765             return;
29766         }
29767         this.currentSize = cs;
29768         this.layout();
29769     },
29770     
29771     layout : function()
29772     {
29773          Roo.log('layout');
29774         this._resetLayout();
29775         //this._manageStamps();
29776       
29777         // don't animate first layout
29778         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29779         this.layoutItems( isInstant );
29780       
29781         // flag for initalized
29782         this._isLayoutInited = true;
29783     },
29784     
29785     layoutItems : function( isInstant )
29786     {
29787         //var items = this._getItemsForLayout( this.items );
29788         // original code supports filtering layout items.. we just ignore it..
29789         
29790         this._layoutItems( this.bricks , isInstant );
29791       
29792         this._postLayout();
29793     },
29794     _layoutItems : function ( items , isInstant)
29795     {
29796        //this.fireEvent( 'layout', this, items );
29797     
29798
29799         if ( !items || !items.elements.length ) {
29800           // no items, emit event with empty array
29801             return;
29802         }
29803
29804         var queue = [];
29805         items.each(function(item) {
29806             Roo.log("layout item");
29807             Roo.log(item);
29808             // get x/y object from method
29809             var position = this._getItemLayoutPosition( item );
29810             // enqueue
29811             position.item = item;
29812             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29813             queue.push( position );
29814         }, this);
29815       
29816         this._processLayoutQueue( queue );
29817     },
29818     /** Sets position of item in DOM
29819     * @param {Element} item
29820     * @param {Number} x - horizontal position
29821     * @param {Number} y - vertical position
29822     * @param {Boolean} isInstant - disables transitions
29823     */
29824     _processLayoutQueue : function( queue )
29825     {
29826         for ( var i=0, len = queue.length; i < len; i++ ) {
29827             var obj = queue[i];
29828             obj.item.position('absolute');
29829             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29830         }
29831     },
29832       
29833     
29834     /**
29835     * Any logic you want to do after each layout,
29836     * i.e. size the container
29837     */
29838     _postLayout : function()
29839     {
29840         this.resizeContainer();
29841     },
29842     
29843     resizeContainer : function()
29844     {
29845         if ( !this.isResizingContainer ) {
29846             return;
29847         }
29848         var size = this._getContainerSize();
29849         if ( size ) {
29850             this.el.setSize(size.width,size.height);
29851             this.boxesEl.setSize(size.width,size.height);
29852         }
29853     },
29854     
29855     
29856     
29857     _resetLayout : function()
29858     {
29859         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
29860         this.colWidth = this.el.getWidth();
29861         //this.gutter = this.el.getWidth(); 
29862         
29863         this.measureColumns();
29864
29865         // reset column Y
29866         var i = this.cols;
29867         this.colYs = [];
29868         while (i--) {
29869             this.colYs.push( 0 );
29870         }
29871     
29872         this.maxY = 0;
29873     },
29874
29875     measureColumns : function()
29876     {
29877         this.getContainerWidth();
29878       // if columnWidth is 0, default to outerWidth of first item
29879         if ( !this.columnWidth ) {
29880             var firstItem = this.bricks.first();
29881             Roo.log(firstItem);
29882             this.columnWidth  = this.containerWidth;
29883             if (firstItem && firstItem.attr('originalwidth') ) {
29884                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
29885             }
29886             // columnWidth fall back to item of first element
29887             Roo.log("set column width?");
29888                         this.initialColumnWidth = this.columnWidth  ;
29889
29890             // if first elem has no width, default to size of container
29891             
29892         }
29893         
29894         
29895         if (this.initialColumnWidth) {
29896             this.columnWidth = this.initialColumnWidth;
29897         }
29898         
29899         
29900             
29901         // column width is fixed at the top - however if container width get's smaller we should
29902         // reduce it...
29903         
29904         // this bit calcs how man columns..
29905             
29906         var columnWidth = this.columnWidth += this.gutter;
29907       
29908         // calculate columns
29909         var containerWidth = this.containerWidth + this.gutter;
29910         
29911         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
29912         // fix rounding errors, typically with gutters
29913         var excess = columnWidth - containerWidth % columnWidth;
29914         
29915         
29916         // if overshoot is less than a pixel, round up, otherwise floor it
29917         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
29918         cols = Math[ mathMethod ]( cols );
29919         this.cols = Math.max( cols, 1 );
29920         
29921         
29922          // padding positioning..
29923         var totalColWidth = this.cols * this.columnWidth;
29924         var padavail = this.containerWidth - totalColWidth;
29925         // so for 2 columns - we need 3 'pads'
29926         
29927         var padNeeded = (1+this.cols) * this.padWidth;
29928         
29929         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
29930         
29931         this.columnWidth += padExtra
29932         //this.padWidth = Math.floor(padavail /  ( this.cols));
29933         
29934         // adjust colum width so that padding is fixed??
29935         
29936         // we have 3 columns ... total = width * 3
29937         // we have X left over... that should be used by 
29938         
29939         //if (this.expandC) {
29940             
29941         //}
29942         
29943         
29944         
29945     },
29946     
29947     getContainerWidth : function()
29948     {
29949        /* // container is parent if fit width
29950         var container = this.isFitWidth ? this.element.parentNode : this.element;
29951         // check that this.size and size are there
29952         // IE8 triggers resize on body size change, so they might not be
29953         
29954         var size = getSize( container );  //FIXME
29955         this.containerWidth = size && size.innerWidth; //FIXME
29956         */
29957          
29958         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29959         
29960     },
29961     
29962     _getItemLayoutPosition : function( item )  // what is item?
29963     {
29964         // we resize the item to our columnWidth..
29965       
29966         item.setWidth(this.columnWidth);
29967         item.autoBoxAdjust  = false;
29968         
29969         var sz = item.getSize();
29970  
29971         // how many columns does this brick span
29972         var remainder = this.containerWidth % this.columnWidth;
29973         
29974         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
29975         // round if off by 1 pixel, otherwise use ceil
29976         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
29977         colSpan = Math.min( colSpan, this.cols );
29978         
29979         // normally this should be '1' as we dont' currently allow multi width columns..
29980         
29981         var colGroup = this._getColGroup( colSpan );
29982         // get the minimum Y value from the columns
29983         var minimumY = Math.min.apply( Math, colGroup );
29984         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
29985         
29986         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
29987          
29988         // position the brick
29989         var position = {
29990             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
29991             y: this.currentSize.y + minimumY + this.padHeight
29992         };
29993         
29994         Roo.log(position);
29995         // apply setHeight to necessary columns
29996         var setHeight = minimumY + sz.height + this.padHeight;
29997         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
29998         
29999         var setSpan = this.cols + 1 - colGroup.length;
30000         for ( var i = 0; i < setSpan; i++ ) {
30001           this.colYs[ shortColIndex + i ] = setHeight ;
30002         }
30003       
30004         return position;
30005     },
30006     
30007     /**
30008      * @param {Number} colSpan - number of columns the element spans
30009      * @returns {Array} colGroup
30010      */
30011     _getColGroup : function( colSpan )
30012     {
30013         if ( colSpan < 2 ) {
30014           // if brick spans only one column, use all the column Ys
30015           return this.colYs;
30016         }
30017       
30018         var colGroup = [];
30019         // how many different places could this brick fit horizontally
30020         var groupCount = this.cols + 1 - colSpan;
30021         // for each group potential horizontal position
30022         for ( var i = 0; i < groupCount; i++ ) {
30023           // make an array of colY values for that one group
30024           var groupColYs = this.colYs.slice( i, i + colSpan );
30025           // and get the max value of the array
30026           colGroup[i] = Math.max.apply( Math, groupColYs );
30027         }
30028         return colGroup;
30029     },
30030     /*
30031     _manageStamp : function( stamp )
30032     {
30033         var stampSize =  stamp.getSize();
30034         var offset = stamp.getBox();
30035         // get the columns that this stamp affects
30036         var firstX = this.isOriginLeft ? offset.x : offset.right;
30037         var lastX = firstX + stampSize.width;
30038         var firstCol = Math.floor( firstX / this.columnWidth );
30039         firstCol = Math.max( 0, firstCol );
30040         
30041         var lastCol = Math.floor( lastX / this.columnWidth );
30042         // lastCol should not go over if multiple of columnWidth #425
30043         lastCol -= lastX % this.columnWidth ? 0 : 1;
30044         lastCol = Math.min( this.cols - 1, lastCol );
30045         
30046         // set colYs to bottom of the stamp
30047         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30048             stampSize.height;
30049             
30050         for ( var i = firstCol; i <= lastCol; i++ ) {
30051           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30052         }
30053     },
30054     */
30055     
30056     _getContainerSize : function()
30057     {
30058         this.maxY = Math.max.apply( Math, this.colYs );
30059         var size = {
30060             height: this.maxY
30061         };
30062       
30063         if ( this.isFitWidth ) {
30064             size.width = this._getContainerFitWidth();
30065         }
30066       
30067         return size;
30068     },
30069     
30070     _getContainerFitWidth : function()
30071     {
30072         var unusedCols = 0;
30073         // count unused columns
30074         var i = this.cols;
30075         while ( --i ) {
30076           if ( this.colYs[i] !== 0 ) {
30077             break;
30078           }
30079           unusedCols++;
30080         }
30081         // fit container to columns that have been used
30082         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30083     },
30084     
30085     needsResizeLayout : function()
30086     {
30087         var previousWidth = this.containerWidth;
30088         this.getContainerWidth();
30089         return previousWidth !== this.containerWidth;
30090     }
30091  
30092 });
30093
30094  
30095
30096  /*
30097  * - LGPL
30098  *
30099  * element
30100  * 
30101  */
30102
30103 /**
30104  * @class Roo.bootstrap.MasonryBrick
30105  * @extends Roo.bootstrap.Component
30106  * Bootstrap MasonryBrick class
30107  * 
30108  * @constructor
30109  * Create a new MasonryBrick
30110  * @param {Object} config The config object
30111  */
30112
30113 Roo.bootstrap.MasonryBrick = function(config){
30114     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30115     
30116     this.addEvents({
30117         // raw events
30118         /**
30119          * @event click
30120          * When a MasonryBrick is clcik
30121          * @param {Roo.bootstrap.MasonryBrick} this
30122          * @param {Roo.EventObject} e
30123          */
30124         "click" : true
30125     });
30126 };
30127
30128 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30129     
30130     /**
30131      * @cfg {String} title
30132      */   
30133     title : '',
30134     /**
30135      * @cfg {String} html
30136      */   
30137     html : '',
30138     /**
30139      * @cfg {String} bgimage
30140      */   
30141     bgimage : '',
30142     /**
30143      * @cfg {String} cls
30144      */   
30145     cls : '',
30146     /**
30147      * @cfg {String} href
30148      */   
30149     href : '',
30150     /**
30151      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30152      */   
30153     size : 'xs',
30154     
30155     /**
30156      * @cfg {String} (center|bottom) placetitle
30157      */   
30158     placetitle : '',
30159     
30160     getAutoCreate : function()
30161     {
30162         var cls = 'masonry-brick';
30163         
30164         if(this.href.length){
30165             cls += ' masonry-brick-link';
30166         }
30167         
30168         if(this.bgimage.length){
30169             cls += ' masonry-brick-image';
30170         }
30171         
30172         if(this.size){
30173             cls += ' masonry-' + this.size + '-brick';
30174         }
30175         
30176         if(this.placetitle.length){
30177             
30178             switch (this.placetitle) {
30179                 case 'center' :
30180                     cls += ' masonry-center-title';
30181                     break;
30182                 case 'bottom' :
30183                     cls += ' masonry-bottom-title';
30184                     break;
30185                 default:
30186                     break;
30187             }
30188             
30189         } else {
30190             if(!this.html.length && !this.bgimage.length){
30191                 cls += ' masonry-center-title';
30192             }
30193
30194             if(!this.html.length && this.bgimage.length){
30195                 cls += ' masonry-bottom-title';
30196             }
30197         }
30198         
30199         if(this.cls){
30200             cls += ' ' + this.cls;
30201         }
30202         
30203         var cfg = {
30204             tag: (this.href.length) ? 'a' : 'div',
30205             cls: cls,
30206             cn: [
30207                 {
30208                     tag: 'div',
30209                     cls: 'masonry-brick-paragraph',
30210                     cn: []
30211                 }
30212             ]
30213         };
30214         
30215         if(this.href.length){
30216             cfg.href = this.href;
30217         }
30218         
30219         var cn = cfg.cn[0].cn;
30220         
30221         if(this.title.length){
30222             cn.push({
30223                 tag: 'h4',
30224                 cls: 'masonry-brick-title',
30225                 html: this.title
30226             });
30227         }
30228         
30229         if(this.html.length){
30230             cn.push({
30231                 tag: 'p',
30232                 cls: 'masonry-brick-text',
30233                 html: this.html
30234             });
30235         }
30236         
30237         if(this.bgimage.length){
30238             cfg.cn.push({
30239                 tag: 'img',
30240                 cls: 'masonry-brick-image-view',
30241                 src: this.bgimage
30242             });
30243         }
30244         
30245         return cfg;
30246         
30247     },
30248     
30249     initEvents: function() 
30250     {
30251         switch (this.size) {
30252             case 'xs' :
30253 //                this.intSize = 1;
30254                 this.x = 1;
30255                 this.y = 1;
30256                 break;
30257             case 'sm' :
30258 //                this.intSize = 2;
30259                 this.x = 2;
30260                 this.y = 2;
30261                 break;
30262             case 'md' :
30263             case 'md-left' :
30264             case 'md-right' :
30265 //                this.intSize = 3;
30266                 this.x = 3;
30267                 this.y = 3;
30268                 break;
30269             case 'tall' :
30270 //                this.intSize = 3;
30271                 this.x = 2;
30272                 this.y = 3;
30273                 break;
30274             case 'wide' :
30275 //                this.intSize = 3;
30276                 this.x = 3;
30277                 this.y = 2;
30278                 break;
30279             case 'wide-thin' :
30280 //                this.intSize = 3;
30281                 this.x = 3;
30282                 this.y = 1;
30283                 break;
30284                         
30285             default :
30286                 break;
30287         }
30288         
30289         
30290         
30291         if(Roo.isTouch){
30292             this.el.on('touchstart', this.onTouchStart, this);
30293             this.el.on('touchmove', this.onTouchMove, this);
30294             this.el.on('touchend', this.onTouchEnd, this);
30295         } else {
30296             this.el.on('mouseenter'  ,this.enter, this);
30297             this.el.on('mouseleave', this.leave, this);
30298         }
30299         
30300         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30301             this.parent().bricks.push(this);   
30302         }
30303         
30304     },
30305     
30306     onClick: function(e, el)
30307     {
30308         alert('click');
30309         
30310         if(!Roo.isTouch){
30311             return;
30312         }
30313         
30314         var time = this.endTimer - this.startTimer;
30315         
30316         alert(time);
30317         
30318         if(time < 1000){
30319             return;
30320         }
30321         
30322         e.preventDefault();
30323     },
30324     
30325     enter: function(e, el)
30326     {
30327         e.preventDefault();
30328         
30329         if(this.bgimage.length && this.html.length){
30330             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30331         }
30332     },
30333     
30334     leave: function(e, el)
30335     {
30336         e.preventDefault();
30337         
30338         if(this.bgimage.length && this.html.length){
30339             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30340         }
30341     },
30342     
30343     onTouchStart: function(e, el)
30344     {
30345 //        e.preventDefault();
30346         
30347         if(!this.bgimage.length || !this.html.length){
30348             return;
30349         }
30350         
30351         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30352         
30353         this.timer = new Date().getTime();
30354         
30355         this.touchmoved = false;
30356     },
30357     
30358     onTouchMove: function(e, el)
30359     {
30360         this.touchmoved = true;
30361     },
30362     
30363     onTouchEnd: function(e, el)
30364     {
30365 //        e.preventDefault();
30366         
30367         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30368             return;
30369         }
30370         
30371         if(!this.bgimage.length || !this.html.length){
30372             
30373             if(this.href.length){
30374                 window.location.href = this.href;
30375             }
30376             
30377             return;
30378         }
30379         
30380         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30381         
30382         window.location.href = this.href;
30383     }
30384     
30385 });
30386
30387  
30388
30389  /*
30390  * - LGPL
30391  *
30392  * element
30393  * 
30394  */
30395
30396 /**
30397  * @class Roo.bootstrap.Brick
30398  * @extends Roo.bootstrap.Component
30399  * Bootstrap Brick class
30400  * 
30401  * @constructor
30402  * Create a new Brick
30403  * @param {Object} config The config object
30404  */
30405
30406 Roo.bootstrap.Brick = function(config){
30407     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30408     
30409     this.addEvents({
30410         // raw events
30411         /**
30412          * @event click
30413          * When a Brick is click
30414          * @param {Roo.bootstrap.Brick} this
30415          * @param {Roo.EventObject} e
30416          */
30417         "click" : true
30418     });
30419 };
30420
30421 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30422     
30423     /**
30424      * @cfg {String} title
30425      */   
30426     title : '',
30427     /**
30428      * @cfg {String} html
30429      */   
30430     html : '',
30431     /**
30432      * @cfg {String} bgimage
30433      */   
30434     bgimage : '',
30435     /**
30436      * @cfg {String} cls
30437      */   
30438     cls : '',
30439     /**
30440      * @cfg {String} href
30441      */   
30442     href : '',
30443     /**
30444      * @cfg {String} video
30445      */   
30446     video : '',
30447     /**
30448      * @cfg {Boolean} square
30449      */   
30450     square : true,
30451     
30452     getAutoCreate : function()
30453     {
30454         var cls = 'roo-brick';
30455         
30456         if(this.href.length){
30457             cls += ' roo-brick-link';
30458         }
30459         
30460         if(this.bgimage.length){
30461             cls += ' roo-brick-image';
30462         }
30463         
30464         if(!this.html.length && !this.bgimage.length){
30465             cls += ' roo-brick-center-title';
30466         }
30467         
30468         if(!this.html.length && this.bgimage.length){
30469             cls += ' roo-brick-bottom-title';
30470         }
30471         
30472         if(this.cls){
30473             cls += ' ' + this.cls;
30474         }
30475         
30476         var cfg = {
30477             tag: (this.href.length) ? 'a' : 'div',
30478             cls: cls,
30479             cn: [
30480                 {
30481                     tag: 'div',
30482                     cls: 'roo-brick-paragraph',
30483                     cn: []
30484                 }
30485             ]
30486         };
30487         
30488         if(this.href.length){
30489             cfg.href = this.href;
30490         }
30491         
30492         var cn = cfg.cn[0].cn;
30493         
30494         if(this.title.length){
30495             cn.push({
30496                 tag: 'h4',
30497                 cls: 'roo-brick-title',
30498                 html: this.title
30499             });
30500         }
30501         
30502         if(this.html.length){
30503             cn.push({
30504                 tag: 'p',
30505                 cls: 'roo-brick-text',
30506                 html: this.html
30507             });
30508         }
30509         
30510         if(this.bgimage.length){
30511             cfg.cn.push({
30512                 tag: 'img',
30513                 cls: 'roo-brick-image-view',
30514                 src: this.bgimage
30515             });
30516         }
30517         
30518         return cfg;
30519     },
30520     
30521     initEvents: function() 
30522     {
30523         if(this.title.length || this.html.length){
30524             this.el.on('mouseenter'  ,this.enter, this);
30525             this.el.on('mouseleave', this.leave, this);
30526         }
30527         
30528         
30529         Roo.EventManager.onWindowResize(this.resize, this); 
30530         
30531         this.resize();
30532     },
30533     
30534     resize : function()
30535     {
30536         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30537         
30538         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30539 //        paragraph.setHeight(paragraph.getWidth());
30540         
30541         if(this.bgimage.length){
30542             var image = this.el.select('.roo-brick-image-view', true).first();
30543             image.setWidth(paragraph.getWidth());
30544             image.setHeight(paragraph.getWidth());
30545         }
30546         
30547     },
30548     
30549     enter: function(e, el)
30550     {
30551         e.preventDefault();
30552         
30553         if(this.bgimage.length){
30554             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30555             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30556         }
30557     },
30558     
30559     leave: function(e, el)
30560     {
30561         e.preventDefault();
30562         
30563         if(this.bgimage.length){
30564             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30565             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30566         }
30567     }
30568     
30569 });
30570
30571  
30572
30573  /*
30574  * Based on:
30575  * Ext JS Library 1.1.1
30576  * Copyright(c) 2006-2007, Ext JS, LLC.
30577  *
30578  * Originally Released Under LGPL - original licence link has changed is not relivant.
30579  *
30580  * Fork - LGPL
30581  * <script type="text/javascript">
30582  */
30583
30584
30585 /**
30586  * @class Roo.bootstrap.SplitBar
30587  * @extends Roo.util.Observable
30588  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30589  * <br><br>
30590  * Usage:
30591  * <pre><code>
30592 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30593                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30594 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30595 split.minSize = 100;
30596 split.maxSize = 600;
30597 split.animate = true;
30598 split.on('moved', splitterMoved);
30599 </code></pre>
30600  * @constructor
30601  * Create a new SplitBar
30602  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
30603  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
30604  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30605  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
30606                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30607                         position of the SplitBar).
30608  */
30609 Roo.bootstrap.SplitBar = function(cfg){
30610     
30611     /** @private */
30612     
30613     //{
30614     //  dragElement : elm
30615     //  resizingElement: el,
30616         // optional..
30617     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30618     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
30619         // existingProxy ???
30620     //}
30621     
30622     this.el = Roo.get(cfg.dragElement, true);
30623     this.el.dom.unselectable = "on";
30624     /** @private */
30625     this.resizingEl = Roo.get(cfg.resizingElement, true);
30626
30627     /**
30628      * @private
30629      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30630      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30631      * @type Number
30632      */
30633     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30634     
30635     /**
30636      * The minimum size of the resizing element. (Defaults to 0)
30637      * @type Number
30638      */
30639     this.minSize = 0;
30640     
30641     /**
30642      * The maximum size of the resizing element. (Defaults to 2000)
30643      * @type Number
30644      */
30645     this.maxSize = 2000;
30646     
30647     /**
30648      * Whether to animate the transition to the new size
30649      * @type Boolean
30650      */
30651     this.animate = false;
30652     
30653     /**
30654      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30655      * @type Boolean
30656      */
30657     this.useShim = false;
30658     
30659     /** @private */
30660     this.shim = null;
30661     
30662     if(!cfg.existingProxy){
30663         /** @private */
30664         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30665     }else{
30666         this.proxy = Roo.get(cfg.existingProxy).dom;
30667     }
30668     /** @private */
30669     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30670     
30671     /** @private */
30672     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30673     
30674     /** @private */
30675     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30676     
30677     /** @private */
30678     this.dragSpecs = {};
30679     
30680     /**
30681      * @private The adapter to use to positon and resize elements
30682      */
30683     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30684     this.adapter.init(this);
30685     
30686     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30687         /** @private */
30688         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30689         this.el.addClass("roo-splitbar-h");
30690     }else{
30691         /** @private */
30692         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30693         this.el.addClass("roo-splitbar-v");
30694     }
30695     
30696     this.addEvents({
30697         /**
30698          * @event resize
30699          * Fires when the splitter is moved (alias for {@link #event-moved})
30700          * @param {Roo.bootstrap.SplitBar} this
30701          * @param {Number} newSize the new width or height
30702          */
30703         "resize" : true,
30704         /**
30705          * @event moved
30706          * Fires when the splitter is moved
30707          * @param {Roo.bootstrap.SplitBar} this
30708          * @param {Number} newSize the new width or height
30709          */
30710         "moved" : true,
30711         /**
30712          * @event beforeresize
30713          * Fires before the splitter is dragged
30714          * @param {Roo.bootstrap.SplitBar} this
30715          */
30716         "beforeresize" : true,
30717
30718         "beforeapply" : true
30719     });
30720
30721     Roo.util.Observable.call(this);
30722 };
30723
30724 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30725     onStartProxyDrag : function(x, y){
30726         this.fireEvent("beforeresize", this);
30727         if(!this.overlay){
30728             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
30729             o.unselectable();
30730             o.enableDisplayMode("block");
30731             // all splitbars share the same overlay
30732             Roo.bootstrap.SplitBar.prototype.overlay = o;
30733         }
30734         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30735         this.overlay.show();
30736         Roo.get(this.proxy).setDisplayed("block");
30737         var size = this.adapter.getElementSize(this);
30738         this.activeMinSize = this.getMinimumSize();;
30739         this.activeMaxSize = this.getMaximumSize();;
30740         var c1 = size - this.activeMinSize;
30741         var c2 = Math.max(this.activeMaxSize - size, 0);
30742         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30743             this.dd.resetConstraints();
30744             this.dd.setXConstraint(
30745                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
30746                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30747             );
30748             this.dd.setYConstraint(0, 0);
30749         }else{
30750             this.dd.resetConstraints();
30751             this.dd.setXConstraint(0, 0);
30752             this.dd.setYConstraint(
30753                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
30754                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30755             );
30756          }
30757         this.dragSpecs.startSize = size;
30758         this.dragSpecs.startPoint = [x, y];
30759         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30760     },
30761     
30762     /** 
30763      * @private Called after the drag operation by the DDProxy
30764      */
30765     onEndProxyDrag : function(e){
30766         Roo.get(this.proxy).setDisplayed(false);
30767         var endPoint = Roo.lib.Event.getXY(e);
30768         if(this.overlay){
30769             this.overlay.hide();
30770         }
30771         var newSize;
30772         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30773             newSize = this.dragSpecs.startSize + 
30774                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30775                     endPoint[0] - this.dragSpecs.startPoint[0] :
30776                     this.dragSpecs.startPoint[0] - endPoint[0]
30777                 );
30778         }else{
30779             newSize = this.dragSpecs.startSize + 
30780                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30781                     endPoint[1] - this.dragSpecs.startPoint[1] :
30782                     this.dragSpecs.startPoint[1] - endPoint[1]
30783                 );
30784         }
30785         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30786         if(newSize != this.dragSpecs.startSize){
30787             if(this.fireEvent('beforeapply', this, newSize) !== false){
30788                 this.adapter.setElementSize(this, newSize);
30789                 this.fireEvent("moved", this, newSize);
30790                 this.fireEvent("resize", this, newSize);
30791             }
30792         }
30793     },
30794     
30795     /**
30796      * Get the adapter this SplitBar uses
30797      * @return The adapter object
30798      */
30799     getAdapter : function(){
30800         return this.adapter;
30801     },
30802     
30803     /**
30804      * Set the adapter this SplitBar uses
30805      * @param {Object} adapter A SplitBar adapter object
30806      */
30807     setAdapter : function(adapter){
30808         this.adapter = adapter;
30809         this.adapter.init(this);
30810     },
30811     
30812     /**
30813      * Gets the minimum size for the resizing element
30814      * @return {Number} The minimum size
30815      */
30816     getMinimumSize : function(){
30817         return this.minSize;
30818     },
30819     
30820     /**
30821      * Sets the minimum size for the resizing element
30822      * @param {Number} minSize The minimum size
30823      */
30824     setMinimumSize : function(minSize){
30825         this.minSize = minSize;
30826     },
30827     
30828     /**
30829      * Gets the maximum size for the resizing element
30830      * @return {Number} The maximum size
30831      */
30832     getMaximumSize : function(){
30833         return this.maxSize;
30834     },
30835     
30836     /**
30837      * Sets the maximum size for the resizing element
30838      * @param {Number} maxSize The maximum size
30839      */
30840     setMaximumSize : function(maxSize){
30841         this.maxSize = maxSize;
30842     },
30843     
30844     /**
30845      * Sets the initialize size for the resizing element
30846      * @param {Number} size The initial size
30847      */
30848     setCurrentSize : function(size){
30849         var oldAnimate = this.animate;
30850         this.animate = false;
30851         this.adapter.setElementSize(this, size);
30852         this.animate = oldAnimate;
30853     },
30854     
30855     /**
30856      * Destroy this splitbar. 
30857      * @param {Boolean} removeEl True to remove the element
30858      */
30859     destroy : function(removeEl){
30860         if(this.shim){
30861             this.shim.remove();
30862         }
30863         this.dd.unreg();
30864         this.proxy.parentNode.removeChild(this.proxy);
30865         if(removeEl){
30866             this.el.remove();
30867         }
30868     }
30869 });
30870
30871 /**
30872  * @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.
30873  */
30874 Roo.bootstrap.SplitBar.createProxy = function(dir){
30875     var proxy = new Roo.Element(document.createElement("div"));
30876     proxy.unselectable();
30877     var cls = 'roo-splitbar-proxy';
30878     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
30879     document.body.appendChild(proxy.dom);
30880     return proxy.dom;
30881 };
30882
30883 /** 
30884  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
30885  * Default Adapter. It assumes the splitter and resizing element are not positioned
30886  * elements and only gets/sets the width of the element. Generally used for table based layouts.
30887  */
30888 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
30889 };
30890
30891 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
30892     // do nothing for now
30893     init : function(s){
30894     
30895     },
30896     /**
30897      * Called before drag operations to get the current size of the resizing element. 
30898      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30899      */
30900      getElementSize : function(s){
30901         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30902             return s.resizingEl.getWidth();
30903         }else{
30904             return s.resizingEl.getHeight();
30905         }
30906     },
30907     
30908     /**
30909      * Called after drag operations to set the size of the resizing element.
30910      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30911      * @param {Number} newSize The new size to set
30912      * @param {Function} onComplete A function to be invoked when resizing is complete
30913      */
30914     setElementSize : function(s, newSize, onComplete){
30915         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30916             if(!s.animate){
30917                 s.resizingEl.setWidth(newSize);
30918                 if(onComplete){
30919                     onComplete(s, newSize);
30920                 }
30921             }else{
30922                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
30923             }
30924         }else{
30925             
30926             if(!s.animate){
30927                 s.resizingEl.setHeight(newSize);
30928                 if(onComplete){
30929                     onComplete(s, newSize);
30930                 }
30931             }else{
30932                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
30933             }
30934         }
30935     }
30936 };
30937
30938 /** 
30939  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
30940  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
30941  * Adapter that  moves the splitter element to align with the resized sizing element. 
30942  * Used with an absolute positioned SplitBar.
30943  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
30944  * document.body, make sure you assign an id to the body element.
30945  */
30946 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
30947     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30948     this.container = Roo.get(container);
30949 };
30950
30951 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
30952     init : function(s){
30953         this.basic.init(s);
30954     },
30955     
30956     getElementSize : function(s){
30957         return this.basic.getElementSize(s);
30958     },
30959     
30960     setElementSize : function(s, newSize, onComplete){
30961         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
30962     },
30963     
30964     moveSplitter : function(s){
30965         var yes = Roo.bootstrap.SplitBar;
30966         switch(s.placement){
30967             case yes.LEFT:
30968                 s.el.setX(s.resizingEl.getRight());
30969                 break;
30970             case yes.RIGHT:
30971                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
30972                 break;
30973             case yes.TOP:
30974                 s.el.setY(s.resizingEl.getBottom());
30975                 break;
30976             case yes.BOTTOM:
30977                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
30978                 break;
30979         }
30980     }
30981 };
30982
30983 /**
30984  * Orientation constant - Create a vertical SplitBar
30985  * @static
30986  * @type Number
30987  */
30988 Roo.bootstrap.SplitBar.VERTICAL = 1;
30989
30990 /**
30991  * Orientation constant - Create a horizontal SplitBar
30992  * @static
30993  * @type Number
30994  */
30995 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
30996
30997 /**
30998  * Placement constant - The resizing element is to the left of the splitter element
30999  * @static
31000  * @type Number
31001  */
31002 Roo.bootstrap.SplitBar.LEFT = 1;
31003
31004 /**
31005  * Placement constant - The resizing element is to the right of the splitter element
31006  * @static
31007  * @type Number
31008  */
31009 Roo.bootstrap.SplitBar.RIGHT = 2;
31010
31011 /**
31012  * Placement constant - The resizing element is positioned above the splitter element
31013  * @static
31014  * @type Number
31015  */
31016 Roo.bootstrap.SplitBar.TOP = 3;
31017
31018 /**
31019  * Placement constant - The resizing element is positioned under splitter element
31020  * @static
31021  * @type Number
31022  */
31023 Roo.bootstrap.SplitBar.BOTTOM = 4;
31024 Roo.namespace("Roo.bootstrap.layout");/*
31025  * Based on:
31026  * Ext JS Library 1.1.1
31027  * Copyright(c) 2006-2007, Ext JS, LLC.
31028  *
31029  * Originally Released Under LGPL - original licence link has changed is not relivant.
31030  *
31031  * Fork - LGPL
31032  * <script type="text/javascript">
31033  */
31034  
31035 /**
31036  * @class Roo.bootstrap.layout.Manager
31037  * @extends Roo.bootstrap.Component
31038  * Base class for layout managers.
31039  */
31040 Roo.bootstrap.layout.Manager = function(config)
31041 {
31042     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31043     this.el = Roo.get(config.el);
31044     // ie scrollbar fix
31045     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31046         document.body.scroll = "no";
31047     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31048         this.el.position('relative');
31049     }
31050     
31051     this.id = this.el.id;
31052     this.el.addClass("roo-layout-container");
31053     /** false to disable window resize monitoring @type Boolean */
31054     this.monitorWindowResize = true;
31055     this.regions = {};
31056     this.addEvents({
31057         /**
31058          * @event layout
31059          * Fires when a layout is performed. 
31060          * @param {Roo.LayoutManager} this
31061          */
31062         "layout" : true,
31063         /**
31064          * @event regionresized
31065          * Fires when the user resizes a region. 
31066          * @param {Roo.LayoutRegion} region The resized region
31067          * @param {Number} newSize The new size (width for east/west, height for north/south)
31068          */
31069         "regionresized" : true,
31070         /**
31071          * @event regioncollapsed
31072          * Fires when a region is collapsed. 
31073          * @param {Roo.LayoutRegion} region The collapsed region
31074          */
31075         "regioncollapsed" : true,
31076         /**
31077          * @event regionexpanded
31078          * Fires when a region is expanded.  
31079          * @param {Roo.LayoutRegion} region The expanded region
31080          */
31081         "regionexpanded" : true
31082     });
31083     this.updating = false;
31084     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31085 };
31086
31087 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31088     
31089     
31090     regions : null,
31091     
31092     monitorWindowResize : true,
31093     
31094     
31095     updating : false,
31096     
31097     /**
31098      * Returns true if this layout is currently being updated
31099      * @return {Boolean}
31100      */
31101     isUpdating : function(){
31102         return this.updating; 
31103     },
31104     
31105     /**
31106      * Suspend the LayoutManager from doing auto-layouts while
31107      * making multiple add or remove calls
31108      */
31109     beginUpdate : function(){
31110         this.updating = true;    
31111     },
31112     
31113     /**
31114      * Restore auto-layouts and optionally disable the manager from performing a layout
31115      * @param {Boolean} noLayout true to disable a layout update 
31116      */
31117     endUpdate : function(noLayout){
31118         this.updating = false;
31119         if(!noLayout){
31120             this.layout();
31121         }    
31122     },
31123     
31124     layout: function(){
31125         // abstract...
31126     },
31127     
31128     onRegionResized : function(region, newSize){
31129         this.fireEvent("regionresized", region, newSize);
31130         this.layout();
31131     },
31132     
31133     onRegionCollapsed : function(region){
31134         this.fireEvent("regioncollapsed", region);
31135     },
31136     
31137     onRegionExpanded : function(region){
31138         this.fireEvent("regionexpanded", region);
31139     },
31140         
31141     /**
31142      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31143      * performs box-model adjustments.
31144      * @return {Object} The size as an object {width: (the width), height: (the height)}
31145      */
31146     getViewSize : function()
31147     {
31148         var size;
31149         if(this.el.dom != document.body){
31150             size = this.el.getSize();
31151         }else{
31152             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31153         }
31154         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31155         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31156         return size;
31157     },
31158     
31159     /**
31160      * Returns the Element this layout is bound to.
31161      * @return {Roo.Element}
31162      */
31163     getEl : function(){
31164         return this.el;
31165     },
31166     
31167     /**
31168      * Returns the specified region.
31169      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31170      * @return {Roo.LayoutRegion}
31171      */
31172     getRegion : function(target){
31173         return this.regions[target.toLowerCase()];
31174     },
31175     
31176     onWindowResize : function(){
31177         if(this.monitorWindowResize){
31178             this.layout();
31179         }
31180     }
31181 });/*
31182  * Based on:
31183  * Ext JS Library 1.1.1
31184  * Copyright(c) 2006-2007, Ext JS, LLC.
31185  *
31186  * Originally Released Under LGPL - original licence link has changed is not relivant.
31187  *
31188  * Fork - LGPL
31189  * <script type="text/javascript">
31190  */
31191 /**
31192  * @class Roo.bootstrap.layout.Border
31193  * @extends Roo.bootstrap.layout.Manager
31194  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31195  * please see: examples/bootstrap/nested.html<br><br>
31196  
31197 <b>The container the layout is rendered into can be either the body element or any other element.
31198 If it is not the body element, the container needs to either be an absolute positioned element,
31199 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31200 the container size if it is not the body element.</b>
31201
31202 * @constructor
31203 * Create a new Border
31204 * @param {Object} config Configuration options
31205  */
31206 Roo.bootstrap.layout.Border = function(config){
31207     config = config || {};
31208     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31209     
31210     
31211     
31212     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31213         if(config[region]){
31214             config[region].region = region;
31215             this.addRegion(config[region]);
31216         }
31217     },this);
31218     
31219 };
31220
31221 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31222
31223 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31224     /**
31225      * Creates and adds a new region if it doesn't already exist.
31226      * @param {String} target The target region key (north, south, east, west or center).
31227      * @param {Object} config The regions config object
31228      * @return {BorderLayoutRegion} The new region
31229      */
31230     addRegion : function(config)
31231     {
31232         if(!this.regions[config.region]){
31233             var r = this.factory(config);
31234             this.bindRegion(r);
31235         }
31236         return this.regions[config.region];
31237     },
31238
31239     // private (kinda)
31240     bindRegion : function(r){
31241         this.regions[r.config.region] = r;
31242         
31243         r.on("visibilitychange",    this.layout, this);
31244         r.on("paneladded",          this.layout, this);
31245         r.on("panelremoved",        this.layout, this);
31246         r.on("invalidated",         this.layout, this);
31247         r.on("resized",             this.onRegionResized, this);
31248         r.on("collapsed",           this.onRegionCollapsed, this);
31249         r.on("expanded",            this.onRegionExpanded, this);
31250     },
31251
31252     /**
31253      * Performs a layout update.
31254      */
31255     layout : function()
31256     {
31257         if(this.updating) {
31258             return;
31259         }
31260         var size = this.getViewSize();
31261         var w = size.width;
31262         var h = size.height;
31263         var centerW = w;
31264         var centerH = h;
31265         var centerY = 0;
31266         var centerX = 0;
31267         //var x = 0, y = 0;
31268
31269         var rs = this.regions;
31270         var north = rs["north"];
31271         var south = rs["south"]; 
31272         var west = rs["west"];
31273         var east = rs["east"];
31274         var center = rs["center"];
31275         //if(this.hideOnLayout){ // not supported anymore
31276             //c.el.setStyle("display", "none");
31277         //}
31278         if(north && north.isVisible()){
31279             var b = north.getBox();
31280             var m = north.getMargins();
31281             b.width = w - (m.left+m.right);
31282             b.x = m.left;
31283             b.y = m.top;
31284             centerY = b.height + b.y + m.bottom;
31285             centerH -= centerY;
31286             north.updateBox(this.safeBox(b));
31287         }
31288         if(south && south.isVisible()){
31289             var b = south.getBox();
31290             var m = south.getMargins();
31291             b.width = w - (m.left+m.right);
31292             b.x = m.left;
31293             var totalHeight = (b.height + m.top + m.bottom);
31294             b.y = h - totalHeight + m.top;
31295             centerH -= totalHeight;
31296             south.updateBox(this.safeBox(b));
31297         }
31298         if(west && west.isVisible()){
31299             var b = west.getBox();
31300             var m = west.getMargins();
31301             b.height = centerH - (m.top+m.bottom);
31302             b.x = m.left;
31303             b.y = centerY + m.top;
31304             var totalWidth = (b.width + m.left + m.right);
31305             centerX += totalWidth;
31306             centerW -= totalWidth;
31307             west.updateBox(this.safeBox(b));
31308         }
31309         if(east && east.isVisible()){
31310             var b = east.getBox();
31311             var m = east.getMargins();
31312             b.height = centerH - (m.top+m.bottom);
31313             var totalWidth = (b.width + m.left + m.right);
31314             b.x = w - totalWidth + m.left;
31315             b.y = centerY + m.top;
31316             centerW -= totalWidth;
31317             east.updateBox(this.safeBox(b));
31318         }
31319         if(center){
31320             var m = center.getMargins();
31321             var centerBox = {
31322                 x: centerX + m.left,
31323                 y: centerY + m.top,
31324                 width: centerW - (m.left+m.right),
31325                 height: centerH - (m.top+m.bottom)
31326             };
31327             //if(this.hideOnLayout){
31328                 //center.el.setStyle("display", "block");
31329             //}
31330             center.updateBox(this.safeBox(centerBox));
31331         }
31332         this.el.repaint();
31333         this.fireEvent("layout", this);
31334     },
31335
31336     // private
31337     safeBox : function(box){
31338         box.width = Math.max(0, box.width);
31339         box.height = Math.max(0, box.height);
31340         return box;
31341     },
31342
31343     /**
31344      * Adds a ContentPanel (or subclass) to this layout.
31345      * @param {String} target The target region key (north, south, east, west or center).
31346      * @param {Roo.ContentPanel} panel The panel to add
31347      * @return {Roo.ContentPanel} The added panel
31348      */
31349     add : function(target, panel){
31350          
31351         target = target.toLowerCase();
31352         return this.regions[target].add(panel);
31353     },
31354
31355     /**
31356      * Remove a ContentPanel (or subclass) to this layout.
31357      * @param {String} target The target region key (north, south, east, west or center).
31358      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31359      * @return {Roo.ContentPanel} The removed panel
31360      */
31361     remove : function(target, panel){
31362         target = target.toLowerCase();
31363         return this.regions[target].remove(panel);
31364     },
31365
31366     /**
31367      * Searches all regions for a panel with the specified id
31368      * @param {String} panelId
31369      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31370      */
31371     findPanel : function(panelId){
31372         var rs = this.regions;
31373         for(var target in rs){
31374             if(typeof rs[target] != "function"){
31375                 var p = rs[target].getPanel(panelId);
31376                 if(p){
31377                     return p;
31378                 }
31379             }
31380         }
31381         return null;
31382     },
31383
31384     /**
31385      * Searches all regions for a panel with the specified id and activates (shows) it.
31386      * @param {String/ContentPanel} panelId The panels id or the panel itself
31387      * @return {Roo.ContentPanel} The shown panel or null
31388      */
31389     showPanel : function(panelId) {
31390       var rs = this.regions;
31391       for(var target in rs){
31392          var r = rs[target];
31393          if(typeof r != "function"){
31394             if(r.hasPanel(panelId)){
31395                return r.showPanel(panelId);
31396             }
31397          }
31398       }
31399       return null;
31400    },
31401
31402    /**
31403      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31404      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31405      */
31406    /*
31407     restoreState : function(provider){
31408         if(!provider){
31409             provider = Roo.state.Manager;
31410         }
31411         var sm = new Roo.LayoutStateManager();
31412         sm.init(this, provider);
31413     },
31414 */
31415  
31416  
31417     /**
31418      * Adds a xtype elements to the layout.
31419      * <pre><code>
31420
31421 layout.addxtype({
31422        xtype : 'ContentPanel',
31423        region: 'west',
31424        items: [ .... ]
31425    }
31426 );
31427
31428 layout.addxtype({
31429         xtype : 'NestedLayoutPanel',
31430         region: 'west',
31431         layout: {
31432            center: { },
31433            west: { }   
31434         },
31435         items : [ ... list of content panels or nested layout panels.. ]
31436    }
31437 );
31438 </code></pre>
31439      * @param {Object} cfg Xtype definition of item to add.
31440      */
31441     addxtype : function(cfg)
31442     {
31443         // basically accepts a pannel...
31444         // can accept a layout region..!?!?
31445         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31446         
31447         
31448         // theory?  children can only be panels??
31449         
31450         //if (!cfg.xtype.match(/Panel$/)) {
31451         //    return false;
31452         //}
31453         var ret = false;
31454         
31455         if (typeof(cfg.region) == 'undefined') {
31456             Roo.log("Failed to add Panel, region was not set");
31457             Roo.log(cfg);
31458             return false;
31459         }
31460         var region = cfg.region;
31461         delete cfg.region;
31462         
31463           
31464         var xitems = [];
31465         if (cfg.items) {
31466             xitems = cfg.items;
31467             delete cfg.items;
31468         }
31469         var nb = false;
31470         
31471         switch(cfg.xtype) 
31472         {
31473             case 'Content':  // ContentPanel (el, cfg)
31474             case 'Scroll':  // ContentPanel (el, cfg)
31475             case 'View': 
31476                 cfg.autoCreate = true;
31477                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31478                 //} else {
31479                 //    var el = this.el.createChild();
31480                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31481                 //}
31482                 
31483                 this.add(region, ret);
31484                 break;
31485             
31486             /*
31487             case 'TreePanel': // our new panel!
31488                 cfg.el = this.el.createChild();
31489                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31490                 this.add(region, ret);
31491                 break;
31492             */
31493             
31494             case 'Nest': 
31495                 // create a new Layout (which is  a Border Layout...
31496                 
31497                 var clayout = cfg.layout;
31498                 clayout.el  = this.el.createChild();
31499                 clayout.items   = clayout.items  || [];
31500                 
31501                 delete cfg.layout;
31502                 
31503                 // replace this exitems with the clayout ones..
31504                 xitems = clayout.items;
31505                  
31506                 // force background off if it's in center...
31507                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31508                     cfg.background = false;
31509                 }
31510                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31511                 
31512                 
31513                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31514                 //console.log('adding nested layout panel '  + cfg.toSource());
31515                 this.add(region, ret);
31516                 nb = {}; /// find first...
31517                 break;
31518             
31519             case 'Grid':
31520                 
31521                 // needs grid and region
31522                 
31523                 //var el = this.getRegion(region).el.createChild();
31524                 var el = this.el.createChild();
31525                 // create the grid first...
31526                 cfg.grid.container = el;
31527                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31528                 
31529                 
31530                 if (region == 'center' && this.active ) {
31531                     cfg.background = false;
31532                 }
31533                 
31534                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31535                 
31536                 this.add(region, ret);
31537                 
31538                 if (cfg.background) {
31539                     // render grid on panel activation (if panel background)
31540                     ret.on('activate', function(gp) {
31541                         if (!gp.grid.rendered) {
31542                             gp.grid.render(gp.grid.getGridEl());
31543                         }
31544                     });
31545                 } else {
31546                     cfg.grid.render(cfg.grid.getGridEl());
31547                 }
31548                 break;
31549            
31550            
31551             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31552                 // it was the old xcomponent building that caused this before.
31553                 // espeically if border is the top element in the tree.
31554                 ret = this;
31555                 break; 
31556                 
31557                     
31558                 
31559                 
31560                 
31561             default:
31562                 /*
31563                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31564                     
31565                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31566                     this.add(region, ret);
31567                 } else {
31568                 */
31569                     Roo.log(cfg);
31570                     throw "Can not add '" + cfg.xtype + "' to Border";
31571                     return null;
31572              
31573                                 
31574              
31575         }
31576         this.beginUpdate();
31577         // add children..
31578         var region = '';
31579         var abn = {};
31580         Roo.each(xitems, function(i)  {
31581             region = nb && i.region ? i.region : false;
31582             
31583             var add = ret.addxtype(i);
31584            
31585             if (region) {
31586                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31587                 if (!i.background) {
31588                     abn[region] = nb[region] ;
31589                 }
31590             }
31591             
31592         });
31593         this.endUpdate();
31594
31595         // make the last non-background panel active..
31596         //if (nb) { Roo.log(abn); }
31597         if (nb) {
31598             
31599             for(var r in abn) {
31600                 region = this.getRegion(r);
31601                 if (region) {
31602                     // tried using nb[r], but it does not work..
31603                      
31604                     region.showPanel(abn[r]);
31605                    
31606                 }
31607             }
31608         }
31609         return ret;
31610         
31611     },
31612     
31613     
31614 // private
31615     factory : function(cfg)
31616     {
31617         
31618         var validRegions = Roo.bootstrap.layout.Border.regions;
31619
31620         var target = cfg.region;
31621         cfg.mgr = this;
31622         
31623         var r = Roo.bootstrap.layout;
31624         Roo.log(target);
31625         switch(target){
31626             case "north":
31627                 return new r.North(cfg);
31628             case "south":
31629                 return new r.South(cfg);
31630             case "east":
31631                 return new r.East(cfg);
31632             case "west":
31633                 return new r.West(cfg);
31634             case "center":
31635                 return new r.Center(cfg);
31636         }
31637         throw 'Layout region "'+target+'" not supported.';
31638     }
31639     
31640     
31641 });
31642  /*
31643  * Based on:
31644  * Ext JS Library 1.1.1
31645  * Copyright(c) 2006-2007, Ext JS, LLC.
31646  *
31647  * Originally Released Under LGPL - original licence link has changed is not relivant.
31648  *
31649  * Fork - LGPL
31650  * <script type="text/javascript">
31651  */
31652  
31653 /**
31654  * @class Roo.bootstrap.layout.Basic
31655  * @extends Roo.util.Observable
31656  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31657  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31658  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31659  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31660  * @cfg {string}   region  the region that it inhabits..
31661  * @cfg {bool}   skipConfig skip config?
31662  * 
31663
31664  */
31665 Roo.bootstrap.layout.Basic = function(config){
31666     
31667     this.mgr = config.mgr;
31668     
31669     this.position = config.region;
31670     
31671     var skipConfig = config.skipConfig;
31672     
31673     this.events = {
31674         /**
31675          * @scope Roo.BasicLayoutRegion
31676          */
31677         
31678         /**
31679          * @event beforeremove
31680          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31681          * @param {Roo.LayoutRegion} this
31682          * @param {Roo.ContentPanel} panel The panel
31683          * @param {Object} e The cancel event object
31684          */
31685         "beforeremove" : true,
31686         /**
31687          * @event invalidated
31688          * Fires when the layout for this region is changed.
31689          * @param {Roo.LayoutRegion} this
31690          */
31691         "invalidated" : true,
31692         /**
31693          * @event visibilitychange
31694          * Fires when this region is shown or hidden 
31695          * @param {Roo.LayoutRegion} this
31696          * @param {Boolean} visibility true or false
31697          */
31698         "visibilitychange" : true,
31699         /**
31700          * @event paneladded
31701          * Fires when a panel is added. 
31702          * @param {Roo.LayoutRegion} this
31703          * @param {Roo.ContentPanel} panel The panel
31704          */
31705         "paneladded" : true,
31706         /**
31707          * @event panelremoved
31708          * Fires when a panel is removed. 
31709          * @param {Roo.LayoutRegion} this
31710          * @param {Roo.ContentPanel} panel The panel
31711          */
31712         "panelremoved" : true,
31713         /**
31714          * @event beforecollapse
31715          * Fires when this region before collapse.
31716          * @param {Roo.LayoutRegion} this
31717          */
31718         "beforecollapse" : true,
31719         /**
31720          * @event collapsed
31721          * Fires when this region is collapsed.
31722          * @param {Roo.LayoutRegion} this
31723          */
31724         "collapsed" : true,
31725         /**
31726          * @event expanded
31727          * Fires when this region is expanded.
31728          * @param {Roo.LayoutRegion} this
31729          */
31730         "expanded" : true,
31731         /**
31732          * @event slideshow
31733          * Fires when this region is slid into view.
31734          * @param {Roo.LayoutRegion} this
31735          */
31736         "slideshow" : true,
31737         /**
31738          * @event slidehide
31739          * Fires when this region slides out of view. 
31740          * @param {Roo.LayoutRegion} this
31741          */
31742         "slidehide" : true,
31743         /**
31744          * @event panelactivated
31745          * Fires when a panel is activated. 
31746          * @param {Roo.LayoutRegion} this
31747          * @param {Roo.ContentPanel} panel The activated panel
31748          */
31749         "panelactivated" : true,
31750         /**
31751          * @event resized
31752          * Fires when the user resizes this region. 
31753          * @param {Roo.LayoutRegion} this
31754          * @param {Number} newSize The new size (width for east/west, height for north/south)
31755          */
31756         "resized" : true
31757     };
31758     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31759     this.panels = new Roo.util.MixedCollection();
31760     this.panels.getKey = this.getPanelId.createDelegate(this);
31761     this.box = null;
31762     this.activePanel = null;
31763     // ensure listeners are added...
31764     
31765     if (config.listeners || config.events) {
31766         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31767             listeners : config.listeners || {},
31768             events : config.events || {}
31769         });
31770     }
31771     
31772     if(skipConfig !== true){
31773         this.applyConfig(config);
31774     }
31775 };
31776
31777 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31778 {
31779     getPanelId : function(p){
31780         return p.getId();
31781     },
31782     
31783     applyConfig : function(config){
31784         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31785         this.config = config;
31786         
31787     },
31788     
31789     /**
31790      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31791      * the width, for horizontal (north, south) the height.
31792      * @param {Number} newSize The new width or height
31793      */
31794     resizeTo : function(newSize){
31795         var el = this.el ? this.el :
31796                  (this.activePanel ? this.activePanel.getEl() : null);
31797         if(el){
31798             switch(this.position){
31799                 case "east":
31800                 case "west":
31801                     el.setWidth(newSize);
31802                     this.fireEvent("resized", this, newSize);
31803                 break;
31804                 case "north":
31805                 case "south":
31806                     el.setHeight(newSize);
31807                     this.fireEvent("resized", this, newSize);
31808                 break;                
31809             }
31810         }
31811     },
31812     
31813     getBox : function(){
31814         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31815     },
31816     
31817     getMargins : function(){
31818         return this.margins;
31819     },
31820     
31821     updateBox : function(box){
31822         this.box = box;
31823         var el = this.activePanel.getEl();
31824         el.dom.style.left = box.x + "px";
31825         el.dom.style.top = box.y + "px";
31826         this.activePanel.setSize(box.width, box.height);
31827     },
31828     
31829     /**
31830      * Returns the container element for this region.
31831      * @return {Roo.Element}
31832      */
31833     getEl : function(){
31834         return this.activePanel;
31835     },
31836     
31837     /**
31838      * Returns true if this region is currently visible.
31839      * @return {Boolean}
31840      */
31841     isVisible : function(){
31842         return this.activePanel ? true : false;
31843     },
31844     
31845     setActivePanel : function(panel){
31846         panel = this.getPanel(panel);
31847         if(this.activePanel && this.activePanel != panel){
31848             this.activePanel.setActiveState(false);
31849             this.activePanel.getEl().setLeftTop(-10000,-10000);
31850         }
31851         this.activePanel = panel;
31852         panel.setActiveState(true);
31853         if(this.box){
31854             panel.setSize(this.box.width, this.box.height);
31855         }
31856         this.fireEvent("panelactivated", this, panel);
31857         this.fireEvent("invalidated");
31858     },
31859     
31860     /**
31861      * Show the specified panel.
31862      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31863      * @return {Roo.ContentPanel} The shown panel or null
31864      */
31865     showPanel : function(panel){
31866         panel = this.getPanel(panel);
31867         if(panel){
31868             this.setActivePanel(panel);
31869         }
31870         return panel;
31871     },
31872     
31873     /**
31874      * Get the active panel for this region.
31875      * @return {Roo.ContentPanel} The active panel or null
31876      */
31877     getActivePanel : function(){
31878         return this.activePanel;
31879     },
31880     
31881     /**
31882      * Add the passed ContentPanel(s)
31883      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31884      * @return {Roo.ContentPanel} The panel added (if only one was added)
31885      */
31886     add : function(panel){
31887         if(arguments.length > 1){
31888             for(var i = 0, len = arguments.length; i < len; i++) {
31889                 this.add(arguments[i]);
31890             }
31891             return null;
31892         }
31893         if(this.hasPanel(panel)){
31894             this.showPanel(panel);
31895             return panel;
31896         }
31897         var el = panel.getEl();
31898         if(el.dom.parentNode != this.mgr.el.dom){
31899             this.mgr.el.dom.appendChild(el.dom);
31900         }
31901         if(panel.setRegion){
31902             panel.setRegion(this);
31903         }
31904         this.panels.add(panel);
31905         el.setStyle("position", "absolute");
31906         if(!panel.background){
31907             this.setActivePanel(panel);
31908             if(this.config.initialSize && this.panels.getCount()==1){
31909                 this.resizeTo(this.config.initialSize);
31910             }
31911         }
31912         this.fireEvent("paneladded", this, panel);
31913         return panel;
31914     },
31915     
31916     /**
31917      * Returns true if the panel is in this region.
31918      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31919      * @return {Boolean}
31920      */
31921     hasPanel : function(panel){
31922         if(typeof panel == "object"){ // must be panel obj
31923             panel = panel.getId();
31924         }
31925         return this.getPanel(panel) ? true : false;
31926     },
31927     
31928     /**
31929      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31930      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31931      * @param {Boolean} preservePanel Overrides the config preservePanel option
31932      * @return {Roo.ContentPanel} The panel that was removed
31933      */
31934     remove : function(panel, preservePanel){
31935         panel = this.getPanel(panel);
31936         if(!panel){
31937             return null;
31938         }
31939         var e = {};
31940         this.fireEvent("beforeremove", this, panel, e);
31941         if(e.cancel === true){
31942             return null;
31943         }
31944         var panelId = panel.getId();
31945         this.panels.removeKey(panelId);
31946         return panel;
31947     },
31948     
31949     /**
31950      * Returns the panel specified or null if it's not in this region.
31951      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31952      * @return {Roo.ContentPanel}
31953      */
31954     getPanel : function(id){
31955         if(typeof id == "object"){ // must be panel obj
31956             return id;
31957         }
31958         return this.panels.get(id);
31959     },
31960     
31961     /**
31962      * Returns this regions position (north/south/east/west/center).
31963      * @return {String} 
31964      */
31965     getPosition: function(){
31966         return this.position;    
31967     }
31968 });/*
31969  * Based on:
31970  * Ext JS Library 1.1.1
31971  * Copyright(c) 2006-2007, Ext JS, LLC.
31972  *
31973  * Originally Released Under LGPL - original licence link has changed is not relivant.
31974  *
31975  * Fork - LGPL
31976  * <script type="text/javascript">
31977  */
31978  
31979 /**
31980  * @class Roo.bootstrap.layout.Region
31981  * @extends Roo.bootstrap.layout.Basic
31982  * This class represents a region in a layout manager.
31983  
31984  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31985  * @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})
31986  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
31987  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31988  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31989  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31990  * @cfg {String}    title           The title for the region (overrides panel titles)
31991  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31992  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31993  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31994  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31995  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31996  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31997  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31998  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31999  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32000  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32001
32002  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32003  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32004  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32005  * @cfg {Number}    width           For East/West panels
32006  * @cfg {Number}    height          For North/South panels
32007  * @cfg {Boolean}   split           To show the splitter
32008  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32009  * 
32010  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32011  * @cfg {string}   region  the region that it inhabits..
32012  *
32013
32014  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32015  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32016
32017  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32018  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32019  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32020  */
32021 Roo.bootstrap.layout.Region = function(config)
32022 {
32023     
32024     var mgr = config.mgr;
32025     var pos = config.region;
32026     config.skipConfig = true;
32027     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32028     var dh = Roo.DomHelper;
32029     /** This region's container element 
32030     * @type Roo.Element */
32031     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "roo-layout-region roo-layout-panel roo-layout-panel-" + this.position}, true);
32032     /** This region's title element 
32033     * @type Roo.Element */
32034
32035     this.titleEl = dh.append(this.el.dom,
32036         {
32037                 tag: "div",
32038                 unselectable: "on",
32039                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32040                 children:[
32041                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32042                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32043                 ]}, true);
32044     
32045     this.titleEl.enableDisplayMode();
32046     /** This region's title text element 
32047     * @type HTMLElement */
32048     this.titleTextEl = this.titleEl.dom.firstChild;
32049     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32050     /*
32051     this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32052     this.closeBtn.enableDisplayMode();
32053     this.closeBtn.on("click", this.closeClicked, this);
32054     this.closeBtn.hide();
32055 */
32056     this.createBody(config);
32057     this.visible = true;
32058     this.collapsed = false;
32059
32060     if(config.hideWhenEmpty){
32061         this.hide();
32062         this.on("paneladded", this.validateVisibility, this);
32063         this.on("panelremoved", this.validateVisibility, this);
32064     }
32065     this.applyConfig(config);
32066 };
32067
32068 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32069
32070
32071
32072     createBody : function(){
32073         /** This region's body element 
32074         * @type Roo.Element */
32075         this.bodyEl = this.el.createChild({
32076                 tag: "div",
32077                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32078         });
32079     },
32080
32081     applyConfig : function(c)
32082     {
32083         /*
32084          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32085             var dh = Roo.DomHelper;
32086             if(c.titlebar !== false){
32087                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32088                 this.collapseBtn.on("click", this.collapse, this);
32089                 this.collapseBtn.enableDisplayMode();
32090                 /*
32091                 if(c.showPin === true || this.showPin){
32092                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32093                     this.stickBtn.enableDisplayMode();
32094                     this.stickBtn.on("click", this.expand, this);
32095                     this.stickBtn.hide();
32096                 }
32097                 
32098             }
32099             */
32100             /** This region's collapsed element
32101             * @type Roo.Element */
32102             /*
32103              *
32104             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32105                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32106             ]}, true);
32107             
32108             if(c.floatable !== false){
32109                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32110                this.collapsedEl.on("click", this.collapseClick, this);
32111             }
32112
32113             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32114                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32115                    id: "message", unselectable: "on", style:{"float":"left"}});
32116                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32117              }
32118             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32119             this.expandBtn.on("click", this.expand, this);
32120             
32121         }
32122         
32123         if(this.collapseBtn){
32124             this.collapseBtn.setVisible(c.collapsible == true);
32125         }
32126         
32127         this.cmargins = c.cmargins || this.cmargins ||
32128                          (this.position == "west" || this.position == "east" ?
32129                              {top: 0, left: 2, right:2, bottom: 0} :
32130                              {top: 2, left: 0, right:0, bottom: 2});
32131         */
32132         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32133         
32134         
32135         this.bottomTabs = c.tabPosition != "top";
32136         
32137         this.autoScroll = c.autoScroll || false;
32138         
32139         
32140         if(this.autoScroll){
32141             this.bodyEl.setStyle("overflow", "auto");
32142         }else{
32143             this.bodyEl.setStyle("overflow", c.overflow || 'hidden');
32144         }
32145         //if(c.titlebar !== false){
32146             if((!c.titlebar && !c.title) || c.titlebar === false){
32147                 this.titleEl.hide();
32148             }else{
32149                 this.titleEl.show();
32150                 if(c.title){
32151                     this.titleTextEl.innerHTML = c.title;
32152                 }
32153             }
32154         //}
32155         this.duration = c.duration || .30;
32156         this.slideDuration = c.slideDuration || .45;
32157         this.config = c;
32158         if(c.collapsed){
32159             this.collapse(true);
32160         }
32161         if(c.hidden){
32162             this.hide();
32163         }
32164     },
32165     /**
32166      * Returns true if this region is currently visible.
32167      * @return {Boolean}
32168      */
32169     isVisible : function(){
32170         return this.visible;
32171     },
32172
32173     /**
32174      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32175      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32176      */
32177     //setCollapsedTitle : function(title){
32178     //    title = title || "&#160;";
32179      //   if(this.collapsedTitleTextEl){
32180       //      this.collapsedTitleTextEl.innerHTML = title;
32181        // }
32182     //},
32183
32184     getBox : function(){
32185         var b;
32186       //  if(!this.collapsed){
32187             b = this.el.getBox(false, true);
32188        // }else{
32189           //  b = this.collapsedEl.getBox(false, true);
32190         //}
32191         return b;
32192     },
32193
32194     getMargins : function(){
32195         return this.margins;
32196         //return this.collapsed ? this.cmargins : this.margins;
32197     },
32198 /*
32199     highlight : function(){
32200         this.el.addClass("x-layout-panel-dragover");
32201     },
32202
32203     unhighlight : function(){
32204         this.el.removeClass("x-layout-panel-dragover");
32205     },
32206 */
32207     updateBox : function(box)
32208     {
32209         this.box = box;
32210         if(!this.collapsed){
32211             this.el.dom.style.left = box.x + "px";
32212             this.el.dom.style.top = box.y + "px";
32213             this.updateBody(box.width, box.height);
32214         }else{
32215             this.collapsedEl.dom.style.left = box.x + "px";
32216             this.collapsedEl.dom.style.top = box.y + "px";
32217             this.collapsedEl.setSize(box.width, box.height);
32218         }
32219         if(this.tabs){
32220             this.tabs.autoSizeTabs();
32221         }
32222     },
32223
32224     updateBody : function(w, h)
32225     {
32226         if(w !== null){
32227             this.el.setWidth(w);
32228             w -= this.el.getBorderWidth("rl");
32229             if(this.config.adjustments){
32230                 w += this.config.adjustments[0];
32231             }
32232         }
32233         if(h !== null){
32234             this.el.setHeight(h);
32235             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32236             h -= this.el.getBorderWidth("tb");
32237             if(this.config.adjustments){
32238                 h += this.config.adjustments[1];
32239             }
32240             this.bodyEl.setHeight(h);
32241             if(this.tabs){
32242                 h = this.tabs.syncHeight(h);
32243             }
32244         }
32245         if(this.panelSize){
32246             w = w !== null ? w : this.panelSize.width;
32247             h = h !== null ? h : this.panelSize.height;
32248         }
32249         if(this.activePanel){
32250             var el = this.activePanel.getEl();
32251             w = w !== null ? w : el.getWidth();
32252             h = h !== null ? h : el.getHeight();
32253             this.panelSize = {width: w, height: h};
32254             this.activePanel.setSize(w, h);
32255         }
32256         if(Roo.isIE && this.tabs){
32257             this.tabs.el.repaint();
32258         }
32259     },
32260
32261     /**
32262      * Returns the container element for this region.
32263      * @return {Roo.Element}
32264      */
32265     getEl : function(){
32266         return this.el;
32267     },
32268
32269     /**
32270      * Hides this region.
32271      */
32272     hide : function(){
32273         //if(!this.collapsed){
32274             this.el.dom.style.left = "-2000px";
32275             this.el.hide();
32276         //}else{
32277          //   this.collapsedEl.dom.style.left = "-2000px";
32278          //   this.collapsedEl.hide();
32279        // }
32280         this.visible = false;
32281         this.fireEvent("visibilitychange", this, false);
32282     },
32283
32284     /**
32285      * Shows this region if it was previously hidden.
32286      */
32287     show : function(){
32288         //if(!this.collapsed){
32289             this.el.show();
32290         //}else{
32291         //    this.collapsedEl.show();
32292        // }
32293         this.visible = true;
32294         this.fireEvent("visibilitychange", this, true);
32295     },
32296 /*
32297     closeClicked : function(){
32298         if(this.activePanel){
32299             this.remove(this.activePanel);
32300         }
32301     },
32302
32303     collapseClick : function(e){
32304         if(this.isSlid){
32305            e.stopPropagation();
32306            this.slideIn();
32307         }else{
32308            e.stopPropagation();
32309            this.slideOut();
32310         }
32311     },
32312 */
32313     /**
32314      * Collapses this region.
32315      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32316      */
32317     /*
32318     collapse : function(skipAnim, skipCheck = false){
32319         if(this.collapsed) {
32320             return;
32321         }
32322         
32323         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32324             
32325             this.collapsed = true;
32326             if(this.split){
32327                 this.split.el.hide();
32328             }
32329             if(this.config.animate && skipAnim !== true){
32330                 this.fireEvent("invalidated", this);
32331                 this.animateCollapse();
32332             }else{
32333                 this.el.setLocation(-20000,-20000);
32334                 this.el.hide();
32335                 this.collapsedEl.show();
32336                 this.fireEvent("collapsed", this);
32337                 this.fireEvent("invalidated", this);
32338             }
32339         }
32340         
32341     },
32342 */
32343     animateCollapse : function(){
32344         // overridden
32345     },
32346
32347     /**
32348      * Expands this region if it was previously collapsed.
32349      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32350      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32351      */
32352     /*
32353     expand : function(e, skipAnim){
32354         if(e) {
32355             e.stopPropagation();
32356         }
32357         if(!this.collapsed || this.el.hasActiveFx()) {
32358             return;
32359         }
32360         if(this.isSlid){
32361             this.afterSlideIn();
32362             skipAnim = true;
32363         }
32364         this.collapsed = false;
32365         if(this.config.animate && skipAnim !== true){
32366             this.animateExpand();
32367         }else{
32368             this.el.show();
32369             if(this.split){
32370                 this.split.el.show();
32371             }
32372             this.collapsedEl.setLocation(-2000,-2000);
32373             this.collapsedEl.hide();
32374             this.fireEvent("invalidated", this);
32375             this.fireEvent("expanded", this);
32376         }
32377     },
32378 */
32379     animateExpand : function(){
32380         // overridden
32381     },
32382
32383     initTabs : function()
32384     {
32385         this.bodyEl.setStyle("overflow", "hidden");
32386         var ts = new Roo.bootstrap.panel.Tabs({
32387                 el: this.bodyEl.dom,
32388                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32389                 disableTooltips: this.config.disableTabTips,
32390                 toolbar : this.config.toolbar
32391             });
32392         
32393         if(this.config.hideTabs){
32394             ts.stripWrap.setDisplayed(false);
32395         }
32396         this.tabs = ts;
32397         ts.resizeTabs = this.config.resizeTabs === true;
32398         ts.minTabWidth = this.config.minTabWidth || 40;
32399         ts.maxTabWidth = this.config.maxTabWidth || 250;
32400         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32401         ts.monitorResize = false;
32402         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32403         ts.bodyEl.addClass('roo-layout-tabs-body');
32404         this.panels.each(this.initPanelAsTab, this);
32405     },
32406
32407     initPanelAsTab : function(panel){
32408         var ti = this.tabs.addTab(
32409                     panel.getEl().id,
32410                     panel.getTitle(), null,
32411                     this.config.closeOnTab && panel.isClosable()
32412             );
32413         if(panel.tabTip !== undefined){
32414             ti.setTooltip(panel.tabTip);
32415         }
32416         ti.on("activate", function(){
32417               this.setActivePanel(panel);
32418         }, this);
32419         
32420         if(this.config.closeOnTab){
32421             ti.on("beforeclose", function(t, e){
32422                 e.cancel = true;
32423                 this.remove(panel);
32424             }, this);
32425         }
32426         return ti;
32427     },
32428
32429     updatePanelTitle : function(panel, title)
32430     {
32431         if(this.activePanel == panel){
32432             this.updateTitle(title);
32433         }
32434         if(this.tabs){
32435             var ti = this.tabs.getTab(panel.getEl().id);
32436             ti.setText(title);
32437             if(panel.tabTip !== undefined){
32438                 ti.setTooltip(panel.tabTip);
32439             }
32440         }
32441     },
32442
32443     updateTitle : function(title){
32444         if(this.titleTextEl && !this.config.title){
32445             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32446         }
32447     },
32448
32449     setActivePanel : function(panel)
32450     {
32451         panel = this.getPanel(panel);
32452         if(this.activePanel && this.activePanel != panel){
32453             this.activePanel.setActiveState(false);
32454         }
32455         this.activePanel = panel;
32456         panel.setActiveState(true);
32457         if(this.panelSize){
32458             panel.setSize(this.panelSize.width, this.panelSize.height);
32459         }
32460         if(this.closeBtn){
32461             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32462         }
32463         this.updateTitle(panel.getTitle());
32464         if(this.tabs){
32465             this.fireEvent("invalidated", this);
32466         }
32467         this.fireEvent("panelactivated", this, panel);
32468     },
32469
32470     /**
32471      * Shows the specified panel.
32472      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32473      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32474      */
32475     showPanel : function(panel)
32476     {
32477         panel = this.getPanel(panel);
32478         if(panel){
32479             if(this.tabs){
32480                 var tab = this.tabs.getTab(panel.getEl().id);
32481                 if(tab.isHidden()){
32482                     this.tabs.unhideTab(tab.id);
32483                 }
32484                 tab.activate();
32485             }else{
32486                 this.setActivePanel(panel);
32487             }
32488         }
32489         return panel;
32490     },
32491
32492     /**
32493      * Get the active panel for this region.
32494      * @return {Roo.ContentPanel} The active panel or null
32495      */
32496     getActivePanel : function(){
32497         return this.activePanel;
32498     },
32499
32500     validateVisibility : function(){
32501         if(this.panels.getCount() < 1){
32502             this.updateTitle("&#160;");
32503             this.closeBtn.hide();
32504             this.hide();
32505         }else{
32506             if(!this.isVisible()){
32507                 this.show();
32508             }
32509         }
32510     },
32511
32512     /**
32513      * Adds the passed ContentPanel(s) to this region.
32514      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32515      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32516      */
32517     add : function(panel){
32518         if(arguments.length > 1){
32519             for(var i = 0, len = arguments.length; i < len; i++) {
32520                 this.add(arguments[i]);
32521             }
32522             return null;
32523         }
32524         if(this.hasPanel(panel)){
32525             this.showPanel(panel);
32526             return panel;
32527         }
32528         panel.setRegion(this);
32529         this.panels.add(panel);
32530         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32531             this.bodyEl.dom.appendChild(panel.getEl().dom);
32532             if(panel.background !== true){
32533                 this.setActivePanel(panel);
32534             }
32535             this.fireEvent("paneladded", this, panel);
32536             return panel;
32537         }
32538         if(!this.tabs){
32539             this.initTabs();
32540         }else{
32541             this.initPanelAsTab(panel);
32542         }
32543         
32544         
32545         if(panel.background !== true){
32546             this.tabs.activate(panel.getEl().id);
32547         }
32548         this.fireEvent("paneladded", this, panel);
32549         return panel;
32550     },
32551
32552     /**
32553      * Hides the tab for the specified panel.
32554      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32555      */
32556     hidePanel : function(panel){
32557         if(this.tabs && (panel = this.getPanel(panel))){
32558             this.tabs.hideTab(panel.getEl().id);
32559         }
32560     },
32561
32562     /**
32563      * Unhides the tab for a previously hidden panel.
32564      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32565      */
32566     unhidePanel : function(panel){
32567         if(this.tabs && (panel = this.getPanel(panel))){
32568             this.tabs.unhideTab(panel.getEl().id);
32569         }
32570     },
32571
32572     clearPanels : function(){
32573         while(this.panels.getCount() > 0){
32574              this.remove(this.panels.first());
32575         }
32576     },
32577
32578     /**
32579      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32580      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32581      * @param {Boolean} preservePanel Overrides the config preservePanel option
32582      * @return {Roo.ContentPanel} The panel that was removed
32583      */
32584     remove : function(panel, preservePanel)
32585     {
32586         panel = this.getPanel(panel);
32587         if(!panel){
32588             return null;
32589         }
32590         var e = {};
32591         this.fireEvent("beforeremove", this, panel, e);
32592         if(e.cancel === true){
32593             return null;
32594         }
32595         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32596         var panelId = panel.getId();
32597         this.panels.removeKey(panelId);
32598         if(preservePanel){
32599             document.body.appendChild(panel.getEl().dom);
32600         }
32601         if(this.tabs){
32602             this.tabs.removeTab(panel.getEl().id);
32603         }else if (!preservePanel){
32604             this.bodyEl.dom.removeChild(panel.getEl().dom);
32605         }
32606         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32607             var p = this.panels.first();
32608             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32609             tempEl.appendChild(p.getEl().dom);
32610             this.bodyEl.update("");
32611             this.bodyEl.dom.appendChild(p.getEl().dom);
32612             tempEl = null;
32613             this.updateTitle(p.getTitle());
32614             this.tabs = null;
32615             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32616             this.setActivePanel(p);
32617         }
32618         panel.setRegion(null);
32619         if(this.activePanel == panel){
32620             this.activePanel = null;
32621         }
32622         if(this.config.autoDestroy !== false && preservePanel !== true){
32623             try{panel.destroy();}catch(e){}
32624         }
32625         this.fireEvent("panelremoved", this, panel);
32626         return panel;
32627     },
32628
32629     /**
32630      * Returns the TabPanel component used by this region
32631      * @return {Roo.TabPanel}
32632      */
32633     getTabs : function(){
32634         return this.tabs;
32635     },
32636
32637     createTool : function(parentEl, className){
32638         var btn = Roo.DomHelper.append(parentEl, {
32639             tag: "div",
32640             cls: "x-layout-tools-button",
32641             children: [ {
32642                 tag: "div",
32643                 cls: "roo-layout-tools-button-inner " + className,
32644                 html: "&#160;"
32645             }]
32646         }, true);
32647         btn.addClassOnOver("roo-layout-tools-button-over");
32648         return btn;
32649     }
32650 });/*
32651  * Based on:
32652  * Ext JS Library 1.1.1
32653  * Copyright(c) 2006-2007, Ext JS, LLC.
32654  *
32655  * Originally Released Under LGPL - original licence link has changed is not relivant.
32656  *
32657  * Fork - LGPL
32658  * <script type="text/javascript">
32659  */
32660  
32661
32662
32663 /**
32664  * @class Roo.SplitLayoutRegion
32665  * @extends Roo.LayoutRegion
32666  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32667  */
32668 Roo.bootstrap.layout.Split = function(config){
32669     this.cursor = config.cursor;
32670     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32671 };
32672
32673 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32674 {
32675     splitTip : "Drag to resize.",
32676     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32677     useSplitTips : false,
32678
32679     applyConfig : function(config){
32680         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32681         
32682         if(config.split){
32683             if(!this.split){
32684                 
32685                 
32686                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,  {
32687                                 tag: "div",
32688                                 id: this.el.id + "-split",
32689                                 cls: "roo-layout-split roo-layout-split-"+this.position,
32690                                 html: "&#160;"
32691                 });
32692                 /** The SplitBar for this region 
32693                 * @type Roo.SplitBar */
32694                 // does not exist yet...
32695                 Roo.log([this.position, this.orientation]);
32696                 
32697                 this.split = new Roo.bootstrap.SplitBar({
32698                     dragElement : splitEl,
32699                     resizingElement: this.el,
32700                     orientation : this.orientation
32701                 });
32702                 
32703                 this.split.on("moved", this.onSplitMove, this);
32704                 this.split.useShim = config.useShim === true;
32705                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32706                 if(this.useSplitTips){
32707                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32708                 }
32709                 //if(config.collapsible){
32710                 //    this.split.el.on("dblclick", this.collapse,  this);
32711                 //}
32712             }
32713             if(typeof config.minSize != "undefined"){
32714                 this.split.minSize = config.minSize;
32715             }
32716             if(typeof config.maxSize != "undefined"){
32717                 this.split.maxSize = config.maxSize;
32718             }
32719             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32720                 this.hideSplitter();
32721             }
32722         }
32723     },
32724
32725     getHMaxSize : function(){
32726          var cmax = this.config.maxSize || 10000;
32727          var center = this.mgr.getRegion("center");
32728          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32729     },
32730
32731     getVMaxSize : function(){
32732          var cmax = this.config.maxSize || 10000;
32733          var center = this.mgr.getRegion("center");
32734          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32735     },
32736
32737     onSplitMove : function(split, newSize){
32738         this.fireEvent("resized", this, newSize);
32739     },
32740     
32741     /** 
32742      * Returns the {@link Roo.SplitBar} for this region.
32743      * @return {Roo.SplitBar}
32744      */
32745     getSplitBar : function(){
32746         return this.split;
32747     },
32748     
32749     hide : function(){
32750         this.hideSplitter();
32751         Roo.bootstrap.layout.Split.superclass.hide.call(this);
32752     },
32753
32754     hideSplitter : function(){
32755         if(this.split){
32756             this.split.el.setLocation(-2000,-2000);
32757             this.split.el.hide();
32758         }
32759     },
32760
32761     show : function(){
32762         if(this.split){
32763             this.split.el.show();
32764         }
32765         Roo.bootstrap.layout.Split.superclass.show.call(this);
32766     },
32767     
32768     beforeSlide: function(){
32769         if(Roo.isGecko){// firefox overflow auto bug workaround
32770             this.bodyEl.clip();
32771             if(this.tabs) {
32772                 this.tabs.bodyEl.clip();
32773             }
32774             if(this.activePanel){
32775                 this.activePanel.getEl().clip();
32776                 
32777                 if(this.activePanel.beforeSlide){
32778                     this.activePanel.beforeSlide();
32779                 }
32780             }
32781         }
32782     },
32783     
32784     afterSlide : function(){
32785         if(Roo.isGecko){// firefox overflow auto bug workaround
32786             this.bodyEl.unclip();
32787             if(this.tabs) {
32788                 this.tabs.bodyEl.unclip();
32789             }
32790             if(this.activePanel){
32791                 this.activePanel.getEl().unclip();
32792                 if(this.activePanel.afterSlide){
32793                     this.activePanel.afterSlide();
32794                 }
32795             }
32796         }
32797     },
32798
32799     initAutoHide : function(){
32800         if(this.autoHide !== false){
32801             if(!this.autoHideHd){
32802                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32803                 this.autoHideHd = {
32804                     "mouseout": function(e){
32805                         if(!e.within(this.el, true)){
32806                             st.delay(500);
32807                         }
32808                     },
32809                     "mouseover" : function(e){
32810                         st.cancel();
32811                     },
32812                     scope : this
32813                 };
32814             }
32815             this.el.on(this.autoHideHd);
32816         }
32817     },
32818
32819     clearAutoHide : function(){
32820         if(this.autoHide !== false){
32821             this.el.un("mouseout", this.autoHideHd.mouseout);
32822             this.el.un("mouseover", this.autoHideHd.mouseover);
32823         }
32824     },
32825
32826     clearMonitor : function(){
32827         Roo.get(document).un("click", this.slideInIf, this);
32828     },
32829
32830     // these names are backwards but not changed for compat
32831     slideOut : function(){
32832         if(this.isSlid || this.el.hasActiveFx()){
32833             return;
32834         }
32835         this.isSlid = true;
32836         if(this.collapseBtn){
32837             this.collapseBtn.hide();
32838         }
32839         this.closeBtnState = this.closeBtn.getStyle('display');
32840         this.closeBtn.hide();
32841         if(this.stickBtn){
32842             this.stickBtn.show();
32843         }
32844         this.el.show();
32845         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32846         this.beforeSlide();
32847         this.el.setStyle("z-index", 10001);
32848         this.el.slideIn(this.getSlideAnchor(), {
32849             callback: function(){
32850                 this.afterSlide();
32851                 this.initAutoHide();
32852                 Roo.get(document).on("click", this.slideInIf, this);
32853                 this.fireEvent("slideshow", this);
32854             },
32855             scope: this,
32856             block: true
32857         });
32858     },
32859
32860     afterSlideIn : function(){
32861         this.clearAutoHide();
32862         this.isSlid = false;
32863         this.clearMonitor();
32864         this.el.setStyle("z-index", "");
32865         if(this.collapseBtn){
32866             this.collapseBtn.show();
32867         }
32868         this.closeBtn.setStyle('display', this.closeBtnState);
32869         if(this.stickBtn){
32870             this.stickBtn.hide();
32871         }
32872         this.fireEvent("slidehide", this);
32873     },
32874
32875     slideIn : function(cb){
32876         if(!this.isSlid || this.el.hasActiveFx()){
32877             Roo.callback(cb);
32878             return;
32879         }
32880         this.isSlid = false;
32881         this.beforeSlide();
32882         this.el.slideOut(this.getSlideAnchor(), {
32883             callback: function(){
32884                 this.el.setLeftTop(-10000, -10000);
32885                 this.afterSlide();
32886                 this.afterSlideIn();
32887                 Roo.callback(cb);
32888             },
32889             scope: this,
32890             block: true
32891         });
32892     },
32893     
32894     slideInIf : function(e){
32895         if(!e.within(this.el)){
32896             this.slideIn();
32897         }
32898     },
32899
32900     animateCollapse : function(){
32901         this.beforeSlide();
32902         this.el.setStyle("z-index", 20000);
32903         var anchor = this.getSlideAnchor();
32904         this.el.slideOut(anchor, {
32905             callback : function(){
32906                 this.el.setStyle("z-index", "");
32907                 this.collapsedEl.slideIn(anchor, {duration:.3});
32908                 this.afterSlide();
32909                 this.el.setLocation(-10000,-10000);
32910                 this.el.hide();
32911                 this.fireEvent("collapsed", this);
32912             },
32913             scope: this,
32914             block: true
32915         });
32916     },
32917
32918     animateExpand : function(){
32919         this.beforeSlide();
32920         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32921         this.el.setStyle("z-index", 20000);
32922         this.collapsedEl.hide({
32923             duration:.1
32924         });
32925         this.el.slideIn(this.getSlideAnchor(), {
32926             callback : function(){
32927                 this.el.setStyle("z-index", "");
32928                 this.afterSlide();
32929                 if(this.split){
32930                     this.split.el.show();
32931                 }
32932                 this.fireEvent("invalidated", this);
32933                 this.fireEvent("expanded", this);
32934             },
32935             scope: this,
32936             block: true
32937         });
32938     },
32939
32940     anchors : {
32941         "west" : "left",
32942         "east" : "right",
32943         "north" : "top",
32944         "south" : "bottom"
32945     },
32946
32947     sanchors : {
32948         "west" : "l",
32949         "east" : "r",
32950         "north" : "t",
32951         "south" : "b"
32952     },
32953
32954     canchors : {
32955         "west" : "tl-tr",
32956         "east" : "tr-tl",
32957         "north" : "tl-bl",
32958         "south" : "bl-tl"
32959     },
32960
32961     getAnchor : function(){
32962         return this.anchors[this.position];
32963     },
32964
32965     getCollapseAnchor : function(){
32966         return this.canchors[this.position];
32967     },
32968
32969     getSlideAnchor : function(){
32970         return this.sanchors[this.position];
32971     },
32972
32973     getAlignAdj : function(){
32974         var cm = this.cmargins;
32975         switch(this.position){
32976             case "west":
32977                 return [0, 0];
32978             break;
32979             case "east":
32980                 return [0, 0];
32981             break;
32982             case "north":
32983                 return [0, 0];
32984             break;
32985             case "south":
32986                 return [0, 0];
32987             break;
32988         }
32989     },
32990
32991     getExpandAdj : function(){
32992         var c = this.collapsedEl, cm = this.cmargins;
32993         switch(this.position){
32994             case "west":
32995                 return [-(cm.right+c.getWidth()+cm.left), 0];
32996             break;
32997             case "east":
32998                 return [cm.right+c.getWidth()+cm.left, 0];
32999             break;
33000             case "north":
33001                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33002             break;
33003             case "south":
33004                 return [0, cm.top+cm.bottom+c.getHeight()];
33005             break;
33006         }
33007     }
33008 });/*
33009  * Based on:
33010  * Ext JS Library 1.1.1
33011  * Copyright(c) 2006-2007, Ext JS, LLC.
33012  *
33013  * Originally Released Under LGPL - original licence link has changed is not relivant.
33014  *
33015  * Fork - LGPL
33016  * <script type="text/javascript">
33017  */
33018 /*
33019  * These classes are private internal classes
33020  */
33021 Roo.bootstrap.layout.Center = function(config){
33022     config.region = "center";
33023     Roo.bootstrap.layout.Region.call(this, config);
33024     this.visible = true;
33025     this.minWidth = config.minWidth || 20;
33026     this.minHeight = config.minHeight || 20;
33027 };
33028
33029 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33030     hide : function(){
33031         // center panel can't be hidden
33032     },
33033     
33034     show : function(){
33035         // center panel can't be hidden
33036     },
33037     
33038     getMinWidth: function(){
33039         return this.minWidth;
33040     },
33041     
33042     getMinHeight: function(){
33043         return this.minHeight;
33044     }
33045 });
33046
33047
33048
33049
33050  
33051
33052
33053
33054
33055
33056 Roo.bootstrap.layout.North = function(config)
33057 {
33058     config.region = 'north';
33059     config.cursor = 'n-resize';
33060     
33061     Roo.bootstrap.layout.Split.call(this, config);
33062     if(this.split){
33063         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33064         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33065         this.split.el.addClass("roo-layout-split-v");
33066     }
33067     var size = config.initialSize || config.height;
33068     if(typeof size != "undefined"){
33069         this.el.setHeight(size);
33070     }
33071 };
33072 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33073 {
33074     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33075     getBox : function(){
33076         if(this.collapsed){
33077             return this.collapsedEl.getBox();
33078         }
33079         var box = this.el.getBox();
33080         if(this.split){
33081             box.height += this.split.el.getHeight();
33082         }
33083         return box;
33084     },
33085     
33086     updateBox : function(box){
33087         if(this.split && !this.collapsed){
33088             box.height -= this.split.el.getHeight();
33089             this.split.el.setLeft(box.x);
33090             this.split.el.setTop(box.y+box.height);
33091             this.split.el.setWidth(box.width);
33092         }
33093         if(this.collapsed){
33094             this.updateBody(box.width, null);
33095         }
33096         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33097     }
33098 });
33099
33100
33101
33102
33103
33104 Roo.bootstrap.layout.South = function(config){
33105     config.region = 'south';
33106     config.cursor = 's-resize';
33107     Roo.bootstrap.layout.Split.call(this, config);
33108     if(this.split){
33109         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33110         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33111         this.split.el.addClass("roo-layout-split-v");
33112     }
33113     var size = config.initialSize || config.height;
33114     if(typeof size != "undefined"){
33115         this.el.setHeight(size);
33116     }
33117 };
33118
33119 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33120     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33121     getBox : function(){
33122         if(this.collapsed){
33123             return this.collapsedEl.getBox();
33124         }
33125         var box = this.el.getBox();
33126         if(this.split){
33127             var sh = this.split.el.getHeight();
33128             box.height += sh;
33129             box.y -= sh;
33130         }
33131         return box;
33132     },
33133     
33134     updateBox : function(box){
33135         if(this.split && !this.collapsed){
33136             var sh = this.split.el.getHeight();
33137             box.height -= sh;
33138             box.y += sh;
33139             this.split.el.setLeft(box.x);
33140             this.split.el.setTop(box.y-sh);
33141             this.split.el.setWidth(box.width);
33142         }
33143         if(this.collapsed){
33144             this.updateBody(box.width, null);
33145         }
33146         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33147     }
33148 });
33149
33150 Roo.bootstrap.layout.East = function(config){
33151     config.region = "east";
33152     config.cursor = "e-resize";
33153     Roo.bootstrap.layout.Split.call(this, config);
33154     if(this.split){
33155         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33156         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33157         this.split.el.addClass("roo-layout-split-h");
33158     }
33159     var size = config.initialSize || config.width;
33160     if(typeof size != "undefined"){
33161         this.el.setWidth(size);
33162     }
33163 };
33164 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33165     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33166     getBox : function(){
33167         if(this.collapsed){
33168             return this.collapsedEl.getBox();
33169         }
33170         var box = this.el.getBox();
33171         if(this.split){
33172             var sw = this.split.el.getWidth();
33173             box.width += sw;
33174             box.x -= sw;
33175         }
33176         return box;
33177     },
33178
33179     updateBox : function(box){
33180         if(this.split && !this.collapsed){
33181             var sw = this.split.el.getWidth();
33182             box.width -= sw;
33183             this.split.el.setLeft(box.x);
33184             this.split.el.setTop(box.y);
33185             this.split.el.setHeight(box.height);
33186             box.x += sw;
33187         }
33188         if(this.collapsed){
33189             this.updateBody(null, box.height);
33190         }
33191         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33192     }
33193 });
33194
33195 Roo.bootstrap.layout.West = function(config){
33196     config.region = "west";
33197     config.cursor = "w-resize";
33198     
33199     Roo.bootstrap.layout.Split.call(this, config);
33200     if(this.split){
33201         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33202         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33203         this.split.el.addClass("roo-layout-split-h");
33204     }
33205     var size = config.initialSize || config.width;
33206     if(typeof size != "undefined"){
33207         this.el.setWidth(size);
33208     }
33209 };
33210 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33211     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33212     getBox : function(){
33213         if(this.collapsed){
33214             return this.collapsedEl.getBox();
33215         }
33216         var box = this.el.getBox();
33217         if(this.split){
33218             box.width += this.split.el.getWidth();
33219         }
33220         return box;
33221     },
33222     
33223     updateBox : function(box){
33224         if(this.split && !this.collapsed){
33225             var sw = this.split.el.getWidth();
33226             box.width -= sw;
33227             this.split.el.setLeft(box.x+box.width);
33228             this.split.el.setTop(box.y);
33229             this.split.el.setHeight(box.height);
33230         }
33231         if(this.collapsed){
33232             this.updateBody(null, box.height);
33233         }
33234         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33235     }
33236 });
33237 Roo.namespace("Roo.bootstrap.panel");/*
33238  * Based on:
33239  * Ext JS Library 1.1.1
33240  * Copyright(c) 2006-2007, Ext JS, LLC.
33241  *
33242  * Originally Released Under LGPL - original licence link has changed is not relivant.
33243  *
33244  * Fork - LGPL
33245  * <script type="text/javascript">
33246  */
33247 /**
33248  * @class Roo.ContentPanel
33249  * @extends Roo.util.Observable
33250  * A basic ContentPanel element.
33251  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33252  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33253  * @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
33254  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33255  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33256  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33257  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33258  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33259  * @cfg {String} title          The title for this panel
33260  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33261  * @cfg {String} url            Calls {@link #setUrl} with this value
33262  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33263  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33264  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33265  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33266
33267  * @constructor
33268  * Create a new ContentPanel.
33269  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33270  * @param {String/Object} config A string to set only the title or a config object
33271  * @param {String} content (optional) Set the HTML content for this panel
33272  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33273  */
33274 Roo.bootstrap.panel.Content = function( config){
33275     
33276     var el = config.el;
33277     var content = config.content;
33278
33279     if(config.autoCreate){ // xtype is available if this is called from factory
33280         el = Roo.id();
33281     }
33282     this.el = Roo.get(el);
33283     if(!this.el && config && config.autoCreate){
33284         if(typeof config.autoCreate == "object"){
33285             if(!config.autoCreate.id){
33286                 config.autoCreate.id = config.id||el;
33287             }
33288             this.el = Roo.DomHelper.append(document.body,
33289                         config.autoCreate, true);
33290         }else{
33291             var elcfg =  {   tag: "div",
33292                             cls: "roo-layout-inactive-content",
33293                             id: config.id||el
33294                             };
33295             if (config.html) {
33296                 elcfg.html = config.html;
33297                 
33298             }
33299                         
33300             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33301         }
33302     } 
33303     this.closable = false;
33304     this.loaded = false;
33305     this.active = false;
33306     if(typeof config == "string"){
33307         this.title = config;
33308     }else{
33309         Roo.apply(this, config);
33310     }
33311     /*
33312     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33313         this.wrapEl = this.el.wrap();
33314         this.toolbar.container = this.el.insertSibling(false, 'before');
33315         this.toolbar = new Roo.Toolbar(this.toolbar);
33316     }
33317     
33318     // xtype created footer. - not sure if will work as we normally have to render first..
33319     if (this.footer && !this.footer.el && this.footer.xtype) {
33320         if (!this.wrapEl) {
33321             this.wrapEl = this.el.wrap();
33322         }
33323     
33324         this.footer.container = this.wrapEl.createChild();
33325          
33326         this.footer = Roo.factory(this.footer, Roo);
33327         
33328     }
33329     */
33330     if(this.resizeEl){
33331         this.resizeEl = Roo.get(this.resizeEl, true);
33332     }else{
33333         this.resizeEl = this.el;
33334     }
33335     // handle view.xtype
33336     
33337  
33338     
33339     
33340     this.addEvents({
33341         /**
33342          * @event activate
33343          * Fires when this panel is activated. 
33344          * @param {Roo.ContentPanel} this
33345          */
33346         "activate" : true,
33347         /**
33348          * @event deactivate
33349          * Fires when this panel is activated. 
33350          * @param {Roo.ContentPanel} this
33351          */
33352         "deactivate" : true,
33353
33354         /**
33355          * @event resize
33356          * Fires when this panel is resized if fitToFrame is true.
33357          * @param {Roo.ContentPanel} this
33358          * @param {Number} width The width after any component adjustments
33359          * @param {Number} height The height after any component adjustments
33360          */
33361         "resize" : true,
33362         
33363          /**
33364          * @event render
33365          * Fires when this tab is created
33366          * @param {Roo.ContentPanel} this
33367          */
33368         "render" : true
33369         
33370         
33371         
33372     });
33373     
33374
33375     
33376     
33377     if(this.autoScroll){
33378         this.resizeEl.setStyle("overflow", "auto");
33379     } else {
33380         // fix randome scrolling
33381         this.el.on('scroll', function() {
33382             Roo.log('fix random scolling');
33383             this.scrollTo('top',0); 
33384         });
33385     }
33386     content = content || this.content;
33387     if(content){
33388         this.setContent(content);
33389     }
33390     if(config && config.url){
33391         this.setUrl(this.url, this.params, this.loadOnce);
33392     }
33393     
33394     
33395     
33396     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33397     
33398     if (this.view && typeof(this.view.xtype) != 'undefined') {
33399         this.view.el = this.el.appendChild(document.createElement("div"));
33400         this.view = Roo.factory(this.view); 
33401         this.view.render  &&  this.view.render(false, '');  
33402     }
33403     
33404     
33405     this.fireEvent('render', this);
33406 };
33407
33408 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33409     tabTip:'',
33410     setRegion : function(region){
33411         this.region = region;
33412         if(region){
33413            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33414         }else{
33415            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33416         } 
33417     },
33418     
33419     /**
33420      * Returns the toolbar for this Panel if one was configured. 
33421      * @return {Roo.Toolbar} 
33422      */
33423     getToolbar : function(){
33424         return this.toolbar;
33425     },
33426     
33427     setActiveState : function(active){
33428         this.active = active;
33429         if(!active){
33430             this.fireEvent("deactivate", this);
33431         }else{
33432             this.fireEvent("activate", this);
33433         }
33434     },
33435     /**
33436      * Updates this panel's element
33437      * @param {String} content The new content
33438      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33439     */
33440     setContent : function(content, loadScripts){
33441         this.el.update(content, loadScripts);
33442     },
33443
33444     ignoreResize : function(w, h){
33445         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33446             return true;
33447         }else{
33448             this.lastSize = {width: w, height: h};
33449             return false;
33450         }
33451     },
33452     /**
33453      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33454      * @return {Roo.UpdateManager} The UpdateManager
33455      */
33456     getUpdateManager : function(){
33457         return this.el.getUpdateManager();
33458     },
33459      /**
33460      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33461      * @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:
33462 <pre><code>
33463 panel.load({
33464     url: "your-url.php",
33465     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33466     callback: yourFunction,
33467     scope: yourObject, //(optional scope)
33468     discardUrl: false,
33469     nocache: false,
33470     text: "Loading...",
33471     timeout: 30,
33472     scripts: false
33473 });
33474 </code></pre>
33475      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33476      * 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.
33477      * @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}
33478      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33479      * @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.
33480      * @return {Roo.ContentPanel} this
33481      */
33482     load : function(){
33483         var um = this.el.getUpdateManager();
33484         um.update.apply(um, arguments);
33485         return this;
33486     },
33487
33488
33489     /**
33490      * 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.
33491      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33492      * @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)
33493      * @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)
33494      * @return {Roo.UpdateManager} The UpdateManager
33495      */
33496     setUrl : function(url, params, loadOnce){
33497         if(this.refreshDelegate){
33498             this.removeListener("activate", this.refreshDelegate);
33499         }
33500         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33501         this.on("activate", this.refreshDelegate);
33502         return this.el.getUpdateManager();
33503     },
33504     
33505     _handleRefresh : function(url, params, loadOnce){
33506         if(!loadOnce || !this.loaded){
33507             var updater = this.el.getUpdateManager();
33508             updater.update(url, params, this._setLoaded.createDelegate(this));
33509         }
33510     },
33511     
33512     _setLoaded : function(){
33513         this.loaded = true;
33514     }, 
33515     
33516     /**
33517      * Returns this panel's id
33518      * @return {String} 
33519      */
33520     getId : function(){
33521         return this.el.id;
33522     },
33523     
33524     /** 
33525      * Returns this panel's element - used by regiosn to add.
33526      * @return {Roo.Element} 
33527      */
33528     getEl : function(){
33529         return this.wrapEl || this.el;
33530     },
33531     
33532    
33533     
33534     adjustForComponents : function(width, height)
33535     {
33536         //Roo.log('adjustForComponents ');
33537         if(this.resizeEl != this.el){
33538             width -= this.el.getFrameWidth('lr');
33539             height -= this.el.getFrameWidth('tb');
33540         }
33541         if(this.toolbar){
33542             var te = this.toolbar.getEl();
33543             height -= te.getHeight();
33544             te.setWidth(width);
33545         }
33546         if(this.footer){
33547             var te = this.footer.getEl();
33548             Roo.log("footer:" + te.getHeight());
33549             
33550             height -= te.getHeight();
33551             te.setWidth(width);
33552         }
33553         
33554         
33555         if(this.adjustments){
33556             width += this.adjustments[0];
33557             height += this.adjustments[1];
33558         }
33559         return {"width": width, "height": height};
33560     },
33561     
33562     setSize : function(width, height){
33563         if(this.fitToFrame && !this.ignoreResize(width, height)){
33564             if(this.fitContainer && this.resizeEl != this.el){
33565                 this.el.setSize(width, height);
33566             }
33567             var size = this.adjustForComponents(width, height);
33568             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33569             this.fireEvent('resize', this, size.width, size.height);
33570         }
33571     },
33572     
33573     /**
33574      * Returns this panel's title
33575      * @return {String} 
33576      */
33577     getTitle : function(){
33578         return this.title;
33579     },
33580     
33581     /**
33582      * Set this panel's title
33583      * @param {String} title
33584      */
33585     setTitle : function(title){
33586         this.title = title;
33587         if(this.region){
33588             this.region.updatePanelTitle(this, title);
33589         }
33590     },
33591     
33592     /**
33593      * Returns true is this panel was configured to be closable
33594      * @return {Boolean} 
33595      */
33596     isClosable : function(){
33597         return this.closable;
33598     },
33599     
33600     beforeSlide : function(){
33601         this.el.clip();
33602         this.resizeEl.clip();
33603     },
33604     
33605     afterSlide : function(){
33606         this.el.unclip();
33607         this.resizeEl.unclip();
33608     },
33609     
33610     /**
33611      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33612      *   Will fail silently if the {@link #setUrl} method has not been called.
33613      *   This does not activate the panel, just updates its content.
33614      */
33615     refresh : function(){
33616         if(this.refreshDelegate){
33617            this.loaded = false;
33618            this.refreshDelegate();
33619         }
33620     },
33621     
33622     /**
33623      * Destroys this panel
33624      */
33625     destroy : function(){
33626         this.el.removeAllListeners();
33627         var tempEl = document.createElement("span");
33628         tempEl.appendChild(this.el.dom);
33629         tempEl.innerHTML = "";
33630         this.el.remove();
33631         this.el = null;
33632     },
33633     
33634     /**
33635      * form - if the content panel contains a form - this is a reference to it.
33636      * @type {Roo.form.Form}
33637      */
33638     form : false,
33639     /**
33640      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33641      *    This contains a reference to it.
33642      * @type {Roo.View}
33643      */
33644     view : false,
33645     
33646       /**
33647      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33648      * <pre><code>
33649
33650 layout.addxtype({
33651        xtype : 'Form',
33652        items: [ .... ]
33653    }
33654 );
33655
33656 </code></pre>
33657      * @param {Object} cfg Xtype definition of item to add.
33658      */
33659     
33660     
33661     getChildContainer: function () {
33662         return this.getEl();
33663     }
33664     
33665     
33666     /*
33667         var  ret = new Roo.factory(cfg);
33668         return ret;
33669         
33670         
33671         // add form..
33672         if (cfg.xtype.match(/^Form$/)) {
33673             
33674             var el;
33675             //if (this.footer) {
33676             //    el = this.footer.container.insertSibling(false, 'before');
33677             //} else {
33678                 el = this.el.createChild();
33679             //}
33680
33681             this.form = new  Roo.form.Form(cfg);
33682             
33683             
33684             if ( this.form.allItems.length) {
33685                 this.form.render(el.dom);
33686             }
33687             return this.form;
33688         }
33689         // should only have one of theses..
33690         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33691             // views.. should not be just added - used named prop 'view''
33692             
33693             cfg.el = this.el.appendChild(document.createElement("div"));
33694             // factory?
33695             
33696             var ret = new Roo.factory(cfg);
33697              
33698              ret.render && ret.render(false, ''); // render blank..
33699             this.view = ret;
33700             return ret;
33701         }
33702         return false;
33703     }
33704     \*/
33705 });
33706  
33707 /**
33708  * @class Roo.bootstrap.panel.Grid
33709  * @extends Roo.bootstrap.panel.Content
33710  * @constructor
33711  * Create a new GridPanel.
33712  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33713  * @param {String/Object} config A string to set only the panel's title, or a config object
33714
33715   new Roo.bootstrap.panel.Grid({
33716                 grid: .....
33717                 ....
33718   }
33719
33720  */
33721
33722
33723
33724 Roo.bootstrap.panel.Grid = function(config){
33725     
33726   
33727     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33728         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33729         
33730     this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
33731     config.el = this.wrapper;
33732     
33733     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
33734     
33735     if(this.toolbar){
33736         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33737     }
33738     // xtype created footer. - not sure if will work as we normally have to render first..
33739     if (this.footer && !this.footer.el && this.footer.xtype) {
33740         
33741         this.footer.container = this.grid.getView().getFooterPanel(true);
33742         this.footer.dataSource = this.grid.dataSource;
33743         this.footer = Roo.factory(this.footer, Roo);
33744         
33745     }
33746     
33747     
33748     config.grid.monitorWindowResize = false; // turn off autosizing
33749     config.grid.autoHeight = false;
33750     config.grid.autoWidth = false;
33751     this.grid = config.grid;
33752     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33753     
33754      
33755 };
33756
33757 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
33758     getId : function(){
33759         return this.grid.id;
33760     },
33761     
33762     /**
33763      * Returns the grid for this panel
33764      * @return {Roo.bootstrap.Table} 
33765      */
33766     getGrid : function(){
33767         return this.grid;    
33768     },
33769     
33770     setSize : function(width, height){
33771         if(!this.ignoreResize(width, height)){
33772             var grid = this.grid;
33773             var size = this.adjustForComponents(width, height);
33774             grid.getGridEl().setSize(size.width, size.height);
33775             /*
33776             var thd = grid.getGridEl().select('thead',true).first();
33777             var tbd = grid.getGridEl().select('tbody', true).first();
33778             if (tbd) {
33779                 tbd.setSize(width, height - thd.getHeight());
33780             }
33781             */
33782             grid.autoSize();
33783         }
33784     },
33785      
33786     
33787     
33788     beforeSlide : function(){
33789         this.grid.getView().scroller.clip();
33790     },
33791     
33792     afterSlide : function(){
33793         this.grid.getView().scroller.unclip();
33794     },
33795     
33796     destroy : function(){
33797         this.grid.destroy();
33798         delete this.grid;
33799         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
33800     }
33801 });
33802
33803 /**
33804  * @class Roo.bootstrap.panel.Nest
33805  * @extends Roo.bootstrap.panel.Content
33806  * @constructor
33807  * Create a new Panel, that can contain a layout.Border.
33808  * 
33809  * 
33810  * @param {Roo.BorderLayout} layout The layout for this panel
33811  * @param {String/Object} config A string to set only the title or a config object
33812  */
33813 Roo.bootstrap.panel.Nest = function(config)
33814 {
33815     // construct with only one argument..
33816     /* FIXME - implement nicer consturctors
33817     if (layout.layout) {
33818         config = layout;
33819         layout = config.layout;
33820         delete config.layout;
33821     }
33822     if (layout.xtype && !layout.getEl) {
33823         // then layout needs constructing..
33824         layout = Roo.factory(layout, Roo);
33825     }
33826     */
33827     
33828     config.el =  config.layout.getEl();
33829     
33830     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
33831     
33832     config.layout.monitorWindowResize = false; // turn off autosizing
33833     this.layout = config.layout;
33834     this.layout.getEl().addClass("roo-layout-nested-layout");
33835     
33836     
33837     
33838     
33839 };
33840
33841 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
33842
33843     setSize : function(width, height){
33844         if(!this.ignoreResize(width, height)){
33845             var size = this.adjustForComponents(width, height);
33846             var el = this.layout.getEl();
33847             el.setSize(size.width, size.height);
33848             var touch = el.dom.offsetWidth;
33849             this.layout.layout();
33850             // ie requires a double layout on the first pass
33851             if(Roo.isIE && !this.initialized){
33852                 this.initialized = true;
33853                 this.layout.layout();
33854             }
33855         }
33856     },
33857     
33858     // activate all subpanels if not currently active..
33859     
33860     setActiveState : function(active){
33861         this.active = active;
33862         if(!active){
33863             this.fireEvent("deactivate", this);
33864             return;
33865         }
33866         
33867         this.fireEvent("activate", this);
33868         // not sure if this should happen before or after..
33869         if (!this.layout) {
33870             return; // should not happen..
33871         }
33872         var reg = false;
33873         for (var r in this.layout.regions) {
33874             reg = this.layout.getRegion(r);
33875             if (reg.getActivePanel()) {
33876                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33877                 reg.setActivePanel(reg.getActivePanel());
33878                 continue;
33879             }
33880             if (!reg.panels.length) {
33881                 continue;
33882             }
33883             reg.showPanel(reg.getPanel(0));
33884         }
33885         
33886         
33887         
33888         
33889     },
33890     
33891     /**
33892      * Returns the nested BorderLayout for this panel
33893      * @return {Roo.BorderLayout} 
33894      */
33895     getLayout : function(){
33896         return this.layout;
33897     },
33898     
33899      /**
33900      * Adds a xtype elements to the layout of the nested panel
33901      * <pre><code>
33902
33903 panel.addxtype({
33904        xtype : 'ContentPanel',
33905        region: 'west',
33906        items: [ .... ]
33907    }
33908 );
33909
33910 panel.addxtype({
33911         xtype : 'NestedLayoutPanel',
33912         region: 'west',
33913         layout: {
33914            center: { },
33915            west: { }   
33916         },
33917         items : [ ... list of content panels or nested layout panels.. ]
33918    }
33919 );
33920 </code></pre>
33921      * @param {Object} cfg Xtype definition of item to add.
33922      */
33923     addxtype : function(cfg) {
33924         return this.layout.addxtype(cfg);
33925     
33926     }
33927 });        /*
33928  * Based on:
33929  * Ext JS Library 1.1.1
33930  * Copyright(c) 2006-2007, Ext JS, LLC.
33931  *
33932  * Originally Released Under LGPL - original licence link has changed is not relivant.
33933  *
33934  * Fork - LGPL
33935  * <script type="text/javascript">
33936  */
33937 /**
33938  * @class Roo.TabPanel
33939  * @extends Roo.util.Observable
33940  * A lightweight tab container.
33941  * <br><br>
33942  * Usage:
33943  * <pre><code>
33944 // basic tabs 1, built from existing content
33945 var tabs = new Roo.TabPanel("tabs1");
33946 tabs.addTab("script", "View Script");
33947 tabs.addTab("markup", "View Markup");
33948 tabs.activate("script");
33949
33950 // more advanced tabs, built from javascript
33951 var jtabs = new Roo.TabPanel("jtabs");
33952 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
33953
33954 // set up the UpdateManager
33955 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
33956 var updater = tab2.getUpdateManager();
33957 updater.setDefaultUrl("ajax1.htm");
33958 tab2.on('activate', updater.refresh, updater, true);
33959
33960 // Use setUrl for Ajax loading
33961 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
33962 tab3.setUrl("ajax2.htm", null, true);
33963
33964 // Disabled tab
33965 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
33966 tab4.disable();
33967
33968 jtabs.activate("jtabs-1");
33969  * </code></pre>
33970  * @constructor
33971  * Create a new TabPanel.
33972  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
33973  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
33974  */
33975 Roo.bootstrap.panel.Tabs = function(config){
33976     /**
33977     * The container element for this TabPanel.
33978     * @type Roo.Element
33979     */
33980     this.el = Roo.get(config.el);
33981     delete config.el;
33982     if(config){
33983         if(typeof config == "boolean"){
33984             this.tabPosition = config ? "bottom" : "top";
33985         }else{
33986             Roo.apply(this, config);
33987         }
33988     }
33989     
33990     if(this.tabPosition == "bottom"){
33991         this.bodyEl = Roo.get(this.createBody(this.el.dom));
33992         this.el.addClass("roo-tabs-bottom");
33993     }
33994     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
33995     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
33996     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
33997     if(Roo.isIE){
33998         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
33999     }
34000     if(this.tabPosition != "bottom"){
34001         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34002          * @type Roo.Element
34003          */
34004         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34005         this.el.addClass("roo-tabs-top");
34006     }
34007     this.items = [];
34008
34009     this.bodyEl.setStyle("position", "relative");
34010
34011     this.active = null;
34012     this.activateDelegate = this.activate.createDelegate(this);
34013
34014     this.addEvents({
34015         /**
34016          * @event tabchange
34017          * Fires when the active tab changes
34018          * @param {Roo.TabPanel} this
34019          * @param {Roo.TabPanelItem} activePanel The new active tab
34020          */
34021         "tabchange": true,
34022         /**
34023          * @event beforetabchange
34024          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34025          * @param {Roo.TabPanel} this
34026          * @param {Object} e Set cancel to true on this object to cancel the tab change
34027          * @param {Roo.TabPanelItem} tab The tab being changed to
34028          */
34029         "beforetabchange" : true
34030     });
34031
34032     Roo.EventManager.onWindowResize(this.onResize, this);
34033     this.cpad = this.el.getPadding("lr");
34034     this.hiddenCount = 0;
34035
34036
34037     // toolbar on the tabbar support...
34038     if (this.toolbar) {
34039         alert("no toolbar support yet");
34040         this.toolbar  = false;
34041         /*
34042         var tcfg = this.toolbar;
34043         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34044         this.toolbar = new Roo.Toolbar(tcfg);
34045         if (Roo.isSafari) {
34046             var tbl = tcfg.container.child('table', true);
34047             tbl.setAttribute('width', '100%');
34048         }
34049         */
34050         
34051     }
34052    
34053
34054
34055     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34056 };
34057
34058 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34059     /*
34060      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34061      */
34062     tabPosition : "top",
34063     /*
34064      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34065      */
34066     currentTabWidth : 0,
34067     /*
34068      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34069      */
34070     minTabWidth : 40,
34071     /*
34072      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34073      */
34074     maxTabWidth : 250,
34075     /*
34076      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34077      */
34078     preferredTabWidth : 175,
34079     /*
34080      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34081      */
34082     resizeTabs : false,
34083     /*
34084      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34085      */
34086     monitorResize : true,
34087     /*
34088      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34089      */
34090     toolbar : false,
34091
34092     /**
34093      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34094      * @param {String} id The id of the div to use <b>or create</b>
34095      * @param {String} text The text for the tab
34096      * @param {String} content (optional) Content to put in the TabPanelItem body
34097      * @param {Boolean} closable (optional) True to create a close icon on the tab
34098      * @return {Roo.TabPanelItem} The created TabPanelItem
34099      */
34100     addTab : function(id, text, content, closable)
34101     {
34102         var item = new Roo.bootstrap.panel.TabItem({
34103             panel: this,
34104             id : id,
34105             text : text,
34106             closable : closable
34107         });
34108         this.addTabItem(item);
34109         if(content){
34110             item.setContent(content);
34111         }
34112         return item;
34113     },
34114
34115     /**
34116      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34117      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34118      * @return {Roo.TabPanelItem}
34119      */
34120     getTab : function(id){
34121         return this.items[id];
34122     },
34123
34124     /**
34125      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34126      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34127      */
34128     hideTab : function(id){
34129         var t = this.items[id];
34130         if(!t.isHidden()){
34131            t.setHidden(true);
34132            this.hiddenCount++;
34133            this.autoSizeTabs();
34134         }
34135     },
34136
34137     /**
34138      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34139      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34140      */
34141     unhideTab : function(id){
34142         var t = this.items[id];
34143         if(t.isHidden()){
34144            t.setHidden(false);
34145            this.hiddenCount--;
34146            this.autoSizeTabs();
34147         }
34148     },
34149
34150     /**
34151      * Adds an existing {@link Roo.TabPanelItem}.
34152      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34153      */
34154     addTabItem : function(item){
34155         this.items[item.id] = item;
34156         this.items.push(item);
34157       //  if(this.resizeTabs){
34158     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34159   //         this.autoSizeTabs();
34160 //        }else{
34161 //            item.autoSize();
34162        // }
34163     },
34164
34165     /**
34166      * Removes a {@link Roo.TabPanelItem}.
34167      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34168      */
34169     removeTab : function(id){
34170         var items = this.items;
34171         var tab = items[id];
34172         if(!tab) { return; }
34173         var index = items.indexOf(tab);
34174         if(this.active == tab && items.length > 1){
34175             var newTab = this.getNextAvailable(index);
34176             if(newTab) {
34177                 newTab.activate();
34178             }
34179         }
34180         this.stripEl.dom.removeChild(tab.pnode.dom);
34181         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34182             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34183         }
34184         items.splice(index, 1);
34185         delete this.items[tab.id];
34186         tab.fireEvent("close", tab);
34187         tab.purgeListeners();
34188         this.autoSizeTabs();
34189     },
34190
34191     getNextAvailable : function(start){
34192         var items = this.items;
34193         var index = start;
34194         // look for a next tab that will slide over to
34195         // replace the one being removed
34196         while(index < items.length){
34197             var item = items[++index];
34198             if(item && !item.isHidden()){
34199                 return item;
34200             }
34201         }
34202         // if one isn't found select the previous tab (on the left)
34203         index = start;
34204         while(index >= 0){
34205             var item = items[--index];
34206             if(item && !item.isHidden()){
34207                 return item;
34208             }
34209         }
34210         return null;
34211     },
34212
34213     /**
34214      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34215      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34216      */
34217     disableTab : function(id){
34218         var tab = this.items[id];
34219         if(tab && this.active != tab){
34220             tab.disable();
34221         }
34222     },
34223
34224     /**
34225      * Enables a {@link Roo.TabPanelItem} that is disabled.
34226      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34227      */
34228     enableTab : function(id){
34229         var tab = this.items[id];
34230         tab.enable();
34231     },
34232
34233     /**
34234      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34235      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34236      * @return {Roo.TabPanelItem} The TabPanelItem.
34237      */
34238     activate : function(id){
34239         var tab = this.items[id];
34240         if(!tab){
34241             return null;
34242         }
34243         if(tab == this.active || tab.disabled){
34244             return tab;
34245         }
34246         var e = {};
34247         this.fireEvent("beforetabchange", this, e, tab);
34248         if(e.cancel !== true && !tab.disabled){
34249             if(this.active){
34250                 this.active.hide();
34251             }
34252             this.active = this.items[id];
34253             this.active.show();
34254             this.fireEvent("tabchange", this, this.active);
34255         }
34256         return tab;
34257     },
34258
34259     /**
34260      * Gets the active {@link Roo.TabPanelItem}.
34261      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34262      */
34263     getActiveTab : function(){
34264         return this.active;
34265     },
34266
34267     /**
34268      * Updates the tab body element to fit the height of the container element
34269      * for overflow scrolling
34270      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34271      */
34272     syncHeight : function(targetHeight){
34273         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34274         var bm = this.bodyEl.getMargins();
34275         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34276         this.bodyEl.setHeight(newHeight);
34277         return newHeight;
34278     },
34279
34280     onResize : function(){
34281         if(this.monitorResize){
34282             this.autoSizeTabs();
34283         }
34284     },
34285
34286     /**
34287      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34288      */
34289     beginUpdate : function(){
34290         this.updating = true;
34291     },
34292
34293     /**
34294      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34295      */
34296     endUpdate : function(){
34297         this.updating = false;
34298         this.autoSizeTabs();
34299     },
34300
34301     /**
34302      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34303      */
34304     autoSizeTabs : function(){
34305         var count = this.items.length;
34306         var vcount = count - this.hiddenCount;
34307         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34308             return;
34309         }
34310         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34311         var availWidth = Math.floor(w / vcount);
34312         var b = this.stripBody;
34313         if(b.getWidth() > w){
34314             var tabs = this.items;
34315             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34316             if(availWidth < this.minTabWidth){
34317                 /*if(!this.sleft){    // incomplete scrolling code
34318                     this.createScrollButtons();
34319                 }
34320                 this.showScroll();
34321                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34322             }
34323         }else{
34324             if(this.currentTabWidth < this.preferredTabWidth){
34325                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34326             }
34327         }
34328     },
34329
34330     /**
34331      * Returns the number of tabs in this TabPanel.
34332      * @return {Number}
34333      */
34334      getCount : function(){
34335          return this.items.length;
34336      },
34337
34338     /**
34339      * Resizes all the tabs to the passed width
34340      * @param {Number} The new width
34341      */
34342     setTabWidth : function(width){
34343         this.currentTabWidth = width;
34344         for(var i = 0, len = this.items.length; i < len; i++) {
34345                 if(!this.items[i].isHidden()) {
34346                 this.items[i].setWidth(width);
34347             }
34348         }
34349     },
34350
34351     /**
34352      * Destroys this TabPanel
34353      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34354      */
34355     destroy : function(removeEl){
34356         Roo.EventManager.removeResizeListener(this.onResize, this);
34357         for(var i = 0, len = this.items.length; i < len; i++){
34358             this.items[i].purgeListeners();
34359         }
34360         if(removeEl === true){
34361             this.el.update("");
34362             this.el.remove();
34363         }
34364     },
34365     
34366     createStrip : function(container)
34367     {
34368         var strip = document.createElement("nav");
34369         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34370         container.appendChild(strip);
34371         return strip;
34372     },
34373     
34374     createStripList : function(strip)
34375     {
34376         // div wrapper for retard IE
34377         // returns the "tr" element.
34378         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34379         //'<div class="x-tabs-strip-wrap">'+
34380           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34381           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34382         return strip.firstChild; //.firstChild.firstChild.firstChild;
34383     },
34384     createBody : function(container)
34385     {
34386         var body = document.createElement("div");
34387         Roo.id(body, "tab-body");
34388         //Roo.fly(body).addClass("x-tabs-body");
34389         Roo.fly(body).addClass("tab-content");
34390         container.appendChild(body);
34391         return body;
34392     },
34393     createItemBody :function(bodyEl, id){
34394         var body = Roo.getDom(id);
34395         if(!body){
34396             body = document.createElement("div");
34397             body.id = id;
34398         }
34399         //Roo.fly(body).addClass("x-tabs-item-body");
34400         Roo.fly(body).addClass("tab-pane");
34401          bodyEl.insertBefore(body, bodyEl.firstChild);
34402         return body;
34403     },
34404     /** @private */
34405     createStripElements :  function(stripEl, text, closable)
34406     {
34407         var td = document.createElement("li"); // was td..
34408         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34409         //stripEl.appendChild(td);
34410         /*if(closable){
34411             td.className = "x-tabs-closable";
34412             if(!this.closeTpl){
34413                 this.closeTpl = new Roo.Template(
34414                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34415                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34416                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34417                 );
34418             }
34419             var el = this.closeTpl.overwrite(td, {"text": text});
34420             var close = el.getElementsByTagName("div")[0];
34421             var inner = el.getElementsByTagName("em")[0];
34422             return {"el": el, "close": close, "inner": inner};
34423         } else {
34424         */
34425         // not sure what this is..
34426             if(!this.tabTpl){
34427                 //this.tabTpl = new Roo.Template(
34428                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34429                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34430                 //);
34431                 this.tabTpl = new Roo.Template(
34432                    '<a href="#">' +
34433                    '<span unselectable="on"' +
34434                             (this.disableTooltips ? '' : ' title="{text}"') +
34435                             ' >{text}</span></span></a>'
34436                 );
34437                 
34438             }
34439             var el = this.tabTpl.overwrite(td, {"text": text});
34440             var inner = el.getElementsByTagName("span")[0];
34441             return {"el": el, "inner": inner};
34442         //}
34443     }
34444         
34445     
34446 });
34447
34448 /**
34449  * @class Roo.TabPanelItem
34450  * @extends Roo.util.Observable
34451  * Represents an individual item (tab plus body) in a TabPanel.
34452  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34453  * @param {String} id The id of this TabPanelItem
34454  * @param {String} text The text for the tab of this TabPanelItem
34455  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34456  */
34457 Roo.bootstrap.panel.TabItem = function(config){
34458     /**
34459      * The {@link Roo.TabPanel} this TabPanelItem belongs to
34460      * @type Roo.TabPanel
34461      */
34462     this.tabPanel = config.panel;
34463     /**
34464      * The id for this TabPanelItem
34465      * @type String
34466      */
34467     this.id = config.id;
34468     /** @private */
34469     this.disabled = false;
34470     /** @private */
34471     this.text = config.text;
34472     /** @private */
34473     this.loaded = false;
34474     this.closable = config.closable;
34475
34476     /**
34477      * The body element for this TabPanelItem.
34478      * @type Roo.Element
34479      */
34480     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34481     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34482     this.bodyEl.setStyle("display", "block");
34483     this.bodyEl.setStyle("zoom", "1");
34484     //this.hideAction();
34485
34486     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34487     /** @private */
34488     this.el = Roo.get(els.el);
34489     this.inner = Roo.get(els.inner, true);
34490     this.textEl = Roo.get(this.el.dom.firstChild, true);
34491     this.pnode = Roo.get(els.el.parentNode, true);
34492     this.el.on("mousedown", this.onTabMouseDown, this);
34493     this.el.on("click", this.onTabClick, this);
34494     /** @private */
34495     if(config.closable){
34496         var c = Roo.get(els.close, true);
34497         c.dom.title = this.closeText;
34498         c.addClassOnOver("close-over");
34499         c.on("click", this.closeClick, this);
34500      }
34501
34502     this.addEvents({
34503          /**
34504          * @event activate
34505          * Fires when this tab becomes the active tab.
34506          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34507          * @param {Roo.TabPanelItem} this
34508          */
34509         "activate": true,
34510         /**
34511          * @event beforeclose
34512          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34513          * @param {Roo.TabPanelItem} this
34514          * @param {Object} e Set cancel to true on this object to cancel the close.
34515          */
34516         "beforeclose": true,
34517         /**
34518          * @event close
34519          * Fires when this tab is closed.
34520          * @param {Roo.TabPanelItem} this
34521          */
34522          "close": true,
34523         /**
34524          * @event deactivate
34525          * Fires when this tab is no longer the active tab.
34526          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34527          * @param {Roo.TabPanelItem} this
34528          */
34529          "deactivate" : true
34530     });
34531     this.hidden = false;
34532
34533     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34534 };
34535
34536 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34537            {
34538     purgeListeners : function(){
34539        Roo.util.Observable.prototype.purgeListeners.call(this);
34540        this.el.removeAllListeners();
34541     },
34542     /**
34543      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34544      */
34545     show : function(){
34546         this.pnode.addClass("active");
34547         this.showAction();
34548         if(Roo.isOpera){
34549             this.tabPanel.stripWrap.repaint();
34550         }
34551         this.fireEvent("activate", this.tabPanel, this);
34552     },
34553
34554     /**
34555      * Returns true if this tab is the active tab.
34556      * @return {Boolean}
34557      */
34558     isActive : function(){
34559         return this.tabPanel.getActiveTab() == this;
34560     },
34561
34562     /**
34563      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34564      */
34565     hide : function(){
34566         this.pnode.removeClass("active");
34567         this.hideAction();
34568         this.fireEvent("deactivate", this.tabPanel, this);
34569     },
34570
34571     hideAction : function(){
34572         this.bodyEl.hide();
34573         this.bodyEl.setStyle("position", "absolute");
34574         this.bodyEl.setLeft("-20000px");
34575         this.bodyEl.setTop("-20000px");
34576     },
34577
34578     showAction : function(){
34579         this.bodyEl.setStyle("position", "relative");
34580         this.bodyEl.setTop("");
34581         this.bodyEl.setLeft("");
34582         this.bodyEl.show();
34583     },
34584
34585     /**
34586      * Set the tooltip for the tab.
34587      * @param {String} tooltip The tab's tooltip
34588      */
34589     setTooltip : function(text){
34590         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34591             this.textEl.dom.qtip = text;
34592             this.textEl.dom.removeAttribute('title');
34593         }else{
34594             this.textEl.dom.title = text;
34595         }
34596     },
34597
34598     onTabClick : function(e){
34599         e.preventDefault();
34600         this.tabPanel.activate(this.id);
34601     },
34602
34603     onTabMouseDown : function(e){
34604         e.preventDefault();
34605         this.tabPanel.activate(this.id);
34606     },
34607 /*
34608     getWidth : function(){
34609         return this.inner.getWidth();
34610     },
34611
34612     setWidth : function(width){
34613         var iwidth = width - this.pnode.getPadding("lr");
34614         this.inner.setWidth(iwidth);
34615         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34616         this.pnode.setWidth(width);
34617     },
34618 */
34619     /**
34620      * Show or hide the tab
34621      * @param {Boolean} hidden True to hide or false to show.
34622      */
34623     setHidden : function(hidden){
34624         this.hidden = hidden;
34625         this.pnode.setStyle("display", hidden ? "none" : "");
34626     },
34627
34628     /**
34629      * Returns true if this tab is "hidden"
34630      * @return {Boolean}
34631      */
34632     isHidden : function(){
34633         return this.hidden;
34634     },
34635
34636     /**
34637      * Returns the text for this tab
34638      * @return {String}
34639      */
34640     getText : function(){
34641         return this.text;
34642     },
34643     /*
34644     autoSize : function(){
34645         //this.el.beginMeasure();
34646         this.textEl.setWidth(1);
34647         /*
34648          *  #2804 [new] Tabs in Roojs
34649          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34650          */
34651         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34652         //this.el.endMeasure();
34653     //},
34654
34655     /**
34656      * Sets the text for the tab (Note: this also sets the tooltip text)
34657      * @param {String} text The tab's text and tooltip
34658      */
34659     setText : function(text){
34660         this.text = text;
34661         this.textEl.update(text);
34662         this.setTooltip(text);
34663         //if(!this.tabPanel.resizeTabs){
34664         //    this.autoSize();
34665         //}
34666     },
34667     /**
34668      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34669      */
34670     activate : function(){
34671         this.tabPanel.activate(this.id);
34672     },
34673
34674     /**
34675      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34676      */
34677     disable : function(){
34678         if(this.tabPanel.active != this){
34679             this.disabled = true;
34680             this.pnode.addClass("disabled");
34681         }
34682     },
34683
34684     /**
34685      * Enables this TabPanelItem if it was previously disabled.
34686      */
34687     enable : function(){
34688         this.disabled = false;
34689         this.pnode.removeClass("disabled");
34690     },
34691
34692     /**
34693      * Sets the content for this TabPanelItem.
34694      * @param {String} content The content
34695      * @param {Boolean} loadScripts true to look for and load scripts
34696      */
34697     setContent : function(content, loadScripts){
34698         this.bodyEl.update(content, loadScripts);
34699     },
34700
34701     /**
34702      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
34703      * @return {Roo.UpdateManager} The UpdateManager
34704      */
34705     getUpdateManager : function(){
34706         return this.bodyEl.getUpdateManager();
34707     },
34708
34709     /**
34710      * Set a URL to be used to load the content for this TabPanelItem.
34711      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
34712      * @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)
34713      * @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)
34714      * @return {Roo.UpdateManager} The UpdateManager
34715      */
34716     setUrl : function(url, params, loadOnce){
34717         if(this.refreshDelegate){
34718             this.un('activate', this.refreshDelegate);
34719         }
34720         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34721         this.on("activate", this.refreshDelegate);
34722         return this.bodyEl.getUpdateManager();
34723     },
34724
34725     /** @private */
34726     _handleRefresh : function(url, params, loadOnce){
34727         if(!loadOnce || !this.loaded){
34728             var updater = this.bodyEl.getUpdateManager();
34729             updater.update(url, params, this._setLoaded.createDelegate(this));
34730         }
34731     },
34732
34733     /**
34734      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
34735      *   Will fail silently if the setUrl method has not been called.
34736      *   This does not activate the panel, just updates its content.
34737      */
34738     refresh : function(){
34739         if(this.refreshDelegate){
34740            this.loaded = false;
34741            this.refreshDelegate();
34742         }
34743     },
34744
34745     /** @private */
34746     _setLoaded : function(){
34747         this.loaded = true;
34748     },
34749
34750     /** @private */
34751     closeClick : function(e){
34752         var o = {};
34753         e.stopEvent();
34754         this.fireEvent("beforeclose", this, o);
34755         if(o.cancel !== true){
34756             this.tabPanel.removeTab(this.id);
34757         }
34758     },
34759     /**
34760      * The text displayed in the tooltip for the close icon.
34761      * @type String
34762      */
34763     closeText : "Close this tab"
34764 });