roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  * 
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  * 
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394     Roo.bootstrap.Body.superclass.constructor.call(this, config);
395     this.el = Roo.get(document.body);
396     if (this.cls && this.cls.length) {
397         Roo.get(document.body).addClass(this.cls);
398     }
399 };
400
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
402     
403     is_body : true,// just to make sure it's constructed?
404     
405         autoCreate : {
406         cls: 'container'
407     },
408     onRender : function(ct, position)
409     {
410        /* Roo.log("Roo.bootstrap.Body - onRender");
411         if (this.cls && this.cls.length) {
412             Roo.get(document.body).addClass(this.cls);
413         }
414         // style??? xttr???
415         */
416     }
417     
418     
419  
420    
421 });
422
423  /*
424  * - LGPL
425  *
426  * button group
427  * 
428  */
429
430
431 /**
432  * @class Roo.bootstrap.ButtonGroup
433  * @extends Roo.bootstrap.Component
434  * Bootstrap ButtonGroup class
435  * @cfg {String} size lg | sm | xs (default empty normal)
436  * @cfg {String} align vertical | justified  (default none)
437  * @cfg {String} direction up | down (default down)
438  * @cfg {Boolean} toolbar false | true
439  * @cfg {Boolean} btn true | false
440  * 
441  * 
442  * @constructor
443  * Create a new Input
444  * @param {Object} config The config object
445  */
446
447 Roo.bootstrap.ButtonGroup = function(config){
448     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
449 };
450
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
452     
453     size: '',
454     align: '',
455     direction: '',
456     toolbar: false,
457     btn: true,
458
459     getAutoCreate : function(){
460         var cfg = {
461             cls: 'btn-group',
462             html : null
463         };
464         
465         cfg.html = this.html || cfg.html;
466         
467         if (this.toolbar) {
468             cfg = {
469                 cls: 'btn-toolbar',
470                 html: null
471             };
472             
473             return cfg;
474         }
475         
476         if (['vertical','justified'].indexOf(this.align)!==-1) {
477             cfg.cls = 'btn-group-' + this.align;
478             
479             if (this.align == 'justified') {
480                 console.log(this.items);
481             }
482         }
483         
484         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485             cfg.cls += ' btn-group-' + this.size;
486         }
487         
488         if (this.direction == 'up') {
489             cfg.cls += ' dropup' ;
490         }
491         
492         return cfg;
493     }
494    
495 });
496
497  /*
498  * - LGPL
499  *
500  * button
501  * 
502  */
503
504 /**
505  * @class Roo.bootstrap.Button
506  * @extends Roo.bootstrap.Component
507  * Bootstrap Button class
508  * @cfg {String} html The button content
509  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
510  * @cfg {String} size ( lg | sm | xs)
511  * @cfg {String} tag ( a | input | submit)
512  * @cfg {String} href empty or href
513  * @cfg {Boolean} disabled default false;
514  * @cfg {Boolean} isClose default false;
515  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
516  * @cfg {String} badge text for badge
517  * @cfg {String} theme default 
518  * @cfg {Boolean} inverse 
519  * @cfg {Boolean} toggle 
520  * @cfg {String} ontext text for on toggle state
521  * @cfg {String} offtext text for off toggle state
522  * @cfg {Boolean} defaulton 
523  * @cfg {Boolean} preventDefault  default true
524  * @cfg {Boolean} removeClass remove the standard class..
525  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
526  * 
527  * @constructor
528  * Create a new button
529  * @param {Object} config The config object
530  */
531
532
533 Roo.bootstrap.Button = function(config){
534     Roo.bootstrap.Button.superclass.constructor.call(this, config);
535     this.addEvents({
536         // raw events
537         /**
538          * @event click
539          * When a butotn is pressed
540          * @param {Roo.bootstrap.Button} this
541          * @param {Roo.EventObject} e
542          */
543         "click" : true,
544          /**
545          * @event toggle
546          * After the button has been toggles
547          * @param {Roo.EventObject} e
548          * @param {boolean} pressed (also available as button.pressed)
549          */
550         "toggle" : true
551     });
552 };
553
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
555     html: false,
556     active: false,
557     weight: '',
558     size: '',
559     tag: 'button',
560     href: '',
561     disabled: false,
562     isClose: false,
563     glyphicon: '',
564     badge: '',
565     theme: 'default',
566     inverse: false,
567     
568     toggle: false,
569     ontext: 'ON',
570     offtext: 'OFF',
571     defaulton: true,
572     preventDefault: true,
573     removeClass: false,
574     name: false,
575     target: false,
576     
577     
578     pressed : null,
579      
580     
581     getAutoCreate : function(){
582         
583         var cfg = {
584             tag : 'button',
585             cls : 'roo-button',
586             html: ''
587         };
588         
589         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
591             this.tag = 'button';
592         } else {
593             cfg.tag = this.tag;
594         }
595         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
596         
597         if (this.toggle == true) {
598             cfg={
599                 tag: 'div',
600                 cls: 'slider-frame roo-button',
601                 cn: [
602                     {
603                         tag: 'span',
604                         'data-on-text':'ON',
605                         'data-off-text':'OFF',
606                         cls: 'slider-button',
607                         html: this.offtext
608                     }
609                 ]
610             };
611             
612             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613                 cfg.cls += ' '+this.weight;
614             }
615             
616             return cfg;
617         }
618         
619         if (this.isClose) {
620             cfg.cls += ' close';
621             
622             cfg["aria-hidden"] = true;
623             
624             cfg.html = "&times;";
625             
626             return cfg;
627         }
628         
629          
630         if (this.theme==='default') {
631             cfg.cls = 'btn roo-button';
632             
633             //if (this.parentType != 'Navbar') {
634             this.weight = this.weight.length ?  this.weight : 'default';
635             //}
636             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637                 
638                 cfg.cls += ' btn-' + this.weight;
639             }
640         } else if (this.theme==='glow') {
641             
642             cfg.tag = 'a';
643             cfg.cls = 'btn-glow roo-button';
644             
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' ' + this.weight;
648             }
649         }
650    
651         
652         if (this.inverse) {
653             this.cls += ' inverse';
654         }
655         
656         
657         if (this.active) {
658             cfg.cls += ' active';
659         }
660         
661         if (this.disabled) {
662             cfg.disabled = 'disabled';
663         }
664         
665         if (this.items) {
666             Roo.log('changing to ul' );
667             cfg.tag = 'ul';
668             this.glyphicon = 'caret';
669         }
670         
671         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
672          
673         //gsRoo.log(this.parentType);
674         if (this.parentType === 'Navbar' && !this.parent().bar) {
675             Roo.log('changing to li?');
676             
677             cfg.tag = 'li';
678             
679             cfg.cls = '';
680             cfg.cn =  [{
681                 tag : 'a',
682                 cls : 'roo-button',
683                 html : this.html,
684                 href : this.href || '#'
685             }];
686             if (this.menu) {
687                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
688                 cfg.cls += ' dropdown';
689             }   
690             
691             delete cfg.html;
692             
693         }
694         
695        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
696         
697         if (this.glyphicon) {
698             cfg.html = ' ' + cfg.html;
699             
700             cfg.cn = [
701                 {
702                     tag: 'span',
703                     cls: 'glyphicon glyphicon-' + this.glyphicon
704                 }
705             ];
706         }
707         
708         if (this.badge) {
709             cfg.html += ' ';
710             
711             cfg.tag = 'a';
712             
713 //            cfg.cls='btn roo-button';
714             
715             cfg.href=this.href;
716             
717             var value = cfg.html;
718             
719             if(this.glyphicon){
720                 value = {
721                             tag: 'span',
722                             cls: 'glyphicon glyphicon-' + this.glyphicon,
723                             html: this.html
724                         };
725                 
726             }
727             
728             cfg.cn = [
729                 value,
730                 {
731                     tag: 'span',
732                     cls: 'badge',
733                     html: this.badge
734                 }
735             ];
736             
737             cfg.html='';
738         }
739         
740         if (this.menu) {
741             cfg.cls += ' dropdown';
742             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
743         }
744         
745         if (cfg.tag !== 'a' && this.href !== '') {
746             throw "Tag must be a to set href.";
747         } else if (this.href.length > 0) {
748             cfg.href = this.href;
749         }
750         
751         if(this.removeClass){
752             cfg.cls = '';
753         }
754         
755         if(this.target){
756             cfg.target = this.target;
757         }
758         
759         return cfg;
760     },
761     initEvents: function() {
762        // Roo.log('init events?');
763 //        Roo.log(this.el.dom);
764         // add the menu...
765         
766         if (typeof (this.menu) != 'undefined') {
767             this.menu.parentType = this.xtype;
768             this.menu.triggerEl = this.el;
769             this.addxtype(Roo.apply({}, this.menu));
770         }
771
772
773        if (this.el.hasClass('roo-button')) {
774             this.el.on('click', this.onClick, this);
775        } else {
776             this.el.select('.roo-button').on('click', this.onClick, this);
777        }
778        
779        if(this.removeClass){
780            this.el.on('click', this.onClick, this);
781        }
782        
783        this.el.enableDisplayMode();
784         
785     },
786     onClick : function(e)
787     {
788         if (this.disabled) {
789             return;
790         }
791         
792         
793         Roo.log('button on click ');
794         if(this.preventDefault){
795             e.preventDefault();
796         }
797         if (this.pressed === true || this.pressed === false) {
798             this.pressed = !this.pressed;
799             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800             this.fireEvent('toggle', this, e, this.pressed);
801         }
802         
803         
804         this.fireEvent('click', this, e);
805     },
806     
807     /**
808      * Enables this button
809      */
810     enable : function()
811     {
812         this.disabled = false;
813         this.el.removeClass('disabled');
814     },
815     
816     /**
817      * Disable this button
818      */
819     disable : function()
820     {
821         this.disabled = true;
822         this.el.addClass('disabled');
823     },
824      /**
825      * sets the active state on/off, 
826      * @param {Boolean} state (optional) Force a particular state
827      */
828     setActive : function(v) {
829         
830         this.el[v ? 'addClass' : 'removeClass']('active');
831     },
832      /**
833      * toggles the current active state 
834      */
835     toggleActive : function()
836     {
837        var active = this.el.hasClass('active');
838        this.setActive(!active);
839        
840         
841     },
842     setText : function(str)
843     {
844         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
845     },
846     getText : function()
847     {
848         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
849     },
850     hide: function() {
851        
852      
853         this.el.hide();   
854     },
855     show: function() {
856        
857         this.el.show();   
858     }
859     
860     
861 });
862
863  /*
864  * - LGPL
865  *
866  * column
867  * 
868  */
869
870 /**
871  * @class Roo.bootstrap.Column
872  * @extends Roo.bootstrap.Component
873  * Bootstrap Column class
874  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
882  *
883  * 
884  * @cfg {Boolean} hidden (true|false) hide the element
885  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886  * @cfg {String} fa (ban|check|...) font awesome icon
887  * @cfg {Number} fasize (1|2|....) font awsome size
888
889  * @cfg {String} icon (info-sign|check|...) glyphicon name
890
891  * @cfg {String} html content of column.
892  * 
893  * @constructor
894  * Create a new Column
895  * @param {Object} config The config object
896  */
897
898 Roo.bootstrap.Column = function(config){
899     Roo.bootstrap.Column.superclass.constructor.call(this, config);
900 };
901
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
903     
904     xs: false,
905     sm: false,
906     md: false,
907     lg: false,
908     xsoff: false,
909     smoff: false,
910     mdoff: false,
911     lgoff: false,
912     html: '',
913     offset: 0,
914     alert: false,
915     fa: false,
916     icon : false,
917     hidden : false,
918     fasize : 1,
919     
920     getAutoCreate : function(){
921         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
922         
923         cfg = {
924             tag: 'div',
925             cls: 'column'
926         };
927         
928         var settings=this;
929         ['xs','sm','md','lg'].map(function(size){
930             //Roo.log( size + ':' + settings[size]);
931             
932             if (settings[size+'off'] !== false) {
933                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
934             }
935             
936             if (settings[size] === false) {
937                 return;
938             }
939             
940             if (!settings[size]) { // 0 = hidden
941                 cfg.cls += ' hidden-' + size;
942                 return;
943             }
944             cfg.cls += ' col-' + size + '-' + settings[size];
945             
946         });
947         
948         if (this.hidden) {
949             cfg.cls += ' hidden';
950         }
951         
952         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953             cfg.cls +=' alert alert-' + this.alert;
954         }
955         
956         
957         if (this.html.length) {
958             cfg.html = this.html;
959         }
960         if (this.fa) {
961             var fasize = '';
962             if (this.fasize > 1) {
963                 fasize = ' fa-' + this.fasize + 'x';
964             }
965             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
966             
967             
968         }
969         if (this.icon) {
970             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
971         }
972         
973         return cfg;
974     }
975    
976 });
977
978  
979
980  /*
981  * - LGPL
982  *
983  * page container.
984  * 
985  */
986
987
988 /**
989  * @class Roo.bootstrap.Container
990  * @extends Roo.bootstrap.Component
991  * Bootstrap Container class
992  * @cfg {Boolean} jumbotron is it a jumbotron element
993  * @cfg {String} html content of element
994  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
996  * @cfg {String} header content of header (for panel)
997  * @cfg {String} footer content of footer (for panel)
998  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999  * @cfg {String} tag (header|aside|section) type of HTML tag.
1000  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001  * @cfg {String} fa font awesome icon
1002  * @cfg {String} icon (info-sign|check|...) glyphicon name
1003  * @cfg {Boolean} hidden (true|false) hide the element
1004  * @cfg {Boolean} expandable (true|false) default false
1005  * @cfg {Boolean} expanded (true|false) default true
1006  * @cfg {String} rheader contet on the right of header
1007  * @cfg {Boolean} clickable (true|false) default false
1008
1009  *     
1010  * @constructor
1011  * Create a new Container
1012  * @param {Object} config The config object
1013  */
1014
1015 Roo.bootstrap.Container = function(config){
1016     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020          /**
1021          * @event expand
1022          * After the panel has been expand
1023          * 
1024          * @param {Roo.bootstrap.Container} this
1025          */
1026         "expand" : true,
1027         /**
1028          * @event collapse
1029          * After the panel has been collapsed
1030          * 
1031          * @param {Roo.bootstrap.Container} this
1032          */
1033         "collapse" : true,
1034         /**
1035          * @event click
1036          * When a element is chick
1037          * @param {Roo.bootstrap.Container} this
1038          * @param {Roo.EventObject} e
1039          */
1040         "click" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1045     
1046     jumbotron : false,
1047     well: '',
1048     panel : '',
1049     header: '',
1050     footer : '',
1051     sticky: '',
1052     tag : false,
1053     alert : false,
1054     fa: false,
1055     icon : false,
1056     expandable : false,
1057     rheader : '',
1058     expanded : true,
1059     clickable: false,
1060   
1061      
1062     getChildContainer : function() {
1063         
1064         if(!this.el){
1065             return false;
1066         }
1067         
1068         if (this.panel.length) {
1069             return this.el.select('.panel-body',true).first();
1070         }
1071         
1072         return this.el;
1073     },
1074     
1075     
1076     getAutoCreate : function(){
1077         
1078         var cfg = {
1079             tag : this.tag || 'div',
1080             html : '',
1081             cls : ''
1082         };
1083         if (this.jumbotron) {
1084             cfg.cls = 'jumbotron';
1085         }
1086         
1087         
1088         
1089         // - this is applied by the parent..
1090         //if (this.cls) {
1091         //    cfg.cls = this.cls + '';
1092         //}
1093         
1094         if (this.sticky.length) {
1095             
1096             var bd = Roo.get(document.body);
1097             if (!bd.hasClass('bootstrap-sticky')) {
1098                 bd.addClass('bootstrap-sticky');
1099                 Roo.select('html',true).setStyle('height', '100%');
1100             }
1101              
1102             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1103         }
1104         
1105         
1106         if (this.well.length) {
1107             switch (this.well) {
1108                 case 'lg':
1109                 case 'sm':
1110                     cfg.cls +=' well well-' +this.well;
1111                     break;
1112                 default:
1113                     cfg.cls +=' well';
1114                     break;
1115             }
1116         }
1117         
1118         if (this.hidden) {
1119             cfg.cls += ' hidden';
1120         }
1121         
1122         
1123         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124             cfg.cls +=' alert alert-' + this.alert;
1125         }
1126         
1127         var body = cfg;
1128         
1129         if (this.panel.length) {
1130             cfg.cls += ' panel panel-' + this.panel;
1131             cfg.cn = [];
1132             if (this.header.length) {
1133                 
1134                 var h = [];
1135                 
1136                 if(this.expandable){
1137                     
1138                     cfg.cls = cfg.cls + ' expandable';
1139                     
1140                     h.push({
1141                         tag: 'i',
1142                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1143                     });
1144                     
1145                 }
1146                 
1147                 h.push(
1148                     {
1149                         tag: 'span',
1150                         cls : 'panel-title',
1151                         html : (this.expandable ? '&nbsp;' : '') + this.header
1152                     },
1153                     {
1154                         tag: 'span',
1155                         cls: 'panel-header-right',
1156                         html: this.rheader
1157                     }
1158                 );
1159                 
1160                 cfg.cn.push({
1161                     cls : 'panel-heading',
1162                     style : this.expandable ? 'cursor: pointer' : '',
1163                     cn : h
1164                 });
1165                 
1166             }
1167             
1168             body = false;
1169             cfg.cn.push({
1170                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1171                 html : this.html
1172             });
1173             
1174             
1175             if (this.footer.length) {
1176                 cfg.cn.push({
1177                     cls : 'panel-footer',
1178                     html : this.footer
1179                     
1180                 });
1181             }
1182             
1183         }
1184         
1185         if (body) {
1186             body.html = this.html || cfg.html;
1187             // prefix with the icons..
1188             if (this.fa) {
1189                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1190             }
1191             if (this.icon) {
1192                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1193             }
1194             
1195             
1196         }
1197         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198             cfg.cls =  'container';
1199         }
1200         
1201         return cfg;
1202     },
1203     
1204     initEvents: function() 
1205     {
1206         if(this.expandable){
1207             var headerEl = this.headerEl();
1208         
1209             if(headerEl){
1210                 headerEl.on('click', this.onToggleClick, this);
1211             }
1212         }
1213         
1214         if(this.clickable){
1215             this.el.on('click', this.onClick, this);
1216         }
1217         
1218     },
1219     
1220     onToggleClick : function()
1221     {
1222         var headerEl = this.headerEl();
1223         
1224         if(!headerEl){
1225             return;
1226         }
1227         
1228         if(this.expanded){
1229             this.collapse();
1230             return;
1231         }
1232         
1233         this.expand();
1234     },
1235     
1236     expand : function()
1237     {
1238         if(this.fireEvent('expand', this)) {
1239             
1240             this.expanded = true;
1241             
1242             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1243             
1244             this.el.select('.panel-body',true).first().removeClass('hide');
1245             
1246             var toggleEl = this.toggleEl();
1247
1248             if(!toggleEl){
1249                 return;
1250             }
1251
1252             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1253         }
1254         
1255     },
1256     
1257     collapse : function()
1258     {
1259         if(this.fireEvent('collapse', this)) {
1260             
1261             this.expanded = false;
1262             
1263             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264             this.el.select('.panel-body',true).first().addClass('hide');
1265         
1266             var toggleEl = this.toggleEl();
1267
1268             if(!toggleEl){
1269                 return;
1270             }
1271
1272             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1273         }
1274     },
1275     
1276     toggleEl : function()
1277     {
1278         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1279             return;
1280         }
1281         
1282         return this.el.select('.panel-heading .fa',true).first();
1283     },
1284     
1285     headerEl : function()
1286     {
1287         if(!this.el || !this.panel.length || !this.header.length){
1288             return;
1289         }
1290         
1291         return this.el.select('.panel-heading',true).first()
1292     },
1293     
1294     titleEl : function()
1295     {
1296         if(!this.el || !this.panel.length || !this.header.length){
1297             return;
1298         }
1299         
1300         return this.el.select('.panel-title',true).first();
1301     },
1302     
1303     setTitle : function(v)
1304     {
1305         var titleEl = this.titleEl();
1306         
1307         if(!titleEl){
1308             return;
1309         }
1310         
1311         titleEl.dom.innerHTML = v;
1312     },
1313     
1314     getTitle : function()
1315     {
1316         
1317         var titleEl = this.titleEl();
1318         
1319         if(!titleEl){
1320             return '';
1321         }
1322         
1323         return titleEl.dom.innerHTML;
1324     },
1325     
1326     setRightTitle : function(v)
1327     {
1328         var t = this.el.select('.panel-header-right',true).first();
1329         
1330         if(!t){
1331             return;
1332         }
1333         
1334         t.dom.innerHTML = v;
1335     },
1336     
1337     onClick : function(e)
1338     {
1339         e.preventDefault();
1340         
1341         this.fireEvent('click', this, e);
1342     }
1343    
1344 });
1345
1346  /*
1347  * - LGPL
1348  *
1349  * image
1350  * 
1351  */
1352
1353
1354 /**
1355  * @class Roo.bootstrap.Img
1356  * @extends Roo.bootstrap.Component
1357  * Bootstrap Img class
1358  * @cfg {Boolean} imgResponsive false | true
1359  * @cfg {String} border rounded | circle | thumbnail
1360  * @cfg {String} src image source
1361  * @cfg {String} alt image alternative text
1362  * @cfg {String} href a tag href
1363  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364  * @cfg {String} xsUrl xs image source
1365  * @cfg {String} smUrl sm image source
1366  * @cfg {String} mdUrl md image source
1367  * @cfg {String} lgUrl lg image source
1368  * 
1369  * @constructor
1370  * Create a new Input
1371  * @param {Object} config The config object
1372  */
1373
1374 Roo.bootstrap.Img = function(config){
1375     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1376     
1377     this.addEvents({
1378         // img events
1379         /**
1380          * @event click
1381          * The img click event for the img.
1382          * @param {Roo.EventObject} e
1383          */
1384         "click" : true
1385     });
1386 };
1387
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1389     
1390     imgResponsive: true,
1391     border: '',
1392     src: 'about:blank',
1393     href: false,
1394     target: false,
1395     xsUrl: '',
1396     smUrl: '',
1397     mdUrl: '',
1398     lgUrl: '',
1399
1400     getAutoCreate : function()
1401     {   
1402         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403             return this.createSingleImg();
1404         }
1405         
1406         var cfg = {
1407             tag: 'div',
1408             cls: 'roo-image-responsive-group',
1409             cn: []
1410         };
1411         var _this = this;
1412         
1413         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1414             
1415             if(!_this[size + 'Url']){
1416                 return;
1417             }
1418             
1419             var img = {
1420                 tag: 'img',
1421                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422                 html: _this.html || cfg.html,
1423                 src: _this[size + 'Url']
1424             };
1425             
1426             img.cls += ' roo-image-responsive-' + size;
1427             
1428             var s = ['xs', 'sm', 'md', 'lg'];
1429             
1430             s.splice(s.indexOf(size), 1);
1431             
1432             Roo.each(s, function(ss){
1433                 img.cls += ' hidden-' + ss;
1434             });
1435             
1436             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437                 cfg.cls += ' img-' + _this.border;
1438             }
1439             
1440             if(_this.alt){
1441                 cfg.alt = _this.alt;
1442             }
1443             
1444             if(_this.href){
1445                 var a = {
1446                     tag: 'a',
1447                     href: _this.href,
1448                     cn: [
1449                         img
1450                     ]
1451                 };
1452
1453                 if(this.target){
1454                     a.target = _this.target;
1455                 }
1456             }
1457             
1458             cfg.cn.push((_this.href) ? a : img);
1459             
1460         });
1461         
1462         return cfg;
1463     },
1464     
1465     createSingleImg : function()
1466     {
1467         var cfg = {
1468             tag: 'img',
1469             cls: (this.imgResponsive) ? 'img-responsive' : '',
1470             html : null,
1471             src : 'about:blank'  // just incase src get's set to undefined?!?
1472         };
1473         
1474         cfg.html = this.html || cfg.html;
1475         
1476         cfg.src = this.src || cfg.src;
1477         
1478         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479             cfg.cls += ' img-' + this.border;
1480         }
1481         
1482         if(this.alt){
1483             cfg.alt = this.alt;
1484         }
1485         
1486         if(this.href){
1487             var a = {
1488                 tag: 'a',
1489                 href: this.href,
1490                 cn: [
1491                     cfg
1492                 ]
1493             };
1494             
1495             if(this.target){
1496                 a.target = this.target;
1497             }
1498             
1499         }
1500         
1501         return (this.href) ? a : cfg;
1502     },
1503     
1504     initEvents: function() 
1505     {
1506         if(!this.href){
1507             this.el.on('click', this.onClick, this);
1508         }
1509         
1510     },
1511     
1512     onClick : function(e)
1513     {
1514         Roo.log('img onclick');
1515         this.fireEvent('click', this, e);
1516     }
1517    
1518 });
1519
1520  /*
1521  * - LGPL
1522  *
1523  * image
1524  * 
1525  */
1526
1527
1528 /**
1529  * @class Roo.bootstrap.Link
1530  * @extends Roo.bootstrap.Component
1531  * Bootstrap Link Class
1532  * @cfg {String} alt image alternative text
1533  * @cfg {String} href a tag href
1534  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535  * @cfg {String} html the content of the link.
1536  * @cfg {String} anchor name for the anchor link
1537  * @cfg {String} fa - favicon
1538
1539  * @cfg {Boolean} preventDefault (true | false) default false
1540
1541  * 
1542  * @constructor
1543  * Create a new Input
1544  * @param {Object} config The config object
1545  */
1546
1547 Roo.bootstrap.Link = function(config){
1548     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1549     
1550     this.addEvents({
1551         // img events
1552         /**
1553          * @event click
1554          * The img click event for the img.
1555          * @param {Roo.EventObject} e
1556          */
1557         "click" : true
1558     });
1559 };
1560
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1562     
1563     href: false,
1564     target: false,
1565     preventDefault: false,
1566     anchor : false,
1567     alt : false,
1568     fa: false,
1569
1570
1571     getAutoCreate : function()
1572     {
1573         var html = this.html || '';
1574         
1575         if (this.fa !== false) {
1576             html = '<i class="fa fa-' + this.fa + '"></i>';
1577         }
1578         var cfg = {
1579             tag: 'a'
1580         };
1581         // anchor's do not require html/href...
1582         if (this.anchor === false) {
1583             cfg.html = html;
1584             cfg.href = this.href || '#';
1585         } else {
1586             cfg.name = this.anchor;
1587             if (this.html !== false || this.fa !== false) {
1588                 cfg.html = html;
1589             }
1590             if (this.href !== false) {
1591                 cfg.href = this.href;
1592             }
1593         }
1594         
1595         if(this.alt !== false){
1596             cfg.alt = this.alt;
1597         }
1598         
1599         
1600         if(this.target !== false) {
1601             cfg.target = this.target;
1602         }
1603         
1604         return cfg;
1605     },
1606     
1607     initEvents: function() {
1608         
1609         if(!this.href || this.preventDefault){
1610             this.el.on('click', this.onClick, this);
1611         }
1612     },
1613     
1614     onClick : function(e)
1615     {
1616         if(this.preventDefault){
1617             e.preventDefault();
1618         }
1619         //Roo.log('img onclick');
1620         this.fireEvent('click', this, e);
1621     }
1622    
1623 });
1624
1625  /*
1626  * - LGPL
1627  *
1628  * header
1629  * 
1630  */
1631
1632 /**
1633  * @class Roo.bootstrap.Header
1634  * @extends Roo.bootstrap.Component
1635  * Bootstrap Header class
1636  * @cfg {String} html content of header
1637  * @cfg {Number} level (1|2|3|4|5|6) default 1
1638  * 
1639  * @constructor
1640  * Create a new Header
1641  * @param {Object} config The config object
1642  */
1643
1644
1645 Roo.bootstrap.Header  = function(config){
1646     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1650     
1651     //href : false,
1652     html : false,
1653     level : 1,
1654     
1655     
1656     
1657     getAutoCreate : function(){
1658         
1659         
1660         
1661         var cfg = {
1662             tag: 'h' + (1 *this.level),
1663             html: this.html || ''
1664         } ;
1665         
1666         return cfg;
1667     }
1668    
1669 });
1670
1671  
1672
1673  /*
1674  * Based on:
1675  * Ext JS Library 1.1.1
1676  * Copyright(c) 2006-2007, Ext JS, LLC.
1677  *
1678  * Originally Released Under LGPL - original licence link has changed is not relivant.
1679  *
1680  * Fork - LGPL
1681  * <script type="text/javascript">
1682  */
1683  
1684 /**
1685  * @class Roo.bootstrap.MenuMgr
1686  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1687  * @singleton
1688  */
1689 Roo.bootstrap.MenuMgr = function(){
1690    var menus, active, groups = {}, attached = false, lastShow = new Date();
1691
1692    // private - called when first menu is created
1693    function init(){
1694        menus = {};
1695        active = new Roo.util.MixedCollection();
1696        Roo.get(document).addKeyListener(27, function(){
1697            if(active.length > 0){
1698                hideAll();
1699            }
1700        });
1701    }
1702
1703    // private
1704    function hideAll(){
1705        if(active && active.length > 0){
1706            var c = active.clone();
1707            c.each(function(m){
1708                m.hide();
1709            });
1710        }
1711    }
1712
1713    // private
1714    function onHide(m){
1715        active.remove(m);
1716        if(active.length < 1){
1717            Roo.get(document).un("mouseup", onMouseDown);
1718             
1719            attached = false;
1720        }
1721    }
1722
1723    // private
1724    function onShow(m){
1725        var last = active.last();
1726        lastShow = new Date();
1727        active.add(m);
1728        if(!attached){
1729           Roo.get(document).on("mouseup", onMouseDown);
1730            
1731            attached = true;
1732        }
1733        if(m.parentMenu){
1734           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735           m.parentMenu.activeChild = m;
1736        }else if(last && last.isVisible()){
1737           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1738        }
1739    }
1740
1741    // private
1742    function onBeforeHide(m){
1743        if(m.activeChild){
1744            m.activeChild.hide();
1745        }
1746        if(m.autoHideTimer){
1747            clearTimeout(m.autoHideTimer);
1748            delete m.autoHideTimer;
1749        }
1750    }
1751
1752    // private
1753    function onBeforeShow(m){
1754        var pm = m.parentMenu;
1755        if(!pm && !m.allowOtherMenus){
1756            hideAll();
1757        }else if(pm && pm.activeChild && active != m){
1758            pm.activeChild.hide();
1759        }
1760    }
1761
1762    // private this should really trigger on mouseup..
1763    function onMouseDown(e){
1764         Roo.log("on Mouse Up");
1765         
1766         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767             Roo.log("MenuManager hideAll");
1768             hideAll();
1769             e.stopEvent();
1770         }
1771         
1772         
1773    }
1774
1775    // private
1776    function onBeforeCheck(mi, state){
1777        if(state){
1778            var g = groups[mi.group];
1779            for(var i = 0, l = g.length; i < l; i++){
1780                if(g[i] != mi){
1781                    g[i].setChecked(false);
1782                }
1783            }
1784        }
1785    }
1786
1787    return {
1788
1789        /**
1790         * Hides all menus that are currently visible
1791         */
1792        hideAll : function(){
1793             hideAll();  
1794        },
1795
1796        // private
1797        register : function(menu){
1798            if(!menus){
1799                init();
1800            }
1801            menus[menu.id] = menu;
1802            menu.on("beforehide", onBeforeHide);
1803            menu.on("hide", onHide);
1804            menu.on("beforeshow", onBeforeShow);
1805            menu.on("show", onShow);
1806            var g = menu.group;
1807            if(g && menu.events["checkchange"]){
1808                if(!groups[g]){
1809                    groups[g] = [];
1810                }
1811                groups[g].push(menu);
1812                menu.on("checkchange", onCheck);
1813            }
1814        },
1815
1816         /**
1817          * Returns a {@link Roo.menu.Menu} object
1818          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819          * be used to generate and return a new Menu instance.
1820          */
1821        get : function(menu){
1822            if(typeof menu == "string"){ // menu id
1823                return menus[menu];
1824            }else if(menu.events){  // menu instance
1825                return menu;
1826            }
1827            /*else if(typeof menu.length == 'number'){ // array of menu items?
1828                return new Roo.bootstrap.Menu({items:menu});
1829            }else{ // otherwise, must be a config
1830                return new Roo.bootstrap.Menu(menu);
1831            }
1832            */
1833            return false;
1834        },
1835
1836        // private
1837        unregister : function(menu){
1838            delete menus[menu.id];
1839            menu.un("beforehide", onBeforeHide);
1840            menu.un("hide", onHide);
1841            menu.un("beforeshow", onBeforeShow);
1842            menu.un("show", onShow);
1843            var g = menu.group;
1844            if(g && menu.events["checkchange"]){
1845                groups[g].remove(menu);
1846                menu.un("checkchange", onCheck);
1847            }
1848        },
1849
1850        // private
1851        registerCheckable : function(menuItem){
1852            var g = menuItem.group;
1853            if(g){
1854                if(!groups[g]){
1855                    groups[g] = [];
1856                }
1857                groups[g].push(menuItem);
1858                menuItem.on("beforecheckchange", onBeforeCheck);
1859            }
1860        },
1861
1862        // private
1863        unregisterCheckable : function(menuItem){
1864            var g = menuItem.group;
1865            if(g){
1866                groups[g].remove(menuItem);
1867                menuItem.un("beforecheckchange", onBeforeCheck);
1868            }
1869        }
1870    };
1871 }();/*
1872  * - LGPL
1873  *
1874  * menu
1875  * 
1876  */
1877
1878 /**
1879  * @class Roo.bootstrap.Menu
1880  * @extends Roo.bootstrap.Component
1881  * Bootstrap Menu class - container for MenuItems
1882  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1884  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1885  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1886  * 
1887  * @constructor
1888  * Create a new Menu
1889  * @param {Object} config The config object
1890  */
1891
1892
1893 Roo.bootstrap.Menu = function(config){
1894     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895     if (this.registerMenu && this.type != 'treeview')  {
1896         Roo.bootstrap.MenuMgr.register(this);
1897     }
1898     this.addEvents({
1899         /**
1900          * @event beforeshow
1901          * Fires before this menu is displayed
1902          * @param {Roo.menu.Menu} this
1903          */
1904         beforeshow : true,
1905         /**
1906          * @event beforehide
1907          * Fires before this menu is hidden
1908          * @param {Roo.menu.Menu} this
1909          */
1910         beforehide : true,
1911         /**
1912          * @event show
1913          * Fires after this menu is displayed
1914          * @param {Roo.menu.Menu} this
1915          */
1916         show : true,
1917         /**
1918          * @event hide
1919          * Fires after this menu is hidden
1920          * @param {Roo.menu.Menu} this
1921          */
1922         hide : true,
1923         /**
1924          * @event click
1925          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926          * @param {Roo.menu.Menu} this
1927          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928          * @param {Roo.EventObject} e
1929          */
1930         click : true,
1931         /**
1932          * @event mouseover
1933          * Fires when the mouse is hovering over this menu
1934          * @param {Roo.menu.Menu} this
1935          * @param {Roo.EventObject} e
1936          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1937          */
1938         mouseover : true,
1939         /**
1940          * @event mouseout
1941          * Fires when the mouse exits this menu
1942          * @param {Roo.menu.Menu} this
1943          * @param {Roo.EventObject} e
1944          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1945          */
1946         mouseout : true,
1947         /**
1948          * @event itemclick
1949          * Fires when a menu item contained in this menu is clicked
1950          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951          * @param {Roo.EventObject} e
1952          */
1953         itemclick: true
1954     });
1955     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1956 };
1957
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1959     
1960    /// html : false,
1961     //align : '',
1962     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1963     type: false,
1964     /**
1965      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1966      */
1967     registerMenu : true,
1968     
1969     menuItems :false, // stores the menu items..
1970     
1971     hidden:true,
1972         
1973     parentMenu : false,
1974     
1975     stopEvent : true,
1976     
1977     isLink : false,
1978     
1979     getChildContainer : function() {
1980         return this.el;  
1981     },
1982     
1983     getAutoCreate : function(){
1984          
1985         //if (['right'].indexOf(this.align)!==-1) {
1986         //    cfg.cn[1].cls += ' pull-right'
1987         //}
1988         
1989         
1990         var cfg = {
1991             tag : 'ul',
1992             cls : 'dropdown-menu' ,
1993             style : 'z-index:1000'
1994             
1995         };
1996         
1997         if (this.type === 'submenu') {
1998             cfg.cls = 'submenu active';
1999         }
2000         if (this.type === 'treeview') {
2001             cfg.cls = 'treeview-menu';
2002         }
2003         
2004         return cfg;
2005     },
2006     initEvents : function() {
2007         
2008        // Roo.log("ADD event");
2009        // Roo.log(this.triggerEl.dom);
2010         
2011         this.triggerEl.on('click', this.onTriggerClick, this);
2012         
2013         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2014         
2015         this.triggerEl.addClass('dropdown-toggle');
2016         
2017         if (Roo.isTouch) {
2018             this.el.on('touchstart'  , this.onTouch, this);
2019         }
2020         this.el.on('click' , this.onClick, this);
2021
2022         this.el.on("mouseover", this.onMouseOver, this);
2023         this.el.on("mouseout", this.onMouseOut, this);
2024         
2025     },
2026     
2027     findTargetItem : function(e)
2028     {
2029         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2030         if(!t){
2031             return false;
2032         }
2033         //Roo.log(t);         Roo.log(t.id);
2034         if(t && t.id){
2035             //Roo.log(this.menuitems);
2036             return this.menuitems.get(t.id);
2037             
2038             //return this.items.get(t.menuItemId);
2039         }
2040         
2041         return false;
2042     },
2043     
2044     onTouch : function(e) 
2045     {
2046         Roo.log("menu.onTouch");
2047         //e.stopEvent(); this make the user popdown broken
2048         this.onClick(e);
2049     },
2050     
2051     onClick : function(e)
2052     {
2053         Roo.log("menu.onClick");
2054         
2055         var t = this.findTargetItem(e);
2056         if(!t || t.isContainer){
2057             return;
2058         }
2059         Roo.log(e);
2060         /*
2061         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2062             if(t == this.activeItem && t.shouldDeactivate(e)){
2063                 this.activeItem.deactivate();
2064                 delete this.activeItem;
2065                 return;
2066             }
2067             if(t.canActivate){
2068                 this.setActiveItem(t, true);
2069             }
2070             return;
2071             
2072             
2073         }
2074         */
2075        
2076         Roo.log('pass click event');
2077         
2078         t.onClick(e);
2079         
2080         this.fireEvent("click", this, t, e);
2081         
2082         var _this = this;
2083         
2084         (function() { _this.hide(); }).defer(500);
2085     },
2086     
2087     onMouseOver : function(e){
2088         var t  = this.findTargetItem(e);
2089         //Roo.log(t);
2090         //if(t){
2091         //    if(t.canActivate && !t.disabled){
2092         //        this.setActiveItem(t, true);
2093         //    }
2094         //}
2095         
2096         this.fireEvent("mouseover", this, e, t);
2097     },
2098     isVisible : function(){
2099         return !this.hidden;
2100     },
2101      onMouseOut : function(e){
2102         var t  = this.findTargetItem(e);
2103         
2104         //if(t ){
2105         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2106         //        this.activeItem.deactivate();
2107         //        delete this.activeItem;
2108         //    }
2109         //}
2110         this.fireEvent("mouseout", this, e, t);
2111     },
2112     
2113     
2114     /**
2115      * Displays this menu relative to another element
2116      * @param {String/HTMLElement/Roo.Element} element The element to align to
2117      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118      * the element (defaults to this.defaultAlign)
2119      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2120      */
2121     show : function(el, pos, parentMenu){
2122         this.parentMenu = parentMenu;
2123         if(!this.el){
2124             this.render();
2125         }
2126         this.fireEvent("beforeshow", this);
2127         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2128     },
2129      /**
2130      * Displays this menu at a specific xy position
2131      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2133      */
2134     showAt : function(xy, parentMenu, /* private: */_e){
2135         this.parentMenu = parentMenu;
2136         if(!this.el){
2137             this.render();
2138         }
2139         if(_e !== false){
2140             this.fireEvent("beforeshow", this);
2141             //xy = this.el.adjustForConstraints(xy);
2142         }
2143         
2144         //this.el.show();
2145         this.hideMenuItems();
2146         this.hidden = false;
2147         this.triggerEl.addClass('open');
2148         
2149         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2151         }
2152         
2153         if(this.el.getStyle('top').slice(-1) != "%"){
2154             this.el.setXY(xy);
2155         }
2156         
2157         this.focus();
2158         this.fireEvent("show", this);
2159     },
2160     
2161     focus : function(){
2162         return;
2163         if(!this.hidden){
2164             this.doFocus.defer(50, this);
2165         }
2166     },
2167
2168     doFocus : function(){
2169         if(!this.hidden){
2170             this.focusEl.focus();
2171         }
2172     },
2173
2174     /**
2175      * Hides this menu and optionally all parent menus
2176      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2177      */
2178     hide : function(deep)
2179     {
2180         
2181         this.hideMenuItems();
2182         if(this.el && this.isVisible()){
2183             this.fireEvent("beforehide", this);
2184             if(this.activeItem){
2185                 this.activeItem.deactivate();
2186                 this.activeItem = null;
2187             }
2188             this.triggerEl.removeClass('open');;
2189             this.hidden = true;
2190             this.fireEvent("hide", this);
2191         }
2192         if(deep === true && this.parentMenu){
2193             this.parentMenu.hide(true);
2194         }
2195     },
2196     
2197     onTriggerClick : function(e)
2198     {
2199         Roo.log('trigger click');
2200         
2201         var target = e.getTarget();
2202         
2203         Roo.log(target.nodeName.toLowerCase());
2204         
2205         if(target.nodeName.toLowerCase() === 'i'){
2206             e.preventDefault();
2207         }
2208         
2209     },
2210     
2211     onTriggerPress  : function(e)
2212     {
2213         Roo.log('trigger press');
2214         //Roo.log(e.getTarget());
2215        // Roo.log(this.triggerEl.dom);
2216        
2217         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2218         var pel = Roo.get(e.getTarget());
2219         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2220             Roo.log('is treeview or dropdown?');
2221             return;
2222         }
2223         
2224         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2225             return;
2226         }
2227         
2228         if (this.isVisible()) {
2229             Roo.log('hide');
2230             this.hide();
2231         } else {
2232             Roo.log('show');
2233             this.show(this.triggerEl, false, false);
2234         }
2235         
2236         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2237             e.stopEvent();
2238         }
2239         
2240     },
2241        
2242     
2243     hideMenuItems : function()
2244     {
2245         Roo.log("hide Menu Items");
2246         if (!this.el) { 
2247             return;
2248         }
2249         //$(backdrop).remove()
2250         this.el.select('.open',true).each(function(aa) {
2251             
2252             aa.removeClass('open');
2253           //var parent = getParent($(this))
2254           //var relatedTarget = { relatedTarget: this }
2255           
2256            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2257           //if (e.isDefaultPrevented()) return
2258            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2259         });
2260     },
2261     addxtypeChild : function (tree, cntr) {
2262         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2263           
2264         this.menuitems.add(comp);
2265         return comp;
2266
2267     },
2268     getEl : function()
2269     {
2270         Roo.log(this.el);
2271         return this.el;
2272     }
2273 });
2274
2275  
2276  /*
2277  * - LGPL
2278  *
2279  * menu item
2280  * 
2281  */
2282
2283
2284 /**
2285  * @class Roo.bootstrap.MenuItem
2286  * @extends Roo.bootstrap.Component
2287  * Bootstrap MenuItem class
2288  * @cfg {String} html the menu label
2289  * @cfg {String} href the link
2290  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2291  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2292  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2293  * @cfg {String} fa favicon to show on left of menu item.
2294  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2295  * 
2296  * 
2297  * @constructor
2298  * Create a new MenuItem
2299  * @param {Object} config The config object
2300  */
2301
2302
2303 Roo.bootstrap.MenuItem = function(config){
2304     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2305     this.addEvents({
2306         // raw events
2307         /**
2308          * @event click
2309          * The raw click event for the entire grid.
2310          * @param {Roo.bootstrap.MenuItem} this
2311          * @param {Roo.EventObject} e
2312          */
2313         "click" : true
2314     });
2315 };
2316
2317 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2318     
2319     href : false,
2320     html : false,
2321     preventDefault: true,
2322     isContainer : false,
2323     active : false,
2324     fa: false,
2325     
2326     getAutoCreate : function(){
2327         
2328         if(this.isContainer){
2329             return {
2330                 tag: 'li',
2331                 cls: 'dropdown-menu-item'
2332             };
2333         }
2334         var ctag = {
2335             tag: 'span',
2336             html: 'Link'
2337         };
2338         
2339         var anc = {
2340             tag : 'a',
2341             href : '#',
2342             cn : [  ]
2343         };
2344         
2345         if (this.fa !== false) {
2346             anc.cn.push({
2347                 tag : 'i',
2348                 cls : 'fa fa-' + this.fa
2349             });
2350         }
2351         
2352         anc.cn.push(ctag);
2353         
2354         
2355         var cfg= {
2356             tag: 'li',
2357             cls: 'dropdown-menu-item',
2358             cn: [ anc ]
2359         };
2360         if (this.parent().type == 'treeview') {
2361             cfg.cls = 'treeview-menu';
2362         }
2363         if (this.active) {
2364             cfg.cls += ' active';
2365         }
2366         
2367         
2368         
2369         anc.href = this.href || cfg.cn[0].href ;
2370         ctag.html = this.html || cfg.cn[0].html ;
2371         return cfg;
2372     },
2373     
2374     initEvents: function()
2375     {
2376         if (this.parent().type == 'treeview') {
2377             this.el.select('a').on('click', this.onClick, this);
2378         }
2379         if (this.menu) {
2380             this.menu.parentType = this.xtype;
2381             this.menu.triggerEl = this.el;
2382             this.menu = this.addxtype(Roo.apply({}, this.menu));
2383         }
2384         
2385     },
2386     onClick : function(e)
2387     {
2388         Roo.log('item on click ');
2389         //if(this.preventDefault){
2390         //    e.preventDefault();
2391         //}
2392         //this.parent().hideMenuItems();
2393         
2394         this.fireEvent('click', this, e);
2395     },
2396     getEl : function()
2397     {
2398         return this.el;
2399     } 
2400 });
2401
2402  
2403
2404  /*
2405  * - LGPL
2406  *
2407  * menu separator
2408  * 
2409  */
2410
2411
2412 /**
2413  * @class Roo.bootstrap.MenuSeparator
2414  * @extends Roo.bootstrap.Component
2415  * Bootstrap MenuSeparator class
2416  * 
2417  * @constructor
2418  * Create a new MenuItem
2419  * @param {Object} config The config object
2420  */
2421
2422
2423 Roo.bootstrap.MenuSeparator = function(config){
2424     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2428     
2429     getAutoCreate : function(){
2430         var cfg = {
2431             cls: 'divider',
2432             tag : 'li'
2433         };
2434         
2435         return cfg;
2436     }
2437    
2438 });
2439
2440  
2441
2442  
2443 /*
2444 * Licence: LGPL
2445 */
2446
2447 /**
2448  * @class Roo.bootstrap.Modal
2449  * @extends Roo.bootstrap.Component
2450  * Bootstrap Modal class
2451  * @cfg {String} title Title of dialog
2452  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2453  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2454  * @cfg {Boolean} specificTitle default false
2455  * @cfg {Array} buttons Array of buttons or standard button set..
2456  * @cfg {String} buttonPosition (left|right|center) default right
2457  * @cfg {Boolean} animate default true
2458  * @cfg {Boolean} allow_close default true
2459  * @cfg {Boolean} fitwindow default true
2460  * 
2461  * 
2462  * @constructor
2463  * Create a new Modal Dialog
2464  * @param {Object} config The config object
2465  */
2466
2467 Roo.bootstrap.Modal = function(config){
2468     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2469     this.addEvents({
2470         // raw events
2471         /**
2472          * @event btnclick
2473          * The raw btnclick event for the button
2474          * @param {Roo.EventObject} e
2475          */
2476         "btnclick" : true
2477     });
2478     this.buttons = this.buttons || [];
2479      
2480     if (this.tmpl) {
2481         this.tmpl = Roo.factory(this.tmpl);
2482     }
2483     
2484 };
2485
2486 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2487     
2488     title : 'test dialog',
2489    
2490     buttons : false,
2491     
2492     // set on load...
2493      
2494     html: false,
2495     
2496     tmp: false,
2497     
2498     specificTitle: false,
2499     
2500     buttonPosition: 'right',
2501     
2502     allow_close : true,
2503     
2504     animate : true,
2505     
2506     fitwindow: false,
2507     
2508     
2509      // private
2510     dialogEl: false,
2511     bodyEl:  false,
2512     footerEl:  false,
2513     titleEl:  false,
2514     closeEl:  false,
2515     
2516     
2517     onRender : function(ct, position)
2518     {
2519         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2520      
2521         if(!this.el){
2522             var cfg = Roo.apply({},  this.getAutoCreate());
2523             cfg.id = Roo.id();
2524             //if(!cfg.name){
2525             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2526             //}
2527             //if (!cfg.name.length) {
2528             //    delete cfg.name;
2529            // }
2530             if (this.cls) {
2531                 cfg.cls += ' ' + this.cls;
2532             }
2533             if (this.style) {
2534                 cfg.style = this.style;
2535             }
2536             this.el = Roo.get(document.body).createChild(cfg, position);
2537         }
2538         //var type = this.el.dom.type;
2539         
2540         
2541         if(this.tabIndex !== undefined){
2542             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2543         }
2544         
2545         this.dialogEl = this.el.select('.modal-dialog',true).first();
2546         this.bodyEl = this.el.select('.modal-body',true).first();
2547         this.closeEl = this.el.select('.modal-header .close', true).first();
2548         this.footerEl = this.el.select('.modal-footer',true).first();
2549         this.titleEl = this.el.select('.modal-title',true).first();
2550         
2551         
2552          
2553         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2554         this.maskEl.enableDisplayMode("block");
2555         this.maskEl.hide();
2556         //this.el.addClass("x-dlg-modal");
2557     
2558         if (this.buttons.length) {
2559             Roo.each(this.buttons, function(bb) {
2560                 var b = Roo.apply({}, bb);
2561                 b.xns = b.xns || Roo.bootstrap;
2562                 b.xtype = b.xtype || 'Button';
2563                 if (typeof(b.listeners) == 'undefined') {
2564                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2565                 }
2566                 
2567                 var btn = Roo.factory(b);
2568                 
2569                 btn.render(this.el.select('.modal-footer div').first());
2570                 
2571             },this);
2572         }
2573         // render the children.
2574         var nitems = [];
2575         
2576         if(typeof(this.items) != 'undefined'){
2577             var items = this.items;
2578             delete this.items;
2579
2580             for(var i =0;i < items.length;i++) {
2581                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2582             }
2583         }
2584         
2585         this.items = nitems;
2586         
2587         // where are these used - they used to be body/close/footer
2588         
2589        
2590         this.initEvents();
2591         //this.el.addClass([this.fieldClass, this.cls]);
2592         
2593     },
2594     
2595     getAutoCreate : function(){
2596         
2597         
2598         var bdy = {
2599                 cls : 'modal-body',
2600                 html : this.html || ''
2601         };
2602         
2603         var title = {
2604             tag: 'h4',
2605             cls : 'modal-title',
2606             html : this.title
2607         };
2608         
2609         if(this.specificTitle){
2610             title = this.title;
2611             
2612         };
2613         
2614         var header = [];
2615         if (this.allow_close) {
2616             header.push({
2617                 tag: 'button',
2618                 cls : 'close',
2619                 html : '&times'
2620             });
2621         }
2622         header.push(title);
2623         
2624         var modal = {
2625             cls: "modal",
2626             style : 'display: none',
2627             cn : [
2628                 {
2629                     cls: "modal-dialog",
2630                     cn : [
2631                         {
2632                             cls : "modal-content",
2633                             cn : [
2634                                 {
2635                                     cls : 'modal-header',
2636                                     cn : header
2637                                 },
2638                                 bdy,
2639                                 {
2640                                     cls : 'modal-footer',
2641                                     cn : [
2642                                         {
2643                                             tag: 'div',
2644                                             cls: 'btn-' + this.buttonPosition
2645                                         }
2646                                     ]
2647                                     
2648                                 }
2649                                 
2650                                 
2651                             ]
2652                             
2653                         }
2654                     ]
2655                         
2656                 }
2657             ]
2658         };
2659         
2660         if(this.animate){
2661             modal.cls += ' fade';
2662         }
2663         
2664         return modal;
2665           
2666     },
2667     getChildContainer : function() {
2668          
2669          return this.bodyEl;
2670         
2671     },
2672     getButtonContainer : function() {
2673          return this.el.select('.modal-footer div',true).first();
2674         
2675     },
2676     initEvents : function()
2677     {
2678         if (this.allow_close) {
2679             this.closeEl.on('click', this.hide, this);
2680         }
2681         Roo.EventManager.onWindowResize(this.resize, this, true);
2682         
2683  
2684     },
2685     
2686     resize : function()
2687     {
2688         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2689         if (this.fitwindow) {
2690             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2691             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2692             this.setSize(w,h)
2693         }
2694     },
2695     
2696     setSize : function(w,h)
2697     {
2698         if (!w && !h) {
2699             return;
2700         }
2701         this.resizeTo(w,h);
2702     },
2703     
2704     show : function() {
2705         
2706         if (!this.rendered) {
2707             this.render();
2708         }
2709         
2710         this.el.setStyle('display', 'block');
2711         
2712         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2713             var _this = this;
2714             (function(){
2715                 this.el.addClass('in');
2716             }).defer(50, this);
2717         }else{
2718             this.el.addClass('in');
2719             
2720         }
2721         
2722         // not sure how we can show data in here.. 
2723         //if (this.tmpl) {
2724         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2725         //}
2726         
2727         Roo.get(document.body).addClass("x-body-masked");
2728         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2729         this.maskEl.show();
2730         this.el.setStyle('zIndex', '10001');
2731        
2732         this.fireEvent('show', this);
2733         this.items.forEach(function(e) {
2734             e.layout ? e.layout() : false;
2735                 
2736         });
2737         this.resize();
2738         
2739         
2740         
2741     },
2742     hide : function()
2743     {
2744         this.maskEl.hide();
2745         Roo.get(document.body).removeClass("x-body-masked");
2746         this.el.removeClass('in');
2747         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2748         
2749         if(this.animate){ // why
2750             var _this = this;
2751             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2752         }else{
2753             this.el.setStyle('display', 'none');
2754         }
2755         
2756         this.fireEvent('hide', this);
2757     },
2758     
2759     addButton : function(str, cb)
2760     {
2761          
2762         
2763         var b = Roo.apply({}, { html : str } );
2764         b.xns = b.xns || Roo.bootstrap;
2765         b.xtype = b.xtype || 'Button';
2766         if (typeof(b.listeners) == 'undefined') {
2767             b.listeners = { click : cb.createDelegate(this)  };
2768         }
2769         
2770         var btn = Roo.factory(b);
2771            
2772         btn.render(this.el.select('.modal-footer div').first());
2773         
2774         return btn;   
2775        
2776     },
2777     
2778     setDefaultButton : function(btn)
2779     {
2780         //this.el.select('.modal-footer').()
2781     },
2782     diff : false,
2783     
2784     resizeTo: function(w,h)
2785     {
2786         // skip.. ?? why??
2787         
2788         this.dialogEl.setWidth(w);
2789         if (this.diff === false) {
2790             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2791         }
2792         
2793         this.bodyEl.setHeight(h-this.diff);
2794         
2795         
2796     },
2797     setContentSize  : function(w, h)
2798     {
2799         
2800     },
2801     onButtonClick: function(btn,e)
2802     {
2803         //Roo.log([a,b,c]);
2804         this.fireEvent('btnclick', btn.name, e);
2805     },
2806      /**
2807      * Set the title of the Dialog
2808      * @param {String} str new Title
2809      */
2810     setTitle: function(str) {
2811         this.titleEl.dom.innerHTML = str;    
2812     },
2813     /**
2814      * Set the body of the Dialog
2815      * @param {String} str new Title
2816      */
2817     setBody: function(str) {
2818         this.bodyEl.dom.innerHTML = str;    
2819     },
2820     /**
2821      * Set the body of the Dialog using the template
2822      * @param {Obj} data - apply this data to the template and replace the body contents.
2823      */
2824     applyBody: function(obj)
2825     {
2826         if (!this.tmpl) {
2827             Roo.log("Error - using apply Body without a template");
2828             //code
2829         }
2830         this.tmpl.overwrite(this.bodyEl, obj);
2831     }
2832     
2833 });
2834
2835
2836 Roo.apply(Roo.bootstrap.Modal,  {
2837     /**
2838          * Button config that displays a single OK button
2839          * @type Object
2840          */
2841         OK :  [{
2842             name : 'ok',
2843             weight : 'primary',
2844             html : 'OK'
2845         }], 
2846         /**
2847          * Button config that displays Yes and No buttons
2848          * @type Object
2849          */
2850         YESNO : [
2851             {
2852                 name  : 'no',
2853                 html : 'No'
2854             },
2855             {
2856                 name  :'yes',
2857                 weight : 'primary',
2858                 html : 'Yes'
2859             }
2860         ],
2861         
2862         /**
2863          * Button config that displays OK and Cancel buttons
2864          * @type Object
2865          */
2866         OKCANCEL : [
2867             {
2868                name : 'cancel',
2869                 html : 'Cancel'
2870             },
2871             {
2872                 name : 'ok',
2873                 weight : 'primary',
2874                 html : 'OK'
2875             }
2876         ],
2877         /**
2878          * Button config that displays Yes, No and Cancel buttons
2879          * @type Object
2880          */
2881         YESNOCANCEL : [
2882             {
2883                 name : 'yes',
2884                 weight : 'primary',
2885                 html : 'Yes'
2886             },
2887             {
2888                 name : 'no',
2889                 html : 'No'
2890             },
2891             {
2892                 name : 'cancel',
2893                 html : 'Cancel'
2894             }
2895         ]
2896 });
2897  
2898  /*
2899  * - LGPL
2900  *
2901  * messagebox - can be used as a replace
2902  * 
2903  */
2904 /**
2905  * @class Roo.MessageBox
2906  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2907  * Example usage:
2908  *<pre><code>
2909 // Basic alert:
2910 Roo.Msg.alert('Status', 'Changes saved successfully.');
2911
2912 // Prompt for user data:
2913 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2914     if (btn == 'ok'){
2915         // process text value...
2916     }
2917 });
2918
2919 // Show a dialog using config options:
2920 Roo.Msg.show({
2921    title:'Save Changes?',
2922    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2923    buttons: Roo.Msg.YESNOCANCEL,
2924    fn: processResult,
2925    animEl: 'elId'
2926 });
2927 </code></pre>
2928  * @singleton
2929  */
2930 Roo.bootstrap.MessageBox = function(){
2931     var dlg, opt, mask, waitTimer;
2932     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2933     var buttons, activeTextEl, bwidth;
2934
2935     
2936     // private
2937     var handleButton = function(button){
2938         dlg.hide();
2939         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2940     };
2941
2942     // private
2943     var handleHide = function(){
2944         if(opt && opt.cls){
2945             dlg.el.removeClass(opt.cls);
2946         }
2947         //if(waitTimer){
2948         //    Roo.TaskMgr.stop(waitTimer);
2949         //    waitTimer = null;
2950         //}
2951     };
2952
2953     // private
2954     var updateButtons = function(b){
2955         var width = 0;
2956         if(!b){
2957             buttons["ok"].hide();
2958             buttons["cancel"].hide();
2959             buttons["yes"].hide();
2960             buttons["no"].hide();
2961             //dlg.footer.dom.style.display = 'none';
2962             return width;
2963         }
2964         dlg.footerEl.dom.style.display = '';
2965         for(var k in buttons){
2966             if(typeof buttons[k] != "function"){
2967                 if(b[k]){
2968                     buttons[k].show();
2969                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2970                     width += buttons[k].el.getWidth()+15;
2971                 }else{
2972                     buttons[k].hide();
2973                 }
2974             }
2975         }
2976         return width;
2977     };
2978
2979     // private
2980     var handleEsc = function(d, k, e){
2981         if(opt && opt.closable !== false){
2982             dlg.hide();
2983         }
2984         if(e){
2985             e.stopEvent();
2986         }
2987     };
2988
2989     return {
2990         /**
2991          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2992          * @return {Roo.BasicDialog} The BasicDialog element
2993          */
2994         getDialog : function(){
2995            if(!dlg){
2996                 dlg = new Roo.bootstrap.Modal( {
2997                     //draggable: true,
2998                     //resizable:false,
2999                     //constraintoviewport:false,
3000                     //fixedcenter:true,
3001                     //collapsible : false,
3002                     //shim:true,
3003                     //modal: true,
3004                   //  width:400,
3005                   //  height:100,
3006                     //buttonAlign:"center",
3007                     closeClick : function(){
3008                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3009                             handleButton("no");
3010                         }else{
3011                             handleButton("cancel");
3012                         }
3013                     }
3014                 });
3015                 dlg.render();
3016                 dlg.on("hide", handleHide);
3017                 mask = dlg.mask;
3018                 //dlg.addKeyListener(27, handleEsc);
3019                 buttons = {};
3020                 this.buttons = buttons;
3021                 var bt = this.buttonText;
3022                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3023                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3024                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3025                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3026                 //Roo.log(buttons);
3027                 bodyEl = dlg.bodyEl.createChild({
3028
3029                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3030                         '<textarea class="roo-mb-textarea"></textarea>' +
3031                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3032                 });
3033                 msgEl = bodyEl.dom.firstChild;
3034                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3035                 textboxEl.enableDisplayMode();
3036                 textboxEl.addKeyListener([10,13], function(){
3037                     if(dlg.isVisible() && opt && opt.buttons){
3038                         if(opt.buttons.ok){
3039                             handleButton("ok");
3040                         }else if(opt.buttons.yes){
3041                             handleButton("yes");
3042                         }
3043                     }
3044                 });
3045                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3046                 textareaEl.enableDisplayMode();
3047                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3048                 progressEl.enableDisplayMode();
3049                 var pf = progressEl.dom.firstChild;
3050                 if (pf) {
3051                     pp = Roo.get(pf.firstChild);
3052                     pp.setHeight(pf.offsetHeight);
3053                 }
3054                 
3055             }
3056             return dlg;
3057         },
3058
3059         /**
3060          * Updates the message box body text
3061          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3062          * the XHTML-compliant non-breaking space character '&amp;#160;')
3063          * @return {Roo.MessageBox} This message box
3064          */
3065         updateText : function(text){
3066             if(!dlg.isVisible() && !opt.width){
3067                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3068             }
3069             msgEl.innerHTML = text || '&#160;';
3070       
3071             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3072             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3073             var w = Math.max(
3074                     Math.min(opt.width || cw , this.maxWidth), 
3075                     Math.max(opt.minWidth || this.minWidth, bwidth)
3076             );
3077             if(opt.prompt){
3078                 activeTextEl.setWidth(w);
3079             }
3080             if(dlg.isVisible()){
3081                 dlg.fixedcenter = false;
3082             }
3083             // to big, make it scroll. = But as usual stupid IE does not support
3084             // !important..
3085             
3086             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3087                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3088                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3089             } else {
3090                 bodyEl.dom.style.height = '';
3091                 bodyEl.dom.style.overflowY = '';
3092             }
3093             if (cw > w) {
3094                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3095             } else {
3096                 bodyEl.dom.style.overflowX = '';
3097             }
3098             
3099             dlg.setContentSize(w, bodyEl.getHeight());
3100             if(dlg.isVisible()){
3101                 dlg.fixedcenter = true;
3102             }
3103             return this;
3104         },
3105
3106         /**
3107          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3108          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3109          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3110          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3111          * @return {Roo.MessageBox} This message box
3112          */
3113         updateProgress : function(value, text){
3114             if(text){
3115                 this.updateText(text);
3116             }
3117             if (pp) { // weird bug on my firefox - for some reason this is not defined
3118                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3119             }
3120             return this;
3121         },        
3122
3123         /**
3124          * Returns true if the message box is currently displayed
3125          * @return {Boolean} True if the message box is visible, else false
3126          */
3127         isVisible : function(){
3128             return dlg && dlg.isVisible();  
3129         },
3130
3131         /**
3132          * Hides the message box if it is displayed
3133          */
3134         hide : function(){
3135             if(this.isVisible()){
3136                 dlg.hide();
3137             }  
3138         },
3139
3140         /**
3141          * Displays a new message box, or reinitializes an existing message box, based on the config options
3142          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3143          * The following config object properties are supported:
3144          * <pre>
3145 Property    Type             Description
3146 ----------  ---------------  ------------------------------------------------------------------------------------
3147 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3148                                    closes (defaults to undefined)
3149 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3150                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3151 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3152                                    progress and wait dialogs will ignore this property and always hide the
3153                                    close button as they can only be closed programmatically.
3154 cls               String           A custom CSS class to apply to the message box element
3155 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3156                                    displayed (defaults to 75)
3157 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3158                                    function will be btn (the name of the button that was clicked, if applicable,
3159                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3160                                    Progress and wait dialogs will ignore this option since they do not respond to
3161                                    user actions and can only be closed programmatically, so any required function
3162                                    should be called by the same code after it closes the dialog.
3163 icon              String           A CSS class that provides a background image to be used as an icon for
3164                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3165 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3166 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3167 modal             Boolean          False to allow user interaction with the page while the message box is
3168                                    displayed (defaults to true)
3169 msg               String           A string that will replace the existing message box body text (defaults
3170                                    to the XHTML-compliant non-breaking space character '&#160;')
3171 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3172 progress          Boolean          True to display a progress bar (defaults to false)
3173 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3174 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3175 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3176 title             String           The title text
3177 value             String           The string value to set into the active textbox element if displayed
3178 wait              Boolean          True to display a progress bar (defaults to false)
3179 width             Number           The width of the dialog in pixels
3180 </pre>
3181          *
3182          * Example usage:
3183          * <pre><code>
3184 Roo.Msg.show({
3185    title: 'Address',
3186    msg: 'Please enter your address:',
3187    width: 300,
3188    buttons: Roo.MessageBox.OKCANCEL,
3189    multiline: true,
3190    fn: saveAddress,
3191    animEl: 'addAddressBtn'
3192 });
3193 </code></pre>
3194          * @param {Object} config Configuration options
3195          * @return {Roo.MessageBox} This message box
3196          */
3197         show : function(options)
3198         {
3199             
3200             // this causes nightmares if you show one dialog after another
3201             // especially on callbacks..
3202              
3203             if(this.isVisible()){
3204                 
3205                 this.hide();
3206                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3207                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3208                 Roo.log("New Dialog Message:" +  options.msg )
3209                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3210                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3211                 
3212             }
3213             var d = this.getDialog();
3214             opt = options;
3215             d.setTitle(opt.title || "&#160;");
3216             d.closeEl.setDisplayed(opt.closable !== false);
3217             activeTextEl = textboxEl;
3218             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3219             if(opt.prompt){
3220                 if(opt.multiline){
3221                     textboxEl.hide();
3222                     textareaEl.show();
3223                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3224                         opt.multiline : this.defaultTextHeight);
3225                     activeTextEl = textareaEl;
3226                 }else{
3227                     textboxEl.show();
3228                     textareaEl.hide();
3229                 }
3230             }else{
3231                 textboxEl.hide();
3232                 textareaEl.hide();
3233             }
3234             progressEl.setDisplayed(opt.progress === true);
3235             this.updateProgress(0);
3236             activeTextEl.dom.value = opt.value || "";
3237             if(opt.prompt){
3238                 dlg.setDefaultButton(activeTextEl);
3239             }else{
3240                 var bs = opt.buttons;
3241                 var db = null;
3242                 if(bs && bs.ok){
3243                     db = buttons["ok"];
3244                 }else if(bs && bs.yes){
3245                     db = buttons["yes"];
3246                 }
3247                 dlg.setDefaultButton(db);
3248             }
3249             bwidth = updateButtons(opt.buttons);
3250             this.updateText(opt.msg);
3251             if(opt.cls){
3252                 d.el.addClass(opt.cls);
3253             }
3254             d.proxyDrag = opt.proxyDrag === true;
3255             d.modal = opt.modal !== false;
3256             d.mask = opt.modal !== false ? mask : false;
3257             if(!d.isVisible()){
3258                 // force it to the end of the z-index stack so it gets a cursor in FF
3259                 document.body.appendChild(dlg.el.dom);
3260                 d.animateTarget = null;
3261                 d.show(options.animEl);
3262             }
3263             return this;
3264         },
3265
3266         /**
3267          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3268          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3269          * and closing the message box when the process is complete.
3270          * @param {String} title The title bar text
3271          * @param {String} msg The message box body text
3272          * @return {Roo.MessageBox} This message box
3273          */
3274         progress : function(title, msg){
3275             this.show({
3276                 title : title,
3277                 msg : msg,
3278                 buttons: false,
3279                 progress:true,
3280                 closable:false,
3281                 minWidth: this.minProgressWidth,
3282                 modal : true
3283             });
3284             return this;
3285         },
3286
3287         /**
3288          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3289          * If a callback function is passed it will be called after the user clicks the button, and the
3290          * id of the button that was clicked will be passed as the only parameter to the callback
3291          * (could also be the top-right close button).
3292          * @param {String} title The title bar text
3293          * @param {String} msg The message box body text
3294          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3295          * @param {Object} scope (optional) The scope of the callback function
3296          * @return {Roo.MessageBox} This message box
3297          */
3298         alert : function(title, msg, fn, scope){
3299             this.show({
3300                 title : title,
3301                 msg : msg,
3302                 buttons: this.OK,
3303                 fn: fn,
3304                 scope : scope,
3305                 modal : true
3306             });
3307             return this;
3308         },
3309
3310         /**
3311          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3312          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3313          * You are responsible for closing the message box when the process is complete.
3314          * @param {String} msg The message box body text
3315          * @param {String} title (optional) The title bar text
3316          * @return {Roo.MessageBox} This message box
3317          */
3318         wait : function(msg, title){
3319             this.show({
3320                 title : title,
3321                 msg : msg,
3322                 buttons: false,
3323                 closable:false,
3324                 progress:true,
3325                 modal:true,
3326                 width:300,
3327                 wait:true
3328             });
3329             waitTimer = Roo.TaskMgr.start({
3330                 run: function(i){
3331                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3332                 },
3333                 interval: 1000
3334             });
3335             return this;
3336         },
3337
3338         /**
3339          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3340          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3341          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3342          * @param {String} title The title bar text
3343          * @param {String} msg The message box body text
3344          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3345          * @param {Object} scope (optional) The scope of the callback function
3346          * @return {Roo.MessageBox} This message box
3347          */
3348         confirm : function(title, msg, fn, scope){
3349             this.show({
3350                 title : title,
3351                 msg : msg,
3352                 buttons: this.YESNO,
3353                 fn: fn,
3354                 scope : scope,
3355                 modal : true
3356             });
3357             return this;
3358         },
3359
3360         /**
3361          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3362          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3363          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3364          * (could also be the top-right close button) and the text that was entered will be passed as the two
3365          * parameters to the callback.
3366          * @param {String} title The title bar text
3367          * @param {String} msg The message box body text
3368          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3369          * @param {Object} scope (optional) The scope of the callback function
3370          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3371          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3372          * @return {Roo.MessageBox} This message box
3373          */
3374         prompt : function(title, msg, fn, scope, multiline){
3375             this.show({
3376                 title : title,
3377                 msg : msg,
3378                 buttons: this.OKCANCEL,
3379                 fn: fn,
3380                 minWidth:250,
3381                 scope : scope,
3382                 prompt:true,
3383                 multiline: multiline,
3384                 modal : true
3385             });
3386             return this;
3387         },
3388
3389         /**
3390          * Button config that displays a single OK button
3391          * @type Object
3392          */
3393         OK : {ok:true},
3394         /**
3395          * Button config that displays Yes and No buttons
3396          * @type Object
3397          */
3398         YESNO : {yes:true, no:true},
3399         /**
3400          * Button config that displays OK and Cancel buttons
3401          * @type Object
3402          */
3403         OKCANCEL : {ok:true, cancel:true},
3404         /**
3405          * Button config that displays Yes, No and Cancel buttons
3406          * @type Object
3407          */
3408         YESNOCANCEL : {yes:true, no:true, cancel:true},
3409
3410         /**
3411          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3412          * @type Number
3413          */
3414         defaultTextHeight : 75,
3415         /**
3416          * The maximum width in pixels of the message box (defaults to 600)
3417          * @type Number
3418          */
3419         maxWidth : 600,
3420         /**
3421          * The minimum width in pixels of the message box (defaults to 100)
3422          * @type Number
3423          */
3424         minWidth : 100,
3425         /**
3426          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3427          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3428          * @type Number
3429          */
3430         minProgressWidth : 250,
3431         /**
3432          * An object containing the default button text strings that can be overriden for localized language support.
3433          * Supported properties are: ok, cancel, yes and no.
3434          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3435          * @type Object
3436          */
3437         buttonText : {
3438             ok : "OK",
3439             cancel : "Cancel",
3440             yes : "Yes",
3441             no : "No"
3442         }
3443     };
3444 }();
3445
3446 /**
3447  * Shorthand for {@link Roo.MessageBox}
3448  */
3449 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3450 Roo.Msg = Roo.Msg || Roo.MessageBox;
3451 /*
3452  * - LGPL
3453  *
3454  * navbar
3455  * 
3456  */
3457
3458 /**
3459  * @class Roo.bootstrap.Navbar
3460  * @extends Roo.bootstrap.Component
3461  * Bootstrap Navbar class
3462
3463  * @constructor
3464  * Create a new Navbar
3465  * @param {Object} config The config object
3466  */
3467
3468
3469 Roo.bootstrap.Navbar = function(config){
3470     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3471     this.addEvents({
3472         // raw events
3473         /**
3474          * @event beforetoggle
3475          * Fire before toggle the menu
3476          * @param {Roo.EventObject} e
3477          */
3478         "beforetoggle" : true
3479     });
3480 };
3481
3482 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3483     
3484     
3485    
3486     // private
3487     navItems : false,
3488     loadMask : false,
3489     
3490     
3491     getAutoCreate : function(){
3492         
3493         
3494         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3495         
3496     },
3497     
3498     initEvents :function ()
3499     {
3500         //Roo.log(this.el.select('.navbar-toggle',true));
3501         this.el.select('.navbar-toggle',true).on('click', function() {
3502             if(this.fireEvent('beforetoggle', this) !== false){
3503                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3504             }
3505             
3506         }, this);
3507         
3508         var mark = {
3509             tag: "div",
3510             cls:"x-dlg-mask"
3511         };
3512         
3513         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3514         
3515         var size = this.el.getSize();
3516         this.maskEl.setSize(size.width, size.height);
3517         this.maskEl.enableDisplayMode("block");
3518         this.maskEl.hide();
3519         
3520         if(this.loadMask){
3521             this.maskEl.show();
3522         }
3523     },
3524     
3525     
3526     getChildContainer : function()
3527     {
3528         if (this.el.select('.collapse').getCount()) {
3529             return this.el.select('.collapse',true).first();
3530         }
3531         
3532         return this.el;
3533     },
3534     
3535     mask : function()
3536     {
3537         this.maskEl.show();
3538     },
3539     
3540     unmask : function()
3541     {
3542         this.maskEl.hide();
3543     } 
3544     
3545     
3546     
3547     
3548 });
3549
3550
3551
3552  
3553
3554  /*
3555  * - LGPL
3556  *
3557  * navbar
3558  * 
3559  */
3560
3561 /**
3562  * @class Roo.bootstrap.NavSimplebar
3563  * @extends Roo.bootstrap.Navbar
3564  * Bootstrap Sidebar class
3565  *
3566  * @cfg {Boolean} inverse is inverted color
3567  * 
3568  * @cfg {String} type (nav | pills | tabs)
3569  * @cfg {Boolean} arrangement stacked | justified
3570  * @cfg {String} align (left | right) alignment
3571  * 
3572  * @cfg {Boolean} main (true|false) main nav bar? default false
3573  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3574  * 
3575  * @cfg {String} tag (header|footer|nav|div) default is nav 
3576
3577  * 
3578  * 
3579  * 
3580  * @constructor
3581  * Create a new Sidebar
3582  * @param {Object} config The config object
3583  */
3584
3585
3586 Roo.bootstrap.NavSimplebar = function(config){
3587     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3588 };
3589
3590 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3591     
3592     inverse: false,
3593     
3594     type: false,
3595     arrangement: '',
3596     align : false,
3597     
3598     
3599     
3600     main : false,
3601     
3602     
3603     tag : false,
3604     
3605     
3606     getAutoCreate : function(){
3607         
3608         
3609         var cfg = {
3610             tag : this.tag || 'div',
3611             cls : 'navbar'
3612         };
3613           
3614         
3615         cfg.cn = [
3616             {
3617                 cls: 'nav',
3618                 tag : 'ul'
3619             }
3620         ];
3621         
3622          
3623         this.type = this.type || 'nav';
3624         if (['tabs','pills'].indexOf(this.type)!==-1) {
3625             cfg.cn[0].cls += ' nav-' + this.type
3626         
3627         
3628         } else {
3629             if (this.type!=='nav') {
3630                 Roo.log('nav type must be nav/tabs/pills')
3631             }
3632             cfg.cn[0].cls += ' navbar-nav'
3633         }
3634         
3635         
3636         
3637         
3638         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3639             cfg.cn[0].cls += ' nav-' + this.arrangement;
3640         }
3641         
3642         
3643         if (this.align === 'right') {
3644             cfg.cn[0].cls += ' navbar-right';
3645         }
3646         
3647         if (this.inverse) {
3648             cfg.cls += ' navbar-inverse';
3649             
3650         }
3651         
3652         
3653         return cfg;
3654     
3655         
3656     }
3657     
3658     
3659     
3660 });
3661
3662
3663
3664  
3665
3666  
3667        /*
3668  * - LGPL
3669  *
3670  * navbar
3671  * 
3672  */
3673
3674 /**
3675  * @class Roo.bootstrap.NavHeaderbar
3676  * @extends Roo.bootstrap.NavSimplebar
3677  * Bootstrap Sidebar class
3678  *
3679  * @cfg {String} brand what is brand
3680  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3681  * @cfg {String} brand_href href of the brand
3682  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3683  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3684  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3685  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3686  * 
3687  * @constructor
3688  * Create a new Sidebar
3689  * @param {Object} config The config object
3690  */
3691
3692
3693 Roo.bootstrap.NavHeaderbar = function(config){
3694     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3695       
3696 };
3697
3698 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3699     
3700     position: '',
3701     brand: '',
3702     brand_href: false,
3703     srButton : true,
3704     autohide : false,
3705     desktopCenter : false,
3706    
3707     
3708     getAutoCreate : function(){
3709         
3710         var   cfg = {
3711             tag: this.nav || 'nav',
3712             cls: 'navbar',
3713             role: 'navigation',
3714             cn: []
3715         };
3716         
3717         var cn = cfg.cn;
3718         if (this.desktopCenter) {
3719             cn.push({cls : 'container', cn : []});
3720             cn = cn[0].cn;
3721         }
3722         
3723         if(this.srButton){
3724             cn.push({
3725                 tag: 'div',
3726                 cls: 'navbar-header',
3727                 cn: [
3728                     {
3729                         tag: 'button',
3730                         type: 'button',
3731                         cls: 'navbar-toggle',
3732                         'data-toggle': 'collapse',
3733                         cn: [
3734                             {
3735                                 tag: 'span',
3736                                 cls: 'sr-only',
3737                                 html: 'Toggle navigation'
3738                             },
3739                             {
3740                                 tag: 'span',
3741                                 cls: 'icon-bar'
3742                             },
3743                             {
3744                                 tag: 'span',
3745                                 cls: 'icon-bar'
3746                             },
3747                             {
3748                                 tag: 'span',
3749                                 cls: 'icon-bar'
3750                             }
3751                         ]
3752                     }
3753                 ]
3754             });
3755         }
3756         
3757         cn.push({
3758             tag: 'div',
3759             cls: 'collapse navbar-collapse',
3760             cn : []
3761         });
3762         
3763         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3764         
3765         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3766             cfg.cls += ' navbar-' + this.position;
3767             
3768             // tag can override this..
3769             
3770             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3771         }
3772         
3773         if (this.brand !== '') {
3774             cn[0].cn.push({
3775                 tag: 'a',
3776                 href: this.brand_href ? this.brand_href : '#',
3777                 cls: 'navbar-brand',
3778                 cn: [
3779                 this.brand
3780                 ]
3781             });
3782         }
3783         
3784         if(this.main){
3785             cfg.cls += ' main-nav';
3786         }
3787         
3788         
3789         return cfg;
3790
3791         
3792     },
3793     getHeaderChildContainer : function()
3794     {
3795         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3796             return this.el.select('.navbar-header',true).first();
3797         }
3798         
3799         return this.getChildContainer();
3800     },
3801     
3802     
3803     initEvents : function()
3804     {
3805         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3806         
3807         if (this.autohide) {
3808             
3809             var prevScroll = 0;
3810             var ft = this.el;
3811             
3812             Roo.get(document).on('scroll',function(e) {
3813                 var ns = Roo.get(document).getScroll().top;
3814                 var os = prevScroll;
3815                 prevScroll = ns;
3816                 
3817                 if(ns > os){
3818                     ft.removeClass('slideDown');
3819                     ft.addClass('slideUp');
3820                     return;
3821                 }
3822                 ft.removeClass('slideUp');
3823                 ft.addClass('slideDown');
3824                  
3825               
3826           },this);
3827         }
3828     }    
3829     
3830 });
3831
3832
3833
3834  
3835
3836  /*
3837  * - LGPL
3838  *
3839  * navbar
3840  * 
3841  */
3842
3843 /**
3844  * @class Roo.bootstrap.NavSidebar
3845  * @extends Roo.bootstrap.Navbar
3846  * Bootstrap Sidebar class
3847  * 
3848  * @constructor
3849  * Create a new Sidebar
3850  * @param {Object} config The config object
3851  */
3852
3853
3854 Roo.bootstrap.NavSidebar = function(config){
3855     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3856 };
3857
3858 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3859     
3860     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3861     
3862     getAutoCreate : function(){
3863         
3864         
3865         return  {
3866             tag: 'div',
3867             cls: 'sidebar sidebar-nav'
3868         };
3869     
3870         
3871     }
3872     
3873     
3874     
3875 });
3876
3877
3878
3879  
3880
3881  /*
3882  * - LGPL
3883  *
3884  * nav group
3885  * 
3886  */
3887
3888 /**
3889  * @class Roo.bootstrap.NavGroup
3890  * @extends Roo.bootstrap.Component
3891  * Bootstrap NavGroup class
3892  * @cfg {String} align (left|right)
3893  * @cfg {Boolean} inverse
3894  * @cfg {String} type (nav|pills|tab) default nav
3895  * @cfg {String} navId - reference Id for navbar.
3896
3897  * 
3898  * @constructor
3899  * Create a new nav group
3900  * @param {Object} config The config object
3901  */
3902
3903 Roo.bootstrap.NavGroup = function(config){
3904     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3905     this.navItems = [];
3906    
3907     Roo.bootstrap.NavGroup.register(this);
3908      this.addEvents({
3909         /**
3910              * @event changed
3911              * Fires when the active item changes
3912              * @param {Roo.bootstrap.NavGroup} this
3913              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3914              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3915          */
3916         'changed': true
3917      });
3918     
3919 };
3920
3921 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3922     
3923     align: '',
3924     inverse: false,
3925     form: false,
3926     type: 'nav',
3927     navId : '',
3928     // private
3929     
3930     navItems : false, 
3931     
3932     getAutoCreate : function()
3933     {
3934         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3935         
3936         cfg = {
3937             tag : 'ul',
3938             cls: 'nav' 
3939         };
3940         
3941         if (['tabs','pills'].indexOf(this.type)!==-1) {
3942             cfg.cls += ' nav-' + this.type
3943         } else {
3944             if (this.type!=='nav') {
3945                 Roo.log('nav type must be nav/tabs/pills')
3946             }
3947             cfg.cls += ' navbar-nav'
3948         }
3949         
3950         if (this.parent().sidebar) {
3951             cfg = {
3952                 tag: 'ul',
3953                 cls: 'dashboard-menu sidebar-menu'
3954             };
3955             
3956             return cfg;
3957         }
3958         
3959         if (this.form === true) {
3960             cfg = {
3961                 tag: 'form',
3962                 cls: 'navbar-form'
3963             };
3964             
3965             if (this.align === 'right') {
3966                 cfg.cls += ' navbar-right';
3967             } else {
3968                 cfg.cls += ' navbar-left';
3969             }
3970         }
3971         
3972         if (this.align === 'right') {
3973             cfg.cls += ' navbar-right';
3974         }
3975         
3976         if (this.inverse) {
3977             cfg.cls += ' navbar-inverse';
3978             
3979         }
3980         
3981         
3982         return cfg;
3983     },
3984     /**
3985     * sets the active Navigation item
3986     * @param {Roo.bootstrap.NavItem} the new current navitem
3987     */
3988     setActiveItem : function(item)
3989     {
3990         var prev = false;
3991         Roo.each(this.navItems, function(v){
3992             if (v == item) {
3993                 return ;
3994             }
3995             if (v.isActive()) {
3996                 v.setActive(false, true);
3997                 prev = v;
3998                 
3999             }
4000             
4001         });
4002
4003         item.setActive(true, true);
4004         this.fireEvent('changed', this, item, prev);
4005         
4006         
4007     },
4008     /**
4009     * gets the active Navigation item
4010     * @return {Roo.bootstrap.NavItem} the current navitem
4011     */
4012     getActive : function()
4013     {
4014         
4015         var prev = false;
4016         Roo.each(this.navItems, function(v){
4017             
4018             if (v.isActive()) {
4019                 prev = v;
4020                 
4021             }
4022             
4023         });
4024         return prev;
4025     },
4026     
4027     indexOfNav : function()
4028     {
4029         
4030         var prev = false;
4031         Roo.each(this.navItems, function(v,i){
4032             
4033             if (v.isActive()) {
4034                 prev = i;
4035                 
4036             }
4037             
4038         });
4039         return prev;
4040     },
4041     /**
4042     * adds a Navigation item
4043     * @param {Roo.bootstrap.NavItem} the navitem to add
4044     */
4045     addItem : function(cfg)
4046     {
4047         var cn = new Roo.bootstrap.NavItem(cfg);
4048         this.register(cn);
4049         cn.parentId = this.id;
4050         cn.onRender(this.el, null);
4051         return cn;
4052     },
4053     /**
4054     * register a Navigation item
4055     * @param {Roo.bootstrap.NavItem} the navitem to add
4056     */
4057     register : function(item)
4058     {
4059         this.navItems.push( item);
4060         item.navId = this.navId;
4061     
4062     },
4063     
4064     /**
4065     * clear all the Navigation item
4066     */
4067    
4068     clearAll : function()
4069     {
4070         this.navItems = [];
4071         this.el.dom.innerHTML = '';
4072     },
4073     
4074     getNavItem: function(tabId)
4075     {
4076         var ret = false;
4077         Roo.each(this.navItems, function(e) {
4078             if (e.tabId == tabId) {
4079                ret =  e;
4080                return false;
4081             }
4082             return true;
4083             
4084         });
4085         return ret;
4086     },
4087     
4088     setActiveNext : function()
4089     {
4090         var i = this.indexOfNav(this.getActive());
4091         if (i > this.navItems.length) {
4092             return;
4093         }
4094         this.setActiveItem(this.navItems[i+1]);
4095     },
4096     setActivePrev : function()
4097     {
4098         var i = this.indexOfNav(this.getActive());
4099         if (i  < 1) {
4100             return;
4101         }
4102         this.setActiveItem(this.navItems[i-1]);
4103     },
4104     clearWasActive : function(except) {
4105         Roo.each(this.navItems, function(e) {
4106             if (e.tabId != except.tabId && e.was_active) {
4107                e.was_active = false;
4108                return false;
4109             }
4110             return true;
4111             
4112         });
4113     },
4114     getWasActive : function ()
4115     {
4116         var r = false;
4117         Roo.each(this.navItems, function(e) {
4118             if (e.was_active) {
4119                r = e;
4120                return false;
4121             }
4122             return true;
4123             
4124         });
4125         return r;
4126     }
4127     
4128     
4129 });
4130
4131  
4132 Roo.apply(Roo.bootstrap.NavGroup, {
4133     
4134     groups: {},
4135      /**
4136     * register a Navigation Group
4137     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4138     */
4139     register : function(navgrp)
4140     {
4141         this.groups[navgrp.navId] = navgrp;
4142         
4143     },
4144     /**
4145     * fetch a Navigation Group based on the navigation ID
4146     * @param {string} the navgroup to add
4147     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4148     */
4149     get: function(navId) {
4150         if (typeof(this.groups[navId]) == 'undefined') {
4151             return false;
4152             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4153         }
4154         return this.groups[navId] ;
4155     }
4156     
4157     
4158     
4159 });
4160
4161  /*
4162  * - LGPL
4163  *
4164  * row
4165  * 
4166  */
4167
4168 /**
4169  * @class Roo.bootstrap.NavItem
4170  * @extends Roo.bootstrap.Component
4171  * Bootstrap Navbar.NavItem class
4172  * @cfg {String} href  link to
4173  * @cfg {String} html content of button
4174  * @cfg {String} badge text inside badge
4175  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4176  * @cfg {String} glyphicon name of glyphicon
4177  * @cfg {String} icon name of font awesome icon
4178  * @cfg {Boolean} active Is item active
4179  * @cfg {Boolean} disabled Is item disabled
4180  
4181  * @cfg {Boolean} preventDefault (true | false) default false
4182  * @cfg {String} tabId the tab that this item activates.
4183  * @cfg {String} tagtype (a|span) render as a href or span?
4184  * @cfg {Boolean} animateRef (true|false) link to element default false  
4185   
4186  * @constructor
4187  * Create a new Navbar Item
4188  * @param {Object} config The config object
4189  */
4190 Roo.bootstrap.NavItem = function(config){
4191     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4192     this.addEvents({
4193         // raw events
4194         /**
4195          * @event click
4196          * The raw click event for the entire grid.
4197          * @param {Roo.EventObject} e
4198          */
4199         "click" : true,
4200          /**
4201             * @event changed
4202             * Fires when the active item active state changes
4203             * @param {Roo.bootstrap.NavItem} this
4204             * @param {boolean} state the new state
4205              
4206          */
4207         'changed': true,
4208         /**
4209             * @event scrollto
4210             * Fires when scroll to element
4211             * @param {Roo.bootstrap.NavItem} this
4212             * @param {Object} options
4213             * @param {Roo.EventObject} e
4214              
4215          */
4216         'scrollto': true
4217     });
4218    
4219 };
4220
4221 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4222     
4223     href: false,
4224     html: '',
4225     badge: '',
4226     icon: false,
4227     glyphicon: false,
4228     active: false,
4229     preventDefault : false,
4230     tabId : false,
4231     tagtype : 'a',
4232     disabled : false,
4233     animateRef : false,
4234     was_active : false,
4235     
4236     getAutoCreate : function(){
4237          
4238         var cfg = {
4239             tag: 'li',
4240             cls: 'nav-item'
4241             
4242         };
4243         
4244         if (this.active) {
4245             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4246         }
4247         if (this.disabled) {
4248             cfg.cls += ' disabled';
4249         }
4250         
4251         if (this.href || this.html || this.glyphicon || this.icon) {
4252             cfg.cn = [
4253                 {
4254                     tag: this.tagtype,
4255                     href : this.href || "#",
4256                     html: this.html || ''
4257                 }
4258             ];
4259             
4260             if (this.icon) {
4261                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4262             }
4263
4264             if(this.glyphicon) {
4265                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4266             }
4267             
4268             if (this.menu) {
4269                 
4270                 cfg.cn[0].html += " <span class='caret'></span>";
4271              
4272             }
4273             
4274             if (this.badge !== '') {
4275                  
4276                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4277             }
4278         }
4279         
4280         
4281         
4282         return cfg;
4283     },
4284     initEvents: function() 
4285     {
4286         if (typeof (this.menu) != 'undefined') {
4287             this.menu.parentType = this.xtype;
4288             this.menu.triggerEl = this.el;
4289             this.menu = this.addxtype(Roo.apply({}, this.menu));
4290         }
4291         
4292         this.el.select('a',true).on('click', this.onClick, this);
4293         
4294         if(this.tagtype == 'span'){
4295             this.el.select('span',true).on('click', this.onClick, this);
4296         }
4297        
4298         // at this point parent should be available..
4299         this.parent().register(this);
4300     },
4301     
4302     onClick : function(e)
4303     {
4304         if (e.getTarget('.dropdown-menu-item')) {
4305             // did you click on a menu itemm.... - then don't trigger onclick..
4306             return;
4307         }
4308         
4309         if(
4310                 this.preventDefault || 
4311                 this.href == '#' 
4312         ){
4313             Roo.log("NavItem - prevent Default?");
4314             e.preventDefault();
4315         }
4316         
4317         if (this.disabled) {
4318             return;
4319         }
4320         
4321         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4322         if (tg && tg.transition) {
4323             Roo.log("waiting for the transitionend");
4324             return;
4325         }
4326         
4327         
4328         
4329         //Roo.log("fire event clicked");
4330         if(this.fireEvent('click', this, e) === false){
4331             return;
4332         };
4333         
4334         if(this.tagtype == 'span'){
4335             return;
4336         }
4337         
4338         //Roo.log(this.href);
4339         var ael = this.el.select('a',true).first();
4340         //Roo.log(ael);
4341         
4342         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4343             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4344             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4345                 return; // ignore... - it's a 'hash' to another page.
4346             }
4347             Roo.log("NavItem - prevent Default?");
4348             e.preventDefault();
4349             this.scrollToElement(e);
4350         }
4351         
4352         
4353         var p =  this.parent();
4354    
4355         if (['tabs','pills'].indexOf(p.type)!==-1) {
4356             if (typeof(p.setActiveItem) !== 'undefined') {
4357                 p.setActiveItem(this);
4358             }
4359         }
4360         
4361         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4362         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4363             // remove the collapsed menu expand...
4364             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4365         }
4366     },
4367     
4368     isActive: function () {
4369         return this.active
4370     },
4371     setActive : function(state, fire, is_was_active)
4372     {
4373         if (this.active && !state && this.navId) {
4374             this.was_active = true;
4375             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4376             if (nv) {
4377                 nv.clearWasActive(this);
4378             }
4379             
4380         }
4381         this.active = state;
4382         
4383         if (!state ) {
4384             this.el.removeClass('active');
4385         } else if (!this.el.hasClass('active')) {
4386             this.el.addClass('active');
4387         }
4388         if (fire) {
4389             this.fireEvent('changed', this, state);
4390         }
4391         
4392         // show a panel if it's registered and related..
4393         
4394         if (!this.navId || !this.tabId || !state || is_was_active) {
4395             return;
4396         }
4397         
4398         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4399         if (!tg) {
4400             return;
4401         }
4402         var pan = tg.getPanelByName(this.tabId);
4403         if (!pan) {
4404             return;
4405         }
4406         // if we can not flip to new panel - go back to old nav highlight..
4407         if (false == tg.showPanel(pan)) {
4408             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4409             if (nv) {
4410                 var onav = nv.getWasActive();
4411                 if (onav) {
4412                     onav.setActive(true, false, true);
4413                 }
4414             }
4415             
4416         }
4417         
4418         
4419         
4420     },
4421      // this should not be here...
4422     setDisabled : function(state)
4423     {
4424         this.disabled = state;
4425         if (!state ) {
4426             this.el.removeClass('disabled');
4427         } else if (!this.el.hasClass('disabled')) {
4428             this.el.addClass('disabled');
4429         }
4430         
4431     },
4432     
4433     /**
4434      * Fetch the element to display the tooltip on.
4435      * @return {Roo.Element} defaults to this.el
4436      */
4437     tooltipEl : function()
4438     {
4439         return this.el.select('' + this.tagtype + '', true).first();
4440     },
4441     
4442     scrollToElement : function(e)
4443     {
4444         var c = document.body;
4445         
4446         /*
4447          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4448          */
4449         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4450             c = document.documentElement;
4451         }
4452         
4453         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4454         
4455         if(!target){
4456             return;
4457         }
4458
4459         var o = target.calcOffsetsTo(c);
4460         
4461         var options = {
4462             target : target,
4463             value : o[1]
4464         };
4465         
4466         this.fireEvent('scrollto', this, options, e);
4467         
4468         Roo.get(c).scrollTo('top', options.value, true);
4469         
4470         return;
4471     }
4472 });
4473  
4474
4475  /*
4476  * - LGPL
4477  *
4478  * sidebar item
4479  *
4480  *  li
4481  *    <span> icon </span>
4482  *    <span> text </span>
4483  *    <span>badge </span>
4484  */
4485
4486 /**
4487  * @class Roo.bootstrap.NavSidebarItem
4488  * @extends Roo.bootstrap.NavItem
4489  * Bootstrap Navbar.NavSidebarItem class
4490  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4491  * {bool} open is the menu open
4492  * @constructor
4493  * Create a new Navbar Button
4494  * @param {Object} config The config object
4495  */
4496 Roo.bootstrap.NavSidebarItem = function(config){
4497     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4498     this.addEvents({
4499         // raw events
4500         /**
4501          * @event click
4502          * The raw click event for the entire grid.
4503          * @param {Roo.EventObject} e
4504          */
4505         "click" : true,
4506          /**
4507             * @event changed
4508             * Fires when the active item active state changes
4509             * @param {Roo.bootstrap.NavSidebarItem} this
4510             * @param {boolean} state the new state
4511              
4512          */
4513         'changed': true
4514     });
4515    
4516 };
4517
4518 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4519     
4520     badgeWeight : 'default',
4521     
4522     open: false,
4523     
4524     getAutoCreate : function(){
4525         
4526         
4527         var a = {
4528                 tag: 'a',
4529                 href : this.href || '#',
4530                 cls: '',
4531                 html : '',
4532                 cn : []
4533         };
4534         var cfg = {
4535             tag: 'li',
4536             cls: '',
4537             cn: [ a ]
4538         };
4539         var span = {
4540             tag: 'span',
4541             html : this.html || ''
4542         };
4543         
4544         
4545         if (this.active) {
4546             cfg.cls += ' active';
4547         }
4548         
4549         if (this.disabled) {
4550             cfg.cls += ' disabled';
4551         }
4552         if (this.open) {
4553             cfg.cls += ' open x-open';
4554         }
4555         // left icon..
4556         if (this.glyphicon || this.icon) {
4557             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4558             a.cn.push({ tag : 'i', cls : c }) ;
4559         }
4560         // html..
4561         a.cn.push(span);
4562         // then badge..
4563         if (this.badge !== '') {
4564             
4565             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4566         }
4567         // fi
4568         if (this.menu) {
4569             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4570             a.cls += 'dropdown-toggle treeview' ;
4571         }
4572         
4573         return cfg;
4574          
4575            
4576     },
4577     
4578     initEvents : function()
4579     { 
4580         if (typeof (this.menu) != 'undefined') {
4581             this.menu.parentType = this.xtype;
4582             this.menu.triggerEl = this.el;
4583             this.menu = this.addxtype(Roo.apply({}, this.menu));
4584         }
4585         
4586         this.el.on('click', this.onClick, this);
4587        
4588     
4589         if(this.badge !== ''){
4590  
4591             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4592         }
4593         
4594     },
4595     
4596     onClick : function(e)
4597     {
4598         if(this.disabled){
4599             e.preventDefault();
4600             return;
4601         }
4602         
4603         if(this.preventDefault){
4604             e.preventDefault();
4605         }
4606         
4607         this.fireEvent('click', this);
4608     },
4609     
4610     disable : function()
4611     {
4612         this.setDisabled(true);
4613     },
4614     
4615     enable : function()
4616     {
4617         this.setDisabled(false);
4618     },
4619     
4620     setDisabled : function(state)
4621     {
4622         if(this.disabled == state){
4623             return;
4624         }
4625         
4626         this.disabled = state;
4627         
4628         if (state) {
4629             this.el.addClass('disabled');
4630             return;
4631         }
4632         
4633         this.el.removeClass('disabled');
4634         
4635         return;
4636     },
4637     
4638     setActive : function(state)
4639     {
4640         if(this.active == state){
4641             return;
4642         }
4643         
4644         this.active = state;
4645         
4646         if (state) {
4647             this.el.addClass('active');
4648             return;
4649         }
4650         
4651         this.el.removeClass('active');
4652         
4653         return;
4654     },
4655     
4656     isActive: function () 
4657     {
4658         return this.active;
4659     },
4660     
4661     setBadge : function(str)
4662     {
4663         if(!this.badgeEl){
4664             return;
4665         }
4666         
4667         this.badgeEl.dom.innerHTML = str;
4668     }
4669     
4670    
4671      
4672  
4673 });
4674  
4675
4676  /*
4677  * - LGPL
4678  *
4679  * row
4680  * 
4681  */
4682
4683 /**
4684  * @class Roo.bootstrap.Row
4685  * @extends Roo.bootstrap.Component
4686  * Bootstrap Row class (contains columns...)
4687  * 
4688  * @constructor
4689  * Create a new Row
4690  * @param {Object} config The config object
4691  */
4692
4693 Roo.bootstrap.Row = function(config){
4694     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4695 };
4696
4697 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4698     
4699     getAutoCreate : function(){
4700        return {
4701             cls: 'row clearfix'
4702        };
4703     }
4704     
4705     
4706 });
4707
4708  
4709
4710  /*
4711  * - LGPL
4712  *
4713  * element
4714  * 
4715  */
4716
4717 /**
4718  * @class Roo.bootstrap.Element
4719  * @extends Roo.bootstrap.Component
4720  * Bootstrap Element class
4721  * @cfg {String} html contents of the element
4722  * @cfg {String} tag tag of the element
4723  * @cfg {String} cls class of the element
4724  * @cfg {Boolean} preventDefault (true|false) default false
4725  * @cfg {Boolean} clickable (true|false) default false
4726  * 
4727  * @constructor
4728  * Create a new Element
4729  * @param {Object} config The config object
4730  */
4731
4732 Roo.bootstrap.Element = function(config){
4733     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4734     
4735     this.addEvents({
4736         // raw events
4737         /**
4738          * @event click
4739          * When a element is chick
4740          * @param {Roo.bootstrap.Element} this
4741          * @param {Roo.EventObject} e
4742          */
4743         "click" : true
4744     });
4745 };
4746
4747 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4748     
4749     tag: 'div',
4750     cls: '',
4751     html: '',
4752     preventDefault: false, 
4753     clickable: false,
4754     
4755     getAutoCreate : function(){
4756         
4757         var cfg = {
4758             tag: this.tag,
4759             cls: this.cls,
4760             html: this.html
4761         };
4762         
4763         return cfg;
4764     },
4765     
4766     initEvents: function() 
4767     {
4768         Roo.bootstrap.Element.superclass.initEvents.call(this);
4769         
4770         if(this.clickable){
4771             this.el.on('click', this.onClick, this);
4772         }
4773         
4774     },
4775     
4776     onClick : function(e)
4777     {
4778         if(this.preventDefault){
4779             e.preventDefault();
4780         }
4781         
4782         this.fireEvent('click', this, e);
4783     },
4784     
4785     getValue : function()
4786     {
4787         return this.el.dom.innerHTML;
4788     },
4789     
4790     setValue : function(value)
4791     {
4792         this.el.dom.innerHTML = value;
4793     }
4794    
4795 });
4796
4797  
4798
4799  /*
4800  * - LGPL
4801  *
4802  * pagination
4803  * 
4804  */
4805
4806 /**
4807  * @class Roo.bootstrap.Pagination
4808  * @extends Roo.bootstrap.Component
4809  * Bootstrap Pagination class
4810  * @cfg {String} size xs | sm | md | lg
4811  * @cfg {Boolean} inverse false | true
4812  * 
4813  * @constructor
4814  * Create a new Pagination
4815  * @param {Object} config The config object
4816  */
4817
4818 Roo.bootstrap.Pagination = function(config){
4819     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4820 };
4821
4822 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4823     
4824     cls: false,
4825     size: false,
4826     inverse: false,
4827     
4828     getAutoCreate : function(){
4829         var cfg = {
4830             tag: 'ul',
4831                 cls: 'pagination'
4832         };
4833         if (this.inverse) {
4834             cfg.cls += ' inverse';
4835         }
4836         if (this.html) {
4837             cfg.html=this.html;
4838         }
4839         if (this.cls) {
4840             cfg.cls += " " + this.cls;
4841         }
4842         return cfg;
4843     }
4844    
4845 });
4846
4847  
4848
4849  /*
4850  * - LGPL
4851  *
4852  * Pagination item
4853  * 
4854  */
4855
4856
4857 /**
4858  * @class Roo.bootstrap.PaginationItem
4859  * @extends Roo.bootstrap.Component
4860  * Bootstrap PaginationItem class
4861  * @cfg {String} html text
4862  * @cfg {String} href the link
4863  * @cfg {Boolean} preventDefault (true | false) default true
4864  * @cfg {Boolean} active (true | false) default false
4865  * @cfg {Boolean} disabled default false
4866  * 
4867  * 
4868  * @constructor
4869  * Create a new PaginationItem
4870  * @param {Object} config The config object
4871  */
4872
4873
4874 Roo.bootstrap.PaginationItem = function(config){
4875     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4876     this.addEvents({
4877         // raw events
4878         /**
4879          * @event click
4880          * The raw click event for the entire grid.
4881          * @param {Roo.EventObject} e
4882          */
4883         "click" : true
4884     });
4885 };
4886
4887 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4888     
4889     href : false,
4890     html : false,
4891     preventDefault: true,
4892     active : false,
4893     cls : false,
4894     disabled: false,
4895     
4896     getAutoCreate : function(){
4897         var cfg= {
4898             tag: 'li',
4899             cn: [
4900                 {
4901                     tag : 'a',
4902                     href : this.href ? this.href : '#',
4903                     html : this.html ? this.html : ''
4904                 }
4905             ]
4906         };
4907         
4908         if(this.cls){
4909             cfg.cls = this.cls;
4910         }
4911         
4912         if(this.disabled){
4913             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4914         }
4915         
4916         if(this.active){
4917             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4918         }
4919         
4920         return cfg;
4921     },
4922     
4923     initEvents: function() {
4924         
4925         this.el.on('click', this.onClick, this);
4926         
4927     },
4928     onClick : function(e)
4929     {
4930         Roo.log('PaginationItem on click ');
4931         if(this.preventDefault){
4932             e.preventDefault();
4933         }
4934         
4935         if(this.disabled){
4936             return;
4937         }
4938         
4939         this.fireEvent('click', this, e);
4940     }
4941    
4942 });
4943
4944  
4945
4946  /*
4947  * - LGPL
4948  *
4949  * slider
4950  * 
4951  */
4952
4953
4954 /**
4955  * @class Roo.bootstrap.Slider
4956  * @extends Roo.bootstrap.Component
4957  * Bootstrap Slider class
4958  *    
4959  * @constructor
4960  * Create a new Slider
4961  * @param {Object} config The config object
4962  */
4963
4964 Roo.bootstrap.Slider = function(config){
4965     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4966 };
4967
4968 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4969     
4970     getAutoCreate : function(){
4971         
4972         var cfg = {
4973             tag: 'div',
4974             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4975             cn: [
4976                 {
4977                     tag: 'a',
4978                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4979                 }
4980             ]
4981         };
4982         
4983         return cfg;
4984     }
4985    
4986 });
4987
4988  /*
4989  * Based on:
4990  * Ext JS Library 1.1.1
4991  * Copyright(c) 2006-2007, Ext JS, LLC.
4992  *
4993  * Originally Released Under LGPL - original licence link has changed is not relivant.
4994  *
4995  * Fork - LGPL
4996  * <script type="text/javascript">
4997  */
4998  
4999
5000 /**
5001  * @class Roo.grid.ColumnModel
5002  * @extends Roo.util.Observable
5003  * This is the default implementation of a ColumnModel used by the Grid. It defines
5004  * the columns in the grid.
5005  * <br>Usage:<br>
5006  <pre><code>
5007  var colModel = new Roo.grid.ColumnModel([
5008         {header: "Ticker", width: 60, sortable: true, locked: true},
5009         {header: "Company Name", width: 150, sortable: true},
5010         {header: "Market Cap.", width: 100, sortable: true},
5011         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5012         {header: "Employees", width: 100, sortable: true, resizable: false}
5013  ]);
5014  </code></pre>
5015  * <p>
5016  
5017  * The config options listed for this class are options which may appear in each
5018  * individual column definition.
5019  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5020  * @constructor
5021  * @param {Object} config An Array of column config objects. See this class's
5022  * config objects for details.
5023 */
5024 Roo.grid.ColumnModel = function(config){
5025         /**
5026      * The config passed into the constructor
5027      */
5028     this.config = config;
5029     this.lookup = {};
5030
5031     // if no id, create one
5032     // if the column does not have a dataIndex mapping,
5033     // map it to the order it is in the config
5034     for(var i = 0, len = config.length; i < len; i++){
5035         var c = config[i];
5036         if(typeof c.dataIndex == "undefined"){
5037             c.dataIndex = i;
5038         }
5039         if(typeof c.renderer == "string"){
5040             c.renderer = Roo.util.Format[c.renderer];
5041         }
5042         if(typeof c.id == "undefined"){
5043             c.id = Roo.id();
5044         }
5045         if(c.editor && c.editor.xtype){
5046             c.editor  = Roo.factory(c.editor, Roo.grid);
5047         }
5048         if(c.editor && c.editor.isFormField){
5049             c.editor = new Roo.grid.GridEditor(c.editor);
5050         }
5051         this.lookup[c.id] = c;
5052     }
5053
5054     /**
5055      * The width of columns which have no width specified (defaults to 100)
5056      * @type Number
5057      */
5058     this.defaultWidth = 100;
5059
5060     /**
5061      * Default sortable of columns which have no sortable specified (defaults to false)
5062      * @type Boolean
5063      */
5064     this.defaultSortable = false;
5065
5066     this.addEvents({
5067         /**
5068              * @event widthchange
5069              * Fires when the width of a column changes.
5070              * @param {ColumnModel} this
5071              * @param {Number} columnIndex The column index
5072              * @param {Number} newWidth The new width
5073              */
5074             "widthchange": true,
5075         /**
5076              * @event headerchange
5077              * Fires when the text of a header changes.
5078              * @param {ColumnModel} this
5079              * @param {Number} columnIndex The column index
5080              * @param {Number} newText The new header text
5081              */
5082             "headerchange": true,
5083         /**
5084              * @event hiddenchange
5085              * Fires when a column is hidden or "unhidden".
5086              * @param {ColumnModel} this
5087              * @param {Number} columnIndex The column index
5088              * @param {Boolean} hidden true if hidden, false otherwise
5089              */
5090             "hiddenchange": true,
5091             /**
5092          * @event columnmoved
5093          * Fires when a column is moved.
5094          * @param {ColumnModel} this
5095          * @param {Number} oldIndex
5096          * @param {Number} newIndex
5097          */
5098         "columnmoved" : true,
5099         /**
5100          * @event columlockchange
5101          * Fires when a column's locked state is changed
5102          * @param {ColumnModel} this
5103          * @param {Number} colIndex
5104          * @param {Boolean} locked true if locked
5105          */
5106         "columnlockchange" : true
5107     });
5108     Roo.grid.ColumnModel.superclass.constructor.call(this);
5109 };
5110 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5111     /**
5112      * @cfg {String} header The header text to display in the Grid view.
5113      */
5114     /**
5115      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5116      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5117      * specified, the column's index is used as an index into the Record's data Array.
5118      */
5119     /**
5120      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5121      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5122      */
5123     /**
5124      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5125      * Defaults to the value of the {@link #defaultSortable} property.
5126      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5127      */
5128     /**
5129      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5130      */
5131     /**
5132      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5133      */
5134     /**
5135      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5136      */
5137     /**
5138      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5139      */
5140     /**
5141      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5142      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5143      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5144      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5145      */
5146        /**
5147      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5148      */
5149     /**
5150      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5151      */
5152     /**
5153      * @cfg {String} cursor (Optional)
5154      */
5155     /**
5156      * @cfg {String} tooltip (Optional)
5157      */
5158     /**
5159      * @cfg {Number} xs (Optional)
5160      */
5161     /**
5162      * @cfg {Number} sm (Optional)
5163      */
5164     /**
5165      * @cfg {Number} md (Optional)
5166      */
5167     /**
5168      * @cfg {Number} lg (Optional)
5169      */
5170     /**
5171      * Returns the id of the column at the specified index.
5172      * @param {Number} index The column index
5173      * @return {String} the id
5174      */
5175     getColumnId : function(index){
5176         return this.config[index].id;
5177     },
5178
5179     /**
5180      * Returns the column for a specified id.
5181      * @param {String} id The column id
5182      * @return {Object} the column
5183      */
5184     getColumnById : function(id){
5185         return this.lookup[id];
5186     },
5187
5188     
5189     /**
5190      * Returns the column for a specified dataIndex.
5191      * @param {String} dataIndex The column dataIndex
5192      * @return {Object|Boolean} the column or false if not found
5193      */
5194     getColumnByDataIndex: function(dataIndex){
5195         var index = this.findColumnIndex(dataIndex);
5196         return index > -1 ? this.config[index] : false;
5197     },
5198     
5199     /**
5200      * Returns the index for a specified column id.
5201      * @param {String} id The column id
5202      * @return {Number} the index, or -1 if not found
5203      */
5204     getIndexById : function(id){
5205         for(var i = 0, len = this.config.length; i < len; i++){
5206             if(this.config[i].id == id){
5207                 return i;
5208             }
5209         }
5210         return -1;
5211     },
5212     
5213     /**
5214      * Returns the index for a specified column dataIndex.
5215      * @param {String} dataIndex The column dataIndex
5216      * @return {Number} the index, or -1 if not found
5217      */
5218     
5219     findColumnIndex : function(dataIndex){
5220         for(var i = 0, len = this.config.length; i < len; i++){
5221             if(this.config[i].dataIndex == dataIndex){
5222                 return i;
5223             }
5224         }
5225         return -1;
5226     },
5227     
5228     
5229     moveColumn : function(oldIndex, newIndex){
5230         var c = this.config[oldIndex];
5231         this.config.splice(oldIndex, 1);
5232         this.config.splice(newIndex, 0, c);
5233         this.dataMap = null;
5234         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5235     },
5236
5237     isLocked : function(colIndex){
5238         return this.config[colIndex].locked === true;
5239     },
5240
5241     setLocked : function(colIndex, value, suppressEvent){
5242         if(this.isLocked(colIndex) == value){
5243             return;
5244         }
5245         this.config[colIndex].locked = value;
5246         if(!suppressEvent){
5247             this.fireEvent("columnlockchange", this, colIndex, value);
5248         }
5249     },
5250
5251     getTotalLockedWidth : function(){
5252         var totalWidth = 0;
5253         for(var i = 0; i < this.config.length; i++){
5254             if(this.isLocked(i) && !this.isHidden(i)){
5255                 this.totalWidth += this.getColumnWidth(i);
5256             }
5257         }
5258         return totalWidth;
5259     },
5260
5261     getLockedCount : function(){
5262         for(var i = 0, len = this.config.length; i < len; i++){
5263             if(!this.isLocked(i)){
5264                 return i;
5265             }
5266         }
5267         
5268         return this.config.length;
5269     },
5270
5271     /**
5272      * Returns the number of columns.
5273      * @return {Number}
5274      */
5275     getColumnCount : function(visibleOnly){
5276         if(visibleOnly === true){
5277             var c = 0;
5278             for(var i = 0, len = this.config.length; i < len; i++){
5279                 if(!this.isHidden(i)){
5280                     c++;
5281                 }
5282             }
5283             return c;
5284         }
5285         return this.config.length;
5286     },
5287
5288     /**
5289      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5290      * @param {Function} fn
5291      * @param {Object} scope (optional)
5292      * @return {Array} result
5293      */
5294     getColumnsBy : function(fn, scope){
5295         var r = [];
5296         for(var i = 0, len = this.config.length; i < len; i++){
5297             var c = this.config[i];
5298             if(fn.call(scope||this, c, i) === true){
5299                 r[r.length] = c;
5300             }
5301         }
5302         return r;
5303     },
5304
5305     /**
5306      * Returns true if the specified column is sortable.
5307      * @param {Number} col The column index
5308      * @return {Boolean}
5309      */
5310     isSortable : function(col){
5311         if(typeof this.config[col].sortable == "undefined"){
5312             return this.defaultSortable;
5313         }
5314         return this.config[col].sortable;
5315     },
5316
5317     /**
5318      * Returns the rendering (formatting) function defined for the column.
5319      * @param {Number} col The column index.
5320      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5321      */
5322     getRenderer : function(col){
5323         if(!this.config[col].renderer){
5324             return Roo.grid.ColumnModel.defaultRenderer;
5325         }
5326         return this.config[col].renderer;
5327     },
5328
5329     /**
5330      * Sets the rendering (formatting) function for a column.
5331      * @param {Number} col The column index
5332      * @param {Function} fn The function to use to process the cell's raw data
5333      * to return HTML markup for the grid view. The render function is called with
5334      * the following parameters:<ul>
5335      * <li>Data value.</li>
5336      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5337      * <li>css A CSS style string to apply to the table cell.</li>
5338      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5339      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5340      * <li>Row index</li>
5341      * <li>Column index</li>
5342      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5343      */
5344     setRenderer : function(col, fn){
5345         this.config[col].renderer = fn;
5346     },
5347
5348     /**
5349      * Returns the width for the specified column.
5350      * @param {Number} col The column index
5351      * @return {Number}
5352      */
5353     getColumnWidth : function(col){
5354         return this.config[col].width * 1 || this.defaultWidth;
5355     },
5356
5357     /**
5358      * Sets the width for a column.
5359      * @param {Number} col The column index
5360      * @param {Number} width The new width
5361      */
5362     setColumnWidth : function(col, width, suppressEvent){
5363         this.config[col].width = width;
5364         this.totalWidth = null;
5365         if(!suppressEvent){
5366              this.fireEvent("widthchange", this, col, width);
5367         }
5368     },
5369
5370     /**
5371      * Returns the total width of all columns.
5372      * @param {Boolean} includeHidden True to include hidden column widths
5373      * @return {Number}
5374      */
5375     getTotalWidth : function(includeHidden){
5376         if(!this.totalWidth){
5377             this.totalWidth = 0;
5378             for(var i = 0, len = this.config.length; i < len; i++){
5379                 if(includeHidden || !this.isHidden(i)){
5380                     this.totalWidth += this.getColumnWidth(i);
5381                 }
5382             }
5383         }
5384         return this.totalWidth;
5385     },
5386
5387     /**
5388      * Returns the header for the specified column.
5389      * @param {Number} col The column index
5390      * @return {String}
5391      */
5392     getColumnHeader : function(col){
5393         return this.config[col].header;
5394     },
5395
5396     /**
5397      * Sets the header for a column.
5398      * @param {Number} col The column index
5399      * @param {String} header The new header
5400      */
5401     setColumnHeader : function(col, header){
5402         this.config[col].header = header;
5403         this.fireEvent("headerchange", this, col, header);
5404     },
5405
5406     /**
5407      * Returns the tooltip for the specified column.
5408      * @param {Number} col The column index
5409      * @return {String}
5410      */
5411     getColumnTooltip : function(col){
5412             return this.config[col].tooltip;
5413     },
5414     /**
5415      * Sets the tooltip for a column.
5416      * @param {Number} col The column index
5417      * @param {String} tooltip The new tooltip
5418      */
5419     setColumnTooltip : function(col, tooltip){
5420             this.config[col].tooltip = tooltip;
5421     },
5422
5423     /**
5424      * Returns the dataIndex for the specified column.
5425      * @param {Number} col The column index
5426      * @return {Number}
5427      */
5428     getDataIndex : function(col){
5429         return this.config[col].dataIndex;
5430     },
5431
5432     /**
5433      * Sets the dataIndex for a column.
5434      * @param {Number} col The column index
5435      * @param {Number} dataIndex The new dataIndex
5436      */
5437     setDataIndex : function(col, dataIndex){
5438         this.config[col].dataIndex = dataIndex;
5439     },
5440
5441     
5442     
5443     /**
5444      * Returns true if the cell is editable.
5445      * @param {Number} colIndex The column index
5446      * @param {Number} rowIndex The row index - this is nto actually used..?
5447      * @return {Boolean}
5448      */
5449     isCellEditable : function(colIndex, rowIndex){
5450         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5451     },
5452
5453     /**
5454      * Returns the editor defined for the cell/column.
5455      * return false or null to disable editing.
5456      * @param {Number} colIndex The column index
5457      * @param {Number} rowIndex The row index
5458      * @return {Object}
5459      */
5460     getCellEditor : function(colIndex, rowIndex){
5461         return this.config[colIndex].editor;
5462     },
5463
5464     /**
5465      * Sets if a column is editable.
5466      * @param {Number} col The column index
5467      * @param {Boolean} editable True if the column is editable
5468      */
5469     setEditable : function(col, editable){
5470         this.config[col].editable = editable;
5471     },
5472
5473
5474     /**
5475      * Returns true if the column is hidden.
5476      * @param {Number} colIndex The column index
5477      * @return {Boolean}
5478      */
5479     isHidden : function(colIndex){
5480         return this.config[colIndex].hidden;
5481     },
5482
5483
5484     /**
5485      * Returns true if the column width cannot be changed
5486      */
5487     isFixed : function(colIndex){
5488         return this.config[colIndex].fixed;
5489     },
5490
5491     /**
5492      * Returns true if the column can be resized
5493      * @return {Boolean}
5494      */
5495     isResizable : function(colIndex){
5496         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5497     },
5498     /**
5499      * Sets if a column is hidden.
5500      * @param {Number} colIndex The column index
5501      * @param {Boolean} hidden True if the column is hidden
5502      */
5503     setHidden : function(colIndex, hidden){
5504         this.config[colIndex].hidden = hidden;
5505         this.totalWidth = null;
5506         this.fireEvent("hiddenchange", this, colIndex, hidden);
5507     },
5508
5509     /**
5510      * Sets the editor for a column.
5511      * @param {Number} col The column index
5512      * @param {Object} editor The editor object
5513      */
5514     setEditor : function(col, editor){
5515         this.config[col].editor = editor;
5516     }
5517 });
5518
5519 Roo.grid.ColumnModel.defaultRenderer = function(value){
5520         if(typeof value == "string" && value.length < 1){
5521             return "&#160;";
5522         }
5523         return value;
5524 };
5525
5526 // Alias for backwards compatibility
5527 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5528 /*
5529  * Based on:
5530  * Ext JS Library 1.1.1
5531  * Copyright(c) 2006-2007, Ext JS, LLC.
5532  *
5533  * Originally Released Under LGPL - original licence link has changed is not relivant.
5534  *
5535  * Fork - LGPL
5536  * <script type="text/javascript">
5537  */
5538  
5539 /**
5540  * @class Roo.LoadMask
5541  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5542  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5543  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5544  * element's UpdateManager load indicator and will be destroyed after the initial load.
5545  * @constructor
5546  * Create a new LoadMask
5547  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5548  * @param {Object} config The config object
5549  */
5550 Roo.LoadMask = function(el, config){
5551     this.el = Roo.get(el);
5552     Roo.apply(this, config);
5553     if(this.store){
5554         this.store.on('beforeload', this.onBeforeLoad, this);
5555         this.store.on('load', this.onLoad, this);
5556         this.store.on('loadexception', this.onLoadException, this);
5557         this.removeMask = false;
5558     }else{
5559         var um = this.el.getUpdateManager();
5560         um.showLoadIndicator = false; // disable the default indicator
5561         um.on('beforeupdate', this.onBeforeLoad, this);
5562         um.on('update', this.onLoad, this);
5563         um.on('failure', this.onLoad, this);
5564         this.removeMask = true;
5565     }
5566 };
5567
5568 Roo.LoadMask.prototype = {
5569     /**
5570      * @cfg {Boolean} removeMask
5571      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5572      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5573      */
5574     /**
5575      * @cfg {String} msg
5576      * The text to display in a centered loading message box (defaults to 'Loading...')
5577      */
5578     msg : 'Loading...',
5579     /**
5580      * @cfg {String} msgCls
5581      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5582      */
5583     msgCls : 'x-mask-loading',
5584
5585     /**
5586      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5587      * @type Boolean
5588      */
5589     disabled: false,
5590
5591     /**
5592      * Disables the mask to prevent it from being displayed
5593      */
5594     disable : function(){
5595        this.disabled = true;
5596     },
5597
5598     /**
5599      * Enables the mask so that it can be displayed
5600      */
5601     enable : function(){
5602         this.disabled = false;
5603     },
5604     
5605     onLoadException : function()
5606     {
5607         Roo.log(arguments);
5608         
5609         if (typeof(arguments[3]) != 'undefined') {
5610             Roo.MessageBox.alert("Error loading",arguments[3]);
5611         } 
5612         /*
5613         try {
5614             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5615                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5616             }   
5617         } catch(e) {
5618             
5619         }
5620         */
5621     
5622         
5623         
5624         this.el.unmask(this.removeMask);
5625     },
5626     // private
5627     onLoad : function()
5628     {
5629         this.el.unmask(this.removeMask);
5630     },
5631
5632     // private
5633     onBeforeLoad : function(){
5634         if(!this.disabled){
5635             this.el.mask(this.msg, this.msgCls);
5636         }
5637     },
5638
5639     // private
5640     destroy : function(){
5641         if(this.store){
5642             this.store.un('beforeload', this.onBeforeLoad, this);
5643             this.store.un('load', this.onLoad, this);
5644             this.store.un('loadexception', this.onLoadException, this);
5645         }else{
5646             var um = this.el.getUpdateManager();
5647             um.un('beforeupdate', this.onBeforeLoad, this);
5648             um.un('update', this.onLoad, this);
5649             um.un('failure', this.onLoad, this);
5650         }
5651     }
5652 };/*
5653  * - LGPL
5654  *
5655  * table
5656  * 
5657  */
5658
5659 /**
5660  * @class Roo.bootstrap.Table
5661  * @extends Roo.bootstrap.Component
5662  * Bootstrap Table class
5663  * @cfg {String} cls table class
5664  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5665  * @cfg {String} bgcolor Specifies the background color for a table
5666  * @cfg {Number} border Specifies whether the table cells should have borders or not
5667  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5668  * @cfg {Number} cellspacing Specifies the space between cells
5669  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5670  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5671  * @cfg {String} sortable Specifies that the table should be sortable
5672  * @cfg {String} summary Specifies a summary of the content of a table
5673  * @cfg {Number} width Specifies the width of a table
5674  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5675  * 
5676  * @cfg {boolean} striped Should the rows be alternative striped
5677  * @cfg {boolean} bordered Add borders to the table
5678  * @cfg {boolean} hover Add hover highlighting
5679  * @cfg {boolean} condensed Format condensed
5680  * @cfg {boolean} responsive Format condensed
5681  * @cfg {Boolean} loadMask (true|false) default false
5682  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5683  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5684  * @cfg {Boolean} rowSelection (true|false) default false
5685  * @cfg {Boolean} cellSelection (true|false) default false
5686  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5687  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5688  
5689  * 
5690  * @constructor
5691  * Create a new Table
5692  * @param {Object} config The config object
5693  */
5694
5695 Roo.bootstrap.Table = function(config){
5696     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5697     
5698     if (config.container) {
5699         // ctor'ed from a Border/panel.grid
5700         this.container = Roo.get(config.container);
5701         this.container.update("");
5702         this.container.setStyle("overflow", "hidden");
5703         this.container.addClass('x-grid-container');
5704
5705     }
5706     
5707     // BC...
5708     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5709     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5710     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5711     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5712     
5713     
5714     if (this.sm) {
5715         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5716         this.sm = this.selModel;
5717         this.sm.xmodule = this.xmodule || false;
5718     }
5719     if (this.cm && typeof(this.cm.config) == 'undefined') {
5720         this.colModel = new Roo.grid.ColumnModel(this.cm);
5721         this.cm = this.colModel;
5722         this.cm.xmodule = this.xmodule || false;
5723     }
5724     if (this.store) {
5725         this.store= Roo.factory(this.store, Roo.data);
5726         this.ds = this.store;
5727         this.ds.xmodule = this.xmodule || false;
5728          
5729     }
5730     if (this.footer && this.store) {
5731         this.footer.dataSource = this.ds;
5732         this.footer = Roo.factory(this.footer);
5733     }
5734     
5735     /** @private */
5736     this.addEvents({
5737         /**
5738          * @event cellclick
5739          * Fires when a cell is clicked
5740          * @param {Roo.bootstrap.Table} this
5741          * @param {Roo.Element} el
5742          * @param {Number} rowIndex
5743          * @param {Number} columnIndex
5744          * @param {Roo.EventObject} e
5745          */
5746         "cellclick" : true,
5747         /**
5748          * @event celldblclick
5749          * Fires when a cell is double clicked
5750          * @param {Roo.bootstrap.Table} this
5751          * @param {Roo.Element} el
5752          * @param {Number} rowIndex
5753          * @param {Number} columnIndex
5754          * @param {Roo.EventObject} e
5755          */
5756         "celldblclick" : true,
5757         /**
5758          * @event rowclick
5759          * Fires when a row is clicked
5760          * @param {Roo.bootstrap.Table} this
5761          * @param {Roo.Element} el
5762          * @param {Number} rowIndex
5763          * @param {Roo.EventObject} e
5764          */
5765         "rowclick" : true,
5766         /**
5767          * @event rowdblclick
5768          * Fires when a row is double clicked
5769          * @param {Roo.bootstrap.Table} this
5770          * @param {Roo.Element} el
5771          * @param {Number} rowIndex
5772          * @param {Roo.EventObject} e
5773          */
5774         "rowdblclick" : true,
5775         /**
5776          * @event mouseover
5777          * Fires when a mouseover occur
5778          * @param {Roo.bootstrap.Table} this
5779          * @param {Roo.Element} el
5780          * @param {Number} rowIndex
5781          * @param {Number} columnIndex
5782          * @param {Roo.EventObject} e
5783          */
5784         "mouseover" : true,
5785         /**
5786          * @event mouseout
5787          * Fires when a mouseout occur
5788          * @param {Roo.bootstrap.Table} this
5789          * @param {Roo.Element} el
5790          * @param {Number} rowIndex
5791          * @param {Number} columnIndex
5792          * @param {Roo.EventObject} e
5793          */
5794         "mouseout" : true,
5795         /**
5796          * @event rowclass
5797          * Fires when a row is rendered, so you can change add a style to it.
5798          * @param {Roo.bootstrap.Table} this
5799          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5800          */
5801         'rowclass' : true,
5802           /**
5803          * @event rowsrendered
5804          * Fires when all the  rows have been rendered
5805          * @param {Roo.bootstrap.Table} this
5806          */
5807         'rowsrendered' : true,
5808         /**
5809          * @event contextmenu
5810          * The raw contextmenu event for the entire grid.
5811          * @param {Roo.EventObject} e
5812          */
5813         "contextmenu" : true,
5814         /**
5815          * @event rowcontextmenu
5816          * Fires when a row is right clicked
5817          * @param {Roo.bootstrap.Table} this
5818          * @param {Number} rowIndex
5819          * @param {Roo.EventObject} e
5820          */
5821         "rowcontextmenu" : true,
5822         /**
5823          * @event cellcontextmenu
5824          * Fires when a cell is right clicked
5825          * @param {Roo.bootstrap.Table} this
5826          * @param {Number} rowIndex
5827          * @param {Number} cellIndex
5828          * @param {Roo.EventObject} e
5829          */
5830          "cellcontextmenu" : true,
5831          /**
5832          * @event headercontextmenu
5833          * Fires when a header is right clicked
5834          * @param {Roo.bootstrap.Table} this
5835          * @param {Number} columnIndex
5836          * @param {Roo.EventObject} e
5837          */
5838         "headercontextmenu" : true
5839     });
5840 };
5841
5842 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5843     
5844     cls: false,
5845     align: false,
5846     bgcolor: false,
5847     border: false,
5848     cellpadding: false,
5849     cellspacing: false,
5850     frame: false,
5851     rules: false,
5852     sortable: false,
5853     summary: false,
5854     width: false,
5855     striped : false,
5856     scrollBody : false,
5857     bordered: false,
5858     hover:  false,
5859     condensed : false,
5860     responsive : false,
5861     sm : false,
5862     cm : false,
5863     store : false,
5864     loadMask : false,
5865     footerShow : true,
5866     headerShow : true,
5867   
5868     rowSelection : false,
5869     cellSelection : false,
5870     layout : false,
5871     
5872     // Roo.Element - the tbody
5873     mainBody: false,
5874     // Roo.Element - thead element
5875     mainHead: false,
5876     
5877     container: false, // used by gridpanel...
5878     
5879     getAutoCreate : function()
5880     {
5881         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5882         
5883         cfg = {
5884             tag: 'table',
5885             cls : 'table',
5886             cn : []
5887         };
5888         if (this.scrollBody) {
5889             cfg.cls += ' table-body-fixed';
5890         }    
5891         if (this.striped) {
5892             cfg.cls += ' table-striped';
5893         }
5894         
5895         if (this.hover) {
5896             cfg.cls += ' table-hover';
5897         }
5898         if (this.bordered) {
5899             cfg.cls += ' table-bordered';
5900         }
5901         if (this.condensed) {
5902             cfg.cls += ' table-condensed';
5903         }
5904         if (this.responsive) {
5905             cfg.cls += ' table-responsive';
5906         }
5907         
5908         if (this.cls) {
5909             cfg.cls+=  ' ' +this.cls;
5910         }
5911         
5912         // this lot should be simplifed...
5913         
5914         if (this.align) {
5915             cfg.align=this.align;
5916         }
5917         if (this.bgcolor) {
5918             cfg.bgcolor=this.bgcolor;
5919         }
5920         if (this.border) {
5921             cfg.border=this.border;
5922         }
5923         if (this.cellpadding) {
5924             cfg.cellpadding=this.cellpadding;
5925         }
5926         if (this.cellspacing) {
5927             cfg.cellspacing=this.cellspacing;
5928         }
5929         if (this.frame) {
5930             cfg.frame=this.frame;
5931         }
5932         if (this.rules) {
5933             cfg.rules=this.rules;
5934         }
5935         if (this.sortable) {
5936             cfg.sortable=this.sortable;
5937         }
5938         if (this.summary) {
5939             cfg.summary=this.summary;
5940         }
5941         if (this.width) {
5942             cfg.width=this.width;
5943         }
5944         if (this.layout) {
5945             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5946         }
5947         
5948         if(this.store || this.cm){
5949             if(this.headerShow){
5950                 cfg.cn.push(this.renderHeader());
5951             }
5952             
5953             cfg.cn.push(this.renderBody());
5954             
5955             if(this.footerShow){
5956                 cfg.cn.push(this.renderFooter());
5957             }
5958             // where does this come from?
5959             //cfg.cls+=  ' TableGrid';
5960         }
5961         
5962         return { cn : [ cfg ] };
5963     },
5964     
5965     initEvents : function()
5966     {   
5967         if(!this.store || !this.cm){
5968             return;
5969         }
5970         
5971         //Roo.log('initEvents with ds!!!!');
5972         
5973         this.mainBody = this.el.select('tbody', true).first();
5974         this.mainHead = this.el.select('thead', true).first();
5975         
5976         
5977         
5978         var _this = this;
5979         
5980         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5981             e.on('click', _this.sort, _this);
5982         });
5983         
5984         this.el.on("click", this.onClick, this);
5985         this.el.on("dblclick", this.onDblClick, this);
5986         
5987         // why is this done????? = it breaks dialogs??
5988         //this.parent().el.setStyle('position', 'relative');
5989         
5990         
5991         if (this.footer) {
5992             this.footer.parentId = this.id;
5993             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5994         }
5995         
5996         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5997         
5998         this.store.on('load', this.onLoad, this);
5999         this.store.on('beforeload', this.onBeforeLoad, this);
6000         this.store.on('update', this.onUpdate, this);
6001         this.store.on('add', this.onAdd, this);
6002         
6003         this.el.on("contextmenu", this.onContextMenu, this);
6004         
6005         this.mainBody.on('scroll', this.onBodyScroll, this);
6006         
6007         
6008     },
6009     
6010     onContextMenu : function(e, t)
6011     {
6012         this.processEvent("contextmenu", e);
6013     },
6014     
6015     processEvent : function(name, e)
6016     {
6017         if (name != 'touchstart' ) {
6018             this.fireEvent(name, e);    
6019         }
6020         
6021         var t = e.getTarget();
6022         
6023         var cell = Roo.get(t);
6024         
6025         if(!cell){
6026             return;
6027         }
6028         
6029         if(cell.findParent('tfoot', false, true)){
6030             return;
6031         }
6032         
6033         if(cell.findParent('thead', false, true)){
6034             
6035             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6036                 cell = Roo.get(t).findParent('th', false, true);
6037                 if (!cell) {
6038                     Roo.log("failed to find th in thead?");
6039                     Roo.log(e.getTarget());
6040                     return;
6041                 }
6042             }
6043             
6044             var cellIndex = cell.dom.cellIndex;
6045             
6046             var ename = name == 'touchstart' ? 'click' : name;
6047             this.fireEvent("header" + ename, this, cellIndex, e);
6048             
6049             return;
6050         }
6051         
6052         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6053             cell = Roo.get(t).findParent('td', false, true);
6054             if (!cell) {
6055                 Roo.log("failed to find th in tbody?");
6056                 Roo.log(e.getTarget());
6057                 return;
6058             }
6059         }
6060         
6061         var row = cell.findParent('tr', false, true);
6062         var cellIndex = cell.dom.cellIndex;
6063         var rowIndex = row.dom.rowIndex - 1;
6064         
6065         if(row !== false){
6066             
6067             this.fireEvent("row" + name, this, rowIndex, e);
6068             
6069             if(cell !== false){
6070             
6071                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6072             }
6073         }
6074         
6075     },
6076     
6077     onMouseover : function(e, el)
6078     {
6079         var cell = Roo.get(el);
6080         
6081         if(!cell){
6082             return;
6083         }
6084         
6085         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6086             cell = cell.findParent('td', false, true);
6087         }
6088         
6089         var row = cell.findParent('tr', false, true);
6090         var cellIndex = cell.dom.cellIndex;
6091         var rowIndex = row.dom.rowIndex - 1; // start from 0
6092         
6093         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6094         
6095     },
6096     
6097     onMouseout : function(e, el)
6098     {
6099         var cell = Roo.get(el);
6100         
6101         if(!cell){
6102             return;
6103         }
6104         
6105         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6106             cell = cell.findParent('td', false, true);
6107         }
6108         
6109         var row = cell.findParent('tr', false, true);
6110         var cellIndex = cell.dom.cellIndex;
6111         var rowIndex = row.dom.rowIndex - 1; // start from 0
6112         
6113         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6114         
6115     },
6116     
6117     onClick : function(e, el)
6118     {
6119         var cell = Roo.get(el);
6120         
6121         if(!cell || (!this.cellSelection && !this.rowSelection)){
6122             return;
6123         }
6124         
6125         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6126             cell = cell.findParent('td', false, true);
6127         }
6128         
6129         if(!cell || typeof(cell) == 'undefined'){
6130             return;
6131         }
6132         
6133         var row = cell.findParent('tr', false, true);
6134         
6135         if(!row || typeof(row) == 'undefined'){
6136             return;
6137         }
6138         
6139         var cellIndex = cell.dom.cellIndex;
6140         var rowIndex = this.getRowIndex(row);
6141         
6142         // why??? - should these not be based on SelectionModel?
6143         if(this.cellSelection){
6144             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6145         }
6146         
6147         if(this.rowSelection){
6148             this.fireEvent('rowclick', this, row, rowIndex, e);
6149         }
6150         
6151         
6152     },
6153     
6154     onDblClick : function(e,el)
6155     {
6156         var cell = Roo.get(el);
6157         
6158         if(!cell || (!this.CellSelection && !this.RowSelection)){
6159             return;
6160         }
6161         
6162         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6163             cell = cell.findParent('td', false, true);
6164         }
6165         
6166         if(!cell || typeof(cell) == 'undefined'){
6167             return;
6168         }
6169         
6170         var row = cell.findParent('tr', false, true);
6171         
6172         if(!row || typeof(row) == 'undefined'){
6173             return;
6174         }
6175         
6176         var cellIndex = cell.dom.cellIndex;
6177         var rowIndex = this.getRowIndex(row);
6178         
6179         if(this.CellSelection){
6180             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6181         }
6182         
6183         if(this.RowSelection){
6184             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6185         }
6186     },
6187     
6188     sort : function(e,el)
6189     {
6190         var col = Roo.get(el);
6191         
6192         if(!col.hasClass('sortable')){
6193             return;
6194         }
6195         
6196         var sort = col.attr('sort');
6197         var dir = 'ASC';
6198         
6199         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6200             dir = 'DESC';
6201         }
6202         
6203         this.store.sortInfo = {field : sort, direction : dir};
6204         
6205         if (this.footer) {
6206             Roo.log("calling footer first");
6207             this.footer.onClick('first');
6208         } else {
6209         
6210             this.store.load({ params : { start : 0 } });
6211         }
6212     },
6213     
6214     renderHeader : function()
6215     {
6216         var header = {
6217             tag: 'thead',
6218             cn : []
6219         };
6220         
6221         var cm = this.cm;
6222         this.totalWidth = 0;
6223         
6224         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6225             
6226             var config = cm.config[i];
6227             
6228             var c = {
6229                 tag: 'th',
6230                 style : '',
6231                 html: cm.getColumnHeader(i)
6232             };
6233             
6234             var hh = '';
6235             
6236             if(typeof(config.sortable) != 'undefined' && config.sortable){
6237                 c.cls = 'sortable';
6238                 c.html = '<i class="glyphicon"></i>' + c.html;
6239             }
6240             
6241             if(typeof(config.lgHeader) != 'undefined'){
6242                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6243             }
6244             
6245             if(typeof(config.mdHeader) != 'undefined'){
6246                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6247             }
6248             
6249             if(typeof(config.smHeader) != 'undefined'){
6250                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6251             }
6252             
6253             if(typeof(config.xsHeader) != 'undefined'){
6254                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6255             }
6256             
6257             if(hh.length){
6258                 c.html = hh;
6259             }
6260             
6261             if(typeof(config.tooltip) != 'undefined'){
6262                 c.tooltip = config.tooltip;
6263             }
6264             
6265             if(typeof(config.colspan) != 'undefined'){
6266                 c.colspan = config.colspan;
6267             }
6268             
6269             if(typeof(config.hidden) != 'undefined' && config.hidden){
6270                 c.style += ' display:none;';
6271             }
6272             
6273             if(typeof(config.dataIndex) != 'undefined'){
6274                 c.sort = config.dataIndex;
6275             }
6276             
6277            
6278             
6279             if(typeof(config.align) != 'undefined' && config.align.length){
6280                 c.style += ' text-align:' + config.align + ';';
6281             }
6282             
6283             if(typeof(config.width) != 'undefined'){
6284                 c.style += ' width:' + config.width + 'px;';
6285                 this.totalWidth += config.width;
6286             } else {
6287                 this.totalWidth += 100; // assume minimum of 100 per column?
6288             }
6289             
6290             if(typeof(config.cls) != 'undefined'){
6291                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6292             }
6293             
6294             ['xs','sm','md','lg'].map(function(size){
6295                 
6296                 if(typeof(config[size]) == 'undefined'){
6297                     return;
6298                 }
6299                 
6300                 if (!config[size]) { // 0 = hidden
6301                     c.cls += ' hidden-' + size;
6302                     return;
6303                 }
6304                 
6305                 c.cls += ' col-' + size + '-' + config[size];
6306
6307             });
6308             
6309             header.cn.push(c)
6310         }
6311         
6312         return header;
6313     },
6314     
6315     renderBody : function()
6316     {
6317         var body = {
6318             tag: 'tbody',
6319             cn : [
6320                 {
6321                     tag: 'tr',
6322                     cn : [
6323                         {
6324                             tag : 'td',
6325                             colspan :  this.cm.getColumnCount()
6326                         }
6327                     ]
6328                 }
6329             ]
6330         };
6331         
6332         return body;
6333     },
6334     
6335     renderFooter : function()
6336     {
6337         var footer = {
6338             tag: 'tfoot',
6339             cn : [
6340                 {
6341                     tag: 'tr',
6342                     cn : [
6343                         {
6344                             tag : 'td',
6345                             colspan :  this.cm.getColumnCount()
6346                         }
6347                     ]
6348                 }
6349             ]
6350         };
6351         
6352         return footer;
6353     },
6354     
6355     
6356     
6357     onLoad : function()
6358     {
6359 //        Roo.log('ds onload');
6360         this.clear();
6361         
6362         var _this = this;
6363         var cm = this.cm;
6364         var ds = this.store;
6365         
6366         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6367             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6368             if (_this.store.sortInfo) {
6369                     
6370                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6371                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6372                 }
6373                 
6374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6375                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6376                 }
6377             }
6378         });
6379         
6380         var tbody =  this.mainBody;
6381               
6382         if(ds.getCount() > 0){
6383             ds.data.each(function(d,rowIndex){
6384                 var row =  this.renderRow(cm, ds, rowIndex);
6385                 
6386                 tbody.createChild(row);
6387                 
6388                 var _this = this;
6389                 
6390                 if(row.cellObjects.length){
6391                     Roo.each(row.cellObjects, function(r){
6392                         _this.renderCellObject(r);
6393                     })
6394                 }
6395                 
6396             }, this);
6397         }
6398         
6399         Roo.each(this.el.select('tbody td', true).elements, function(e){
6400             e.on('mouseover', _this.onMouseover, _this);
6401         });
6402         
6403         Roo.each(this.el.select('tbody td', true).elements, function(e){
6404             e.on('mouseout', _this.onMouseout, _this);
6405         });
6406         this.fireEvent('rowsrendered', this);
6407         //if(this.loadMask){
6408         //    this.maskEl.hide();
6409         //}
6410         
6411         this.autoSize();
6412     },
6413     
6414     
6415     onUpdate : function(ds,record)
6416     {
6417         this.refreshRow(record);
6418     },
6419     
6420     onRemove : function(ds, record, index, isUpdate){
6421         if(isUpdate !== true){
6422             this.fireEvent("beforerowremoved", this, index, record);
6423         }
6424         var bt = this.mainBody.dom;
6425         
6426         var rows = this.el.select('tbody > tr', true).elements;
6427         
6428         if(typeof(rows[index]) != 'undefined'){
6429             bt.removeChild(rows[index].dom);
6430         }
6431         
6432 //        if(bt.rows[index]){
6433 //            bt.removeChild(bt.rows[index]);
6434 //        }
6435         
6436         if(isUpdate !== true){
6437             //this.stripeRows(index);
6438             //this.syncRowHeights(index, index);
6439             //this.layout();
6440             this.fireEvent("rowremoved", this, index, record);
6441         }
6442     },
6443     
6444     onAdd : function(ds, records, rowIndex)
6445     {
6446         //Roo.log('on Add called');
6447         // - note this does not handle multiple adding very well..
6448         var bt = this.mainBody.dom;
6449         for (var i =0 ; i < records.length;i++) {
6450             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6451             //Roo.log(records[i]);
6452             //Roo.log(this.store.getAt(rowIndex+i));
6453             this.insertRow(this.store, rowIndex + i, false);
6454             return;
6455         }
6456         
6457     },
6458     
6459     
6460     refreshRow : function(record){
6461         var ds = this.store, index;
6462         if(typeof record == 'number'){
6463             index = record;
6464             record = ds.getAt(index);
6465         }else{
6466             index = ds.indexOf(record);
6467         }
6468         this.insertRow(ds, index, true);
6469         this.onRemove(ds, record, index+1, true);
6470         //this.syncRowHeights(index, index);
6471         //this.layout();
6472         this.fireEvent("rowupdated", this, index, record);
6473     },
6474     
6475     insertRow : function(dm, rowIndex, isUpdate){
6476         
6477         if(!isUpdate){
6478             this.fireEvent("beforerowsinserted", this, rowIndex);
6479         }
6480             //var s = this.getScrollState();
6481         var row = this.renderRow(this.cm, this.store, rowIndex);
6482         // insert before rowIndex..
6483         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6484         
6485         var _this = this;
6486                 
6487         if(row.cellObjects.length){
6488             Roo.each(row.cellObjects, function(r){
6489                 _this.renderCellObject(r);
6490             })
6491         }
6492             
6493         if(!isUpdate){
6494             this.fireEvent("rowsinserted", this, rowIndex);
6495             //this.syncRowHeights(firstRow, lastRow);
6496             //this.stripeRows(firstRow);
6497             //this.layout();
6498         }
6499         
6500     },
6501     
6502     
6503     getRowDom : function(rowIndex)
6504     {
6505         var rows = this.el.select('tbody > tr', true).elements;
6506         
6507         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6508         
6509     },
6510     // returns the object tree for a tr..
6511   
6512     
6513     renderRow : function(cm, ds, rowIndex) 
6514     {
6515         
6516         var d = ds.getAt(rowIndex);
6517         
6518         var row = {
6519             tag : 'tr',
6520             cn : []
6521         };
6522             
6523         var cellObjects = [];
6524         
6525         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6526             var config = cm.config[i];
6527             
6528             var renderer = cm.getRenderer(i);
6529             var value = '';
6530             var id = false;
6531             
6532             if(typeof(renderer) !== 'undefined'){
6533                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6534             }
6535             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6536             // and are rendered into the cells after the row is rendered - using the id for the element.
6537             
6538             if(typeof(value) === 'object'){
6539                 id = Roo.id();
6540                 cellObjects.push({
6541                     container : id,
6542                     cfg : value 
6543                 })
6544             }
6545             
6546             var rowcfg = {
6547                 record: d,
6548                 rowIndex : rowIndex,
6549                 colIndex : i,
6550                 rowClass : ''
6551             };
6552
6553             this.fireEvent('rowclass', this, rowcfg);
6554             
6555             var td = {
6556                 tag: 'td',
6557                 cls : rowcfg.rowClass,
6558                 style: '',
6559                 html: (typeof(value) === 'object') ? '' : value
6560             };
6561             
6562             if (id) {
6563                 td.id = id;
6564             }
6565             
6566             if(typeof(config.colspan) != 'undefined'){
6567                 td.colspan = config.colspan;
6568             }
6569             
6570             if(typeof(config.hidden) != 'undefined' && config.hidden){
6571                 td.style += ' display:none;';
6572             }
6573             
6574             if(typeof(config.align) != 'undefined' && config.align.length){
6575                 td.style += ' text-align:' + config.align + ';';
6576             }
6577             
6578             if(typeof(config.width) != 'undefined'){
6579                 td.style += ' width:' +  config.width + 'px;';
6580             }
6581             
6582             if(typeof(config.cursor) != 'undefined'){
6583                 td.style += ' cursor:' +  config.cursor + ';';
6584             }
6585             
6586             if(typeof(config.cls) != 'undefined'){
6587                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6588             }
6589             
6590             ['xs','sm','md','lg'].map(function(size){
6591                 
6592                 if(typeof(config[size]) == 'undefined'){
6593                     return;
6594                 }
6595                 
6596                 if (!config[size]) { // 0 = hidden
6597                     td.cls += ' hidden-' + size;
6598                     return;
6599                 }
6600                 
6601                 td.cls += ' col-' + size + '-' + config[size];
6602
6603             });
6604              
6605             row.cn.push(td);
6606            
6607         }
6608         
6609         row.cellObjects = cellObjects;
6610         
6611         return row;
6612           
6613     },
6614     
6615     
6616     
6617     onBeforeLoad : function()
6618     {
6619         //Roo.log('ds onBeforeLoad');
6620         
6621         //this.clear();
6622         
6623         //if(this.loadMask){
6624         //    this.maskEl.show();
6625         //}
6626     },
6627      /**
6628      * Remove all rows
6629      */
6630     clear : function()
6631     {
6632         this.el.select('tbody', true).first().dom.innerHTML = '';
6633     },
6634     /**
6635      * Show or hide a row.
6636      * @param {Number} rowIndex to show or hide
6637      * @param {Boolean} state hide
6638      */
6639     setRowVisibility : function(rowIndex, state)
6640     {
6641         var bt = this.mainBody.dom;
6642         
6643         var rows = this.el.select('tbody > tr', true).elements;
6644         
6645         if(typeof(rows[rowIndex]) == 'undefined'){
6646             return;
6647         }
6648         rows[rowIndex].dom.style.display = state ? '' : 'none';
6649     },
6650     
6651     
6652     getSelectionModel : function(){
6653         if(!this.selModel){
6654             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6655         }
6656         return this.selModel;
6657     },
6658     /*
6659      * Render the Roo.bootstrap object from renderder
6660      */
6661     renderCellObject : function(r)
6662     {
6663         var _this = this;
6664         
6665         var t = r.cfg.render(r.container);
6666         
6667         if(r.cfg.cn){
6668             Roo.each(r.cfg.cn, function(c){
6669                 var child = {
6670                     container: t.getChildContainer(),
6671                     cfg: c
6672                 };
6673                 _this.renderCellObject(child);
6674             })
6675         }
6676     },
6677     
6678     getRowIndex : function(row)
6679     {
6680         var rowIndex = -1;
6681         
6682         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6683             if(el != row){
6684                 return;
6685             }
6686             
6687             rowIndex = index;
6688         });
6689         
6690         return rowIndex;
6691     },
6692      /**
6693      * Returns the grid's underlying element = used by panel.Grid
6694      * @return {Element} The element
6695      */
6696     getGridEl : function(){
6697         return this.container;
6698     },
6699      /**
6700      * Forces a resize - used by panel.Grid
6701      * @return {Element} The element
6702      */
6703     autoSize : function(){
6704         //var ctr = Roo.get(this.container.dom.parentElement);
6705         var ctr = Roo.get(this.container.dom);
6706         
6707         var thd = this.getGridEl().select('thead',true).first();
6708         var tbd = this.getGridEl().select('tbody', true).first();
6709         
6710         
6711         var cw = ctr.getWidth();
6712         
6713         if (tbd) {
6714             
6715             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6716             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6717             cw -= barsize;
6718         }
6719         cw = Math.max(cw, this.totalWidth);
6720         this.getGridEl().select('tr',true).setWidth(cw);
6721         
6722         return; // we doe not have a view in this design..
6723         
6724     },
6725     onBodyScroll: function()
6726     {
6727         
6728         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6729         this.mainHead.setStyle({
6730                     'position' : 'relative',
6731                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6732         });
6733         
6734         
6735     }
6736 });
6737
6738  
6739
6740  /*
6741  * - LGPL
6742  *
6743  * table cell
6744  * 
6745  */
6746
6747 /**
6748  * @class Roo.bootstrap.TableCell
6749  * @extends Roo.bootstrap.Component
6750  * Bootstrap TableCell class
6751  * @cfg {String} html cell contain text
6752  * @cfg {String} cls cell class
6753  * @cfg {String} tag cell tag (td|th) default td
6754  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6755  * @cfg {String} align Aligns the content in a cell
6756  * @cfg {String} axis Categorizes cells
6757  * @cfg {String} bgcolor Specifies the background color of a cell
6758  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6759  * @cfg {Number} colspan Specifies the number of columns a cell should span
6760  * @cfg {String} headers Specifies one or more header cells a cell is related to
6761  * @cfg {Number} height Sets the height of a cell
6762  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6763  * @cfg {Number} rowspan Sets the number of rows a cell should span
6764  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6765  * @cfg {String} valign Vertical aligns the content in a cell
6766  * @cfg {Number} width Specifies the width of a cell
6767  * 
6768  * @constructor
6769  * Create a new TableCell
6770  * @param {Object} config The config object
6771  */
6772
6773 Roo.bootstrap.TableCell = function(config){
6774     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6775 };
6776
6777 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6778     
6779     html: false,
6780     cls: false,
6781     tag: false,
6782     abbr: false,
6783     align: false,
6784     axis: false,
6785     bgcolor: false,
6786     charoff: false,
6787     colspan: false,
6788     headers: false,
6789     height: false,
6790     nowrap: false,
6791     rowspan: false,
6792     scope: false,
6793     valign: false,
6794     width: false,
6795     
6796     
6797     getAutoCreate : function(){
6798         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6799         
6800         cfg = {
6801             tag: 'td'
6802         };
6803         
6804         if(this.tag){
6805             cfg.tag = this.tag;
6806         }
6807         
6808         if (this.html) {
6809             cfg.html=this.html
6810         }
6811         if (this.cls) {
6812             cfg.cls=this.cls
6813         }
6814         if (this.abbr) {
6815             cfg.abbr=this.abbr
6816         }
6817         if (this.align) {
6818             cfg.align=this.align
6819         }
6820         if (this.axis) {
6821             cfg.axis=this.axis
6822         }
6823         if (this.bgcolor) {
6824             cfg.bgcolor=this.bgcolor
6825         }
6826         if (this.charoff) {
6827             cfg.charoff=this.charoff
6828         }
6829         if (this.colspan) {
6830             cfg.colspan=this.colspan
6831         }
6832         if (this.headers) {
6833             cfg.headers=this.headers
6834         }
6835         if (this.height) {
6836             cfg.height=this.height
6837         }
6838         if (this.nowrap) {
6839             cfg.nowrap=this.nowrap
6840         }
6841         if (this.rowspan) {
6842             cfg.rowspan=this.rowspan
6843         }
6844         if (this.scope) {
6845             cfg.scope=this.scope
6846         }
6847         if (this.valign) {
6848             cfg.valign=this.valign
6849         }
6850         if (this.width) {
6851             cfg.width=this.width
6852         }
6853         
6854         
6855         return cfg;
6856     }
6857    
6858 });
6859
6860  
6861
6862  /*
6863  * - LGPL
6864  *
6865  * table row
6866  * 
6867  */
6868
6869 /**
6870  * @class Roo.bootstrap.TableRow
6871  * @extends Roo.bootstrap.Component
6872  * Bootstrap TableRow class
6873  * @cfg {String} cls row class
6874  * @cfg {String} align Aligns the content in a table row
6875  * @cfg {String} bgcolor Specifies a background color for a table row
6876  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6877  * @cfg {String} valign Vertical aligns the content in a table row
6878  * 
6879  * @constructor
6880  * Create a new TableRow
6881  * @param {Object} config The config object
6882  */
6883
6884 Roo.bootstrap.TableRow = function(config){
6885     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6886 };
6887
6888 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6889     
6890     cls: false,
6891     align: false,
6892     bgcolor: false,
6893     charoff: false,
6894     valign: false,
6895     
6896     getAutoCreate : function(){
6897         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6898         
6899         cfg = {
6900             tag: 'tr'
6901         };
6902             
6903         if(this.cls){
6904             cfg.cls = this.cls;
6905         }
6906         if(this.align){
6907             cfg.align = this.align;
6908         }
6909         if(this.bgcolor){
6910             cfg.bgcolor = this.bgcolor;
6911         }
6912         if(this.charoff){
6913             cfg.charoff = this.charoff;
6914         }
6915         if(this.valign){
6916             cfg.valign = this.valign;
6917         }
6918         
6919         return cfg;
6920     }
6921    
6922 });
6923
6924  
6925
6926  /*
6927  * - LGPL
6928  *
6929  * table body
6930  * 
6931  */
6932
6933 /**
6934  * @class Roo.bootstrap.TableBody
6935  * @extends Roo.bootstrap.Component
6936  * Bootstrap TableBody class
6937  * @cfg {String} cls element class
6938  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6939  * @cfg {String} align Aligns the content inside the element
6940  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6941  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6942  * 
6943  * @constructor
6944  * Create a new TableBody
6945  * @param {Object} config The config object
6946  */
6947
6948 Roo.bootstrap.TableBody = function(config){
6949     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6950 };
6951
6952 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6953     
6954     cls: false,
6955     tag: false,
6956     align: false,
6957     charoff: false,
6958     valign: false,
6959     
6960     getAutoCreate : function(){
6961         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6962         
6963         cfg = {
6964             tag: 'tbody'
6965         };
6966             
6967         if (this.cls) {
6968             cfg.cls=this.cls
6969         }
6970         if(this.tag){
6971             cfg.tag = this.tag;
6972         }
6973         
6974         if(this.align){
6975             cfg.align = this.align;
6976         }
6977         if(this.charoff){
6978             cfg.charoff = this.charoff;
6979         }
6980         if(this.valign){
6981             cfg.valign = this.valign;
6982         }
6983         
6984         return cfg;
6985     }
6986     
6987     
6988 //    initEvents : function()
6989 //    {
6990 //        
6991 //        if(!this.store){
6992 //            return;
6993 //        }
6994 //        
6995 //        this.store = Roo.factory(this.store, Roo.data);
6996 //        this.store.on('load', this.onLoad, this);
6997 //        
6998 //        this.store.load();
6999 //        
7000 //    },
7001 //    
7002 //    onLoad: function () 
7003 //    {   
7004 //        this.fireEvent('load', this);
7005 //    }
7006 //    
7007 //   
7008 });
7009
7010  
7011
7012  /*
7013  * Based on:
7014  * Ext JS Library 1.1.1
7015  * Copyright(c) 2006-2007, Ext JS, LLC.
7016  *
7017  * Originally Released Under LGPL - original licence link has changed is not relivant.
7018  *
7019  * Fork - LGPL
7020  * <script type="text/javascript">
7021  */
7022
7023 // as we use this in bootstrap.
7024 Roo.namespace('Roo.form');
7025  /**
7026  * @class Roo.form.Action
7027  * Internal Class used to handle form actions
7028  * @constructor
7029  * @param {Roo.form.BasicForm} el The form element or its id
7030  * @param {Object} config Configuration options
7031  */
7032
7033  
7034  
7035 // define the action interface
7036 Roo.form.Action = function(form, options){
7037     this.form = form;
7038     this.options = options || {};
7039 };
7040 /**
7041  * Client Validation Failed
7042  * @const 
7043  */
7044 Roo.form.Action.CLIENT_INVALID = 'client';
7045 /**
7046  * Server Validation Failed
7047  * @const 
7048  */
7049 Roo.form.Action.SERVER_INVALID = 'server';
7050  /**
7051  * Connect to Server Failed
7052  * @const 
7053  */
7054 Roo.form.Action.CONNECT_FAILURE = 'connect';
7055 /**
7056  * Reading Data from Server Failed
7057  * @const 
7058  */
7059 Roo.form.Action.LOAD_FAILURE = 'load';
7060
7061 Roo.form.Action.prototype = {
7062     type : 'default',
7063     failureType : undefined,
7064     response : undefined,
7065     result : undefined,
7066
7067     // interface method
7068     run : function(options){
7069
7070     },
7071
7072     // interface method
7073     success : function(response){
7074
7075     },
7076
7077     // interface method
7078     handleResponse : function(response){
7079
7080     },
7081
7082     // default connection failure
7083     failure : function(response){
7084         
7085         this.response = response;
7086         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7087         this.form.afterAction(this, false);
7088     },
7089
7090     processResponse : function(response){
7091         this.response = response;
7092         if(!response.responseText){
7093             return true;
7094         }
7095         this.result = this.handleResponse(response);
7096         return this.result;
7097     },
7098
7099     // utility functions used internally
7100     getUrl : function(appendParams){
7101         var url = this.options.url || this.form.url || this.form.el.dom.action;
7102         if(appendParams){
7103             var p = this.getParams();
7104             if(p){
7105                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7106             }
7107         }
7108         return url;
7109     },
7110
7111     getMethod : function(){
7112         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7113     },
7114
7115     getParams : function(){
7116         var bp = this.form.baseParams;
7117         var p = this.options.params;
7118         if(p){
7119             if(typeof p == "object"){
7120                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7121             }else if(typeof p == 'string' && bp){
7122                 p += '&' + Roo.urlEncode(bp);
7123             }
7124         }else if(bp){
7125             p = Roo.urlEncode(bp);
7126         }
7127         return p;
7128     },
7129
7130     createCallback : function(){
7131         return {
7132             success: this.success,
7133             failure: this.failure,
7134             scope: this,
7135             timeout: (this.form.timeout*1000),
7136             upload: this.form.fileUpload ? this.success : undefined
7137         };
7138     }
7139 };
7140
7141 Roo.form.Action.Submit = function(form, options){
7142     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7143 };
7144
7145 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7146     type : 'submit',
7147
7148     haveProgress : false,
7149     uploadComplete : false,
7150     
7151     // uploadProgress indicator.
7152     uploadProgress : function()
7153     {
7154         if (!this.form.progressUrl) {
7155             return;
7156         }
7157         
7158         if (!this.haveProgress) {
7159             Roo.MessageBox.progress("Uploading", "Uploading");
7160         }
7161         if (this.uploadComplete) {
7162            Roo.MessageBox.hide();
7163            return;
7164         }
7165         
7166         this.haveProgress = true;
7167    
7168         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7169         
7170         var c = new Roo.data.Connection();
7171         c.request({
7172             url : this.form.progressUrl,
7173             params: {
7174                 id : uid
7175             },
7176             method: 'GET',
7177             success : function(req){
7178                //console.log(data);
7179                 var rdata = false;
7180                 var edata;
7181                 try  {
7182                    rdata = Roo.decode(req.responseText)
7183                 } catch (e) {
7184                     Roo.log("Invalid data from server..");
7185                     Roo.log(edata);
7186                     return;
7187                 }
7188                 if (!rdata || !rdata.success) {
7189                     Roo.log(rdata);
7190                     Roo.MessageBox.alert(Roo.encode(rdata));
7191                     return;
7192                 }
7193                 var data = rdata.data;
7194                 
7195                 if (this.uploadComplete) {
7196                    Roo.MessageBox.hide();
7197                    return;
7198                 }
7199                    
7200                 if (data){
7201                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7202                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7203                     );
7204                 }
7205                 this.uploadProgress.defer(2000,this);
7206             },
7207        
7208             failure: function(data) {
7209                 Roo.log('progress url failed ');
7210                 Roo.log(data);
7211             },
7212             scope : this
7213         });
7214            
7215     },
7216     
7217     
7218     run : function()
7219     {
7220         // run get Values on the form, so it syncs any secondary forms.
7221         this.form.getValues();
7222         
7223         var o = this.options;
7224         var method = this.getMethod();
7225         var isPost = method == 'POST';
7226         if(o.clientValidation === false || this.form.isValid()){
7227             
7228             if (this.form.progressUrl) {
7229                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7230                     (new Date() * 1) + '' + Math.random());
7231                     
7232             } 
7233             
7234             
7235             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7236                 form:this.form.el.dom,
7237                 url:this.getUrl(!isPost),
7238                 method: method,
7239                 params:isPost ? this.getParams() : null,
7240                 isUpload: this.form.fileUpload
7241             }));
7242             
7243             this.uploadProgress();
7244
7245         }else if (o.clientValidation !== false){ // client validation failed
7246             this.failureType = Roo.form.Action.CLIENT_INVALID;
7247             this.form.afterAction(this, false);
7248         }
7249     },
7250
7251     success : function(response)
7252     {
7253         this.uploadComplete= true;
7254         if (this.haveProgress) {
7255             Roo.MessageBox.hide();
7256         }
7257         
7258         
7259         var result = this.processResponse(response);
7260         if(result === true || result.success){
7261             this.form.afterAction(this, true);
7262             return;
7263         }
7264         if(result.errors){
7265             this.form.markInvalid(result.errors);
7266             this.failureType = Roo.form.Action.SERVER_INVALID;
7267         }
7268         this.form.afterAction(this, false);
7269     },
7270     failure : function(response)
7271     {
7272         this.uploadComplete= true;
7273         if (this.haveProgress) {
7274             Roo.MessageBox.hide();
7275         }
7276         
7277         this.response = response;
7278         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7279         this.form.afterAction(this, false);
7280     },
7281     
7282     handleResponse : function(response){
7283         if(this.form.errorReader){
7284             var rs = this.form.errorReader.read(response);
7285             var errors = [];
7286             if(rs.records){
7287                 for(var i = 0, len = rs.records.length; i < len; i++) {
7288                     var r = rs.records[i];
7289                     errors[i] = r.data;
7290                 }
7291             }
7292             if(errors.length < 1){
7293                 errors = null;
7294             }
7295             return {
7296                 success : rs.success,
7297                 errors : errors
7298             };
7299         }
7300         var ret = false;
7301         try {
7302             ret = Roo.decode(response.responseText);
7303         } catch (e) {
7304             ret = {
7305                 success: false,
7306                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7307                 errors : []
7308             };
7309         }
7310         return ret;
7311         
7312     }
7313 });
7314
7315
7316 Roo.form.Action.Load = function(form, options){
7317     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7318     this.reader = this.form.reader;
7319 };
7320
7321 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7322     type : 'load',
7323
7324     run : function(){
7325         
7326         Roo.Ajax.request(Roo.apply(
7327                 this.createCallback(), {
7328                     method:this.getMethod(),
7329                     url:this.getUrl(false),
7330                     params:this.getParams()
7331         }));
7332     },
7333
7334     success : function(response){
7335         
7336         var result = this.processResponse(response);
7337         if(result === true || !result.success || !result.data){
7338             this.failureType = Roo.form.Action.LOAD_FAILURE;
7339             this.form.afterAction(this, false);
7340             return;
7341         }
7342         this.form.clearInvalid();
7343         this.form.setValues(result.data);
7344         this.form.afterAction(this, true);
7345     },
7346
7347     handleResponse : function(response){
7348         if(this.form.reader){
7349             var rs = this.form.reader.read(response);
7350             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7351             return {
7352                 success : rs.success,
7353                 data : data
7354             };
7355         }
7356         return Roo.decode(response.responseText);
7357     }
7358 });
7359
7360 Roo.form.Action.ACTION_TYPES = {
7361     'load' : Roo.form.Action.Load,
7362     'submit' : Roo.form.Action.Submit
7363 };/*
7364  * - LGPL
7365  *
7366  * form
7367  * 
7368  */
7369
7370 /**
7371  * @class Roo.bootstrap.Form
7372  * @extends Roo.bootstrap.Component
7373  * Bootstrap Form class
7374  * @cfg {String} method  GET | POST (default POST)
7375  * @cfg {String} labelAlign top | left (default top)
7376  * @cfg {String} align left  | right - for navbars
7377  * @cfg {Boolean} loadMask load mask when submit (default true)
7378
7379  * 
7380  * @constructor
7381  * Create a new Form
7382  * @param {Object} config The config object
7383  */
7384
7385
7386 Roo.bootstrap.Form = function(config){
7387     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7388     this.addEvents({
7389         /**
7390          * @event clientvalidation
7391          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7392          * @param {Form} this
7393          * @param {Boolean} valid true if the form has passed client-side validation
7394          */
7395         clientvalidation: true,
7396         /**
7397          * @event beforeaction
7398          * Fires before any action is performed. Return false to cancel the action.
7399          * @param {Form} this
7400          * @param {Action} action The action to be performed
7401          */
7402         beforeaction: true,
7403         /**
7404          * @event actionfailed
7405          * Fires when an action fails.
7406          * @param {Form} this
7407          * @param {Action} action The action that failed
7408          */
7409         actionfailed : true,
7410         /**
7411          * @event actioncomplete
7412          * Fires when an action is completed.
7413          * @param {Form} this
7414          * @param {Action} action The action that completed
7415          */
7416         actioncomplete : true
7417     });
7418     
7419 };
7420
7421 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7422       
7423      /**
7424      * @cfg {String} method
7425      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7426      */
7427     method : 'POST',
7428     /**
7429      * @cfg {String} url
7430      * The URL to use for form actions if one isn't supplied in the action options.
7431      */
7432     /**
7433      * @cfg {Boolean} fileUpload
7434      * Set to true if this form is a file upload.
7435      */
7436      
7437     /**
7438      * @cfg {Object} baseParams
7439      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7440      */
7441       
7442     /**
7443      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7444      */
7445     timeout: 30,
7446     /**
7447      * @cfg {Sting} align (left|right) for navbar forms
7448      */
7449     align : 'left',
7450
7451     // private
7452     activeAction : null,
7453  
7454     /**
7455      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7456      * element by passing it or its id or mask the form itself by passing in true.
7457      * @type Mixed
7458      */
7459     waitMsgTarget : false,
7460     
7461     loadMask : true,
7462     
7463     getAutoCreate : function(){
7464         
7465         var cfg = {
7466             tag: 'form',
7467             method : this.method || 'POST',
7468             id : this.id || Roo.id(),
7469             cls : ''
7470         };
7471         if (this.parent().xtype.match(/^Nav/)) {
7472             cfg.cls = 'navbar-form navbar-' + this.align;
7473             
7474         }
7475         
7476         if (this.labelAlign == 'left' ) {
7477             cfg.cls += ' form-horizontal';
7478         }
7479         
7480         
7481         return cfg;
7482     },
7483     initEvents : function()
7484     {
7485         this.el.on('submit', this.onSubmit, this);
7486         // this was added as random key presses on the form where triggering form submit.
7487         this.el.on('keypress', function(e) {
7488             if (e.getCharCode() != 13) {
7489                 return true;
7490             }
7491             // we might need to allow it for textareas.. and some other items.
7492             // check e.getTarget().
7493             
7494             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7495                 return true;
7496             }
7497         
7498             Roo.log("keypress blocked");
7499             
7500             e.preventDefault();
7501             return false;
7502         });
7503         
7504     },
7505     // private
7506     onSubmit : function(e){
7507         e.stopEvent();
7508     },
7509     
7510      /**
7511      * Returns true if client-side validation on the form is successful.
7512      * @return Boolean
7513      */
7514     isValid : function(){
7515         var items = this.getItems();
7516         var valid = true;
7517         items.each(function(f){
7518            if(!f.validate()){
7519                valid = false;
7520                
7521            }
7522         });
7523         return valid;
7524     },
7525     /**
7526      * Returns true if any fields in this form have changed since their original load.
7527      * @return Boolean
7528      */
7529     isDirty : function(){
7530         var dirty = false;
7531         var items = this.getItems();
7532         items.each(function(f){
7533            if(f.isDirty()){
7534                dirty = true;
7535                return false;
7536            }
7537            return true;
7538         });
7539         return dirty;
7540     },
7541      /**
7542      * Performs a predefined action (submit or load) or custom actions you define on this form.
7543      * @param {String} actionName The name of the action type
7544      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7545      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7546      * accept other config options):
7547      * <pre>
7548 Property          Type             Description
7549 ----------------  ---------------  ----------------------------------------------------------------------------------
7550 url               String           The url for the action (defaults to the form's url)
7551 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7552 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7553 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7554                                    validate the form on the client (defaults to false)
7555      * </pre>
7556      * @return {BasicForm} this
7557      */
7558     doAction : function(action, options){
7559         if(typeof action == 'string'){
7560             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7561         }
7562         if(this.fireEvent('beforeaction', this, action) !== false){
7563             this.beforeAction(action);
7564             action.run.defer(100, action);
7565         }
7566         return this;
7567     },
7568     
7569     // private
7570     beforeAction : function(action){
7571         var o = action.options;
7572         
7573         if(this.loadMask){
7574             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7575         }
7576         // not really supported yet.. ??
7577         
7578         //if(this.waitMsgTarget === true){
7579         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7580         //}else if(this.waitMsgTarget){
7581         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7582         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7583         //}else {
7584         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7585        // }
7586          
7587     },
7588
7589     // private
7590     afterAction : function(action, success){
7591         this.activeAction = null;
7592         var o = action.options;
7593         
7594         //if(this.waitMsgTarget === true){
7595             this.el.unmask();
7596         //}else if(this.waitMsgTarget){
7597         //    this.waitMsgTarget.unmask();
7598         //}else{
7599         //    Roo.MessageBox.updateProgress(1);
7600         //    Roo.MessageBox.hide();
7601        // }
7602         // 
7603         if(success){
7604             if(o.reset){
7605                 this.reset();
7606             }
7607             Roo.callback(o.success, o.scope, [this, action]);
7608             this.fireEvent('actioncomplete', this, action);
7609             
7610         }else{
7611             
7612             // failure condition..
7613             // we have a scenario where updates need confirming.
7614             // eg. if a locking scenario exists..
7615             // we look for { errors : { needs_confirm : true }} in the response.
7616             if (
7617                 (typeof(action.result) != 'undefined')  &&
7618                 (typeof(action.result.errors) != 'undefined')  &&
7619                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7620            ){
7621                 var _t = this;
7622                 Roo.log("not supported yet");
7623                  /*
7624                 
7625                 Roo.MessageBox.confirm(
7626                     "Change requires confirmation",
7627                     action.result.errorMsg,
7628                     function(r) {
7629                         if (r != 'yes') {
7630                             return;
7631                         }
7632                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7633                     }
7634                     
7635                 );
7636                 */
7637                 
7638                 
7639                 return;
7640             }
7641             
7642             Roo.callback(o.failure, o.scope, [this, action]);
7643             // show an error message if no failed handler is set..
7644             if (!this.hasListener('actionfailed')) {
7645                 Roo.log("need to add dialog support");
7646                 /*
7647                 Roo.MessageBox.alert("Error",
7648                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7649                         action.result.errorMsg :
7650                         "Saving Failed, please check your entries or try again"
7651                 );
7652                 */
7653             }
7654             
7655             this.fireEvent('actionfailed', this, action);
7656         }
7657         
7658     },
7659     /**
7660      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7661      * @param {String} id The value to search for
7662      * @return Field
7663      */
7664     findField : function(id){
7665         var items = this.getItems();
7666         var field = items.get(id);
7667         if(!field){
7668              items.each(function(f){
7669                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7670                     field = f;
7671                     return false;
7672                 }
7673                 return true;
7674             });
7675         }
7676         return field || null;
7677     },
7678      /**
7679      * Mark fields in this form invalid in bulk.
7680      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7681      * @return {BasicForm} this
7682      */
7683     markInvalid : function(errors){
7684         if(errors instanceof Array){
7685             for(var i = 0, len = errors.length; i < len; i++){
7686                 var fieldError = errors[i];
7687                 var f = this.findField(fieldError.id);
7688                 if(f){
7689                     f.markInvalid(fieldError.msg);
7690                 }
7691             }
7692         }else{
7693             var field, id;
7694             for(id in errors){
7695                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7696                     field.markInvalid(errors[id]);
7697                 }
7698             }
7699         }
7700         //Roo.each(this.childForms || [], function (f) {
7701         //    f.markInvalid(errors);
7702         //});
7703         
7704         return this;
7705     },
7706
7707     /**
7708      * Set values for fields in this form in bulk.
7709      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7710      * @return {BasicForm} this
7711      */
7712     setValues : function(values){
7713         if(values instanceof Array){ // array of objects
7714             for(var i = 0, len = values.length; i < len; i++){
7715                 var v = values[i];
7716                 var f = this.findField(v.id);
7717                 if(f){
7718                     f.setValue(v.value);
7719                     if(this.trackResetOnLoad){
7720                         f.originalValue = f.getValue();
7721                     }
7722                 }
7723             }
7724         }else{ // object hash
7725             var field, id;
7726             for(id in values){
7727                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7728                     
7729                     if (field.setFromData && 
7730                         field.valueField && 
7731                         field.displayField &&
7732                         // combos' with local stores can 
7733                         // be queried via setValue()
7734                         // to set their value..
7735                         (field.store && !field.store.isLocal)
7736                         ) {
7737                         // it's a combo
7738                         var sd = { };
7739                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7740                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7741                         field.setFromData(sd);
7742                         
7743                     } else {
7744                         field.setValue(values[id]);
7745                     }
7746                     
7747                     
7748                     if(this.trackResetOnLoad){
7749                         field.originalValue = field.getValue();
7750                     }
7751                 }
7752             }
7753         }
7754          
7755         //Roo.each(this.childForms || [], function (f) {
7756         //    f.setValues(values);
7757         //});
7758                 
7759         return this;
7760     },
7761
7762     /**
7763      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7764      * they are returned as an array.
7765      * @param {Boolean} asString
7766      * @return {Object}
7767      */
7768     getValues : function(asString){
7769         //if (this.childForms) {
7770             // copy values from the child forms
7771         //    Roo.each(this.childForms, function (f) {
7772         //        this.setValues(f.getValues());
7773         //    }, this);
7774         //}
7775         
7776         
7777         
7778         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7779         if(asString === true){
7780             return fs;
7781         }
7782         return Roo.urlDecode(fs);
7783     },
7784     
7785     /**
7786      * Returns the fields in this form as an object with key/value pairs. 
7787      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7788      * @return {Object}
7789      */
7790     getFieldValues : function(with_hidden)
7791     {
7792         var items = this.getItems();
7793         var ret = {};
7794         items.each(function(f){
7795             if (!f.getName()) {
7796                 return;
7797             }
7798             var v = f.getValue();
7799             if (f.inputType =='radio') {
7800                 if (typeof(ret[f.getName()]) == 'undefined') {
7801                     ret[f.getName()] = ''; // empty..
7802                 }
7803                 
7804                 if (!f.el.dom.checked) {
7805                     return;
7806                     
7807                 }
7808                 v = f.el.dom.value;
7809                 
7810             }
7811             
7812             // not sure if this supported any more..
7813             if ((typeof(v) == 'object') && f.getRawValue) {
7814                 v = f.getRawValue() ; // dates..
7815             }
7816             // combo boxes where name != hiddenName...
7817             if (f.name != f.getName()) {
7818                 ret[f.name] = f.getRawValue();
7819             }
7820             ret[f.getName()] = v;
7821         });
7822         
7823         return ret;
7824     },
7825
7826     /**
7827      * Clears all invalid messages in this form.
7828      * @return {BasicForm} this
7829      */
7830     clearInvalid : function(){
7831         var items = this.getItems();
7832         
7833         items.each(function(f){
7834            f.clearInvalid();
7835         });
7836         
7837         
7838         
7839         return this;
7840     },
7841
7842     /**
7843      * Resets this form.
7844      * @return {BasicForm} this
7845      */
7846     reset : function(){
7847         var items = this.getItems();
7848         items.each(function(f){
7849             f.reset();
7850         });
7851         
7852         Roo.each(this.childForms || [], function (f) {
7853             f.reset();
7854         });
7855        
7856         
7857         return this;
7858     },
7859     getItems : function()
7860     {
7861         var r=new Roo.util.MixedCollection(false, function(o){
7862             return o.id || (o.id = Roo.id());
7863         });
7864         var iter = function(el) {
7865             if (el.inputEl) {
7866                 r.add(el);
7867             }
7868             if (!el.items) {
7869                 return;
7870             }
7871             Roo.each(el.items,function(e) {
7872                 iter(e);
7873             });
7874             
7875             
7876         };
7877         
7878         iter(this);
7879         return r;
7880         
7881         
7882         
7883         
7884     }
7885     
7886 });
7887
7888  
7889 /*
7890  * Based on:
7891  * Ext JS Library 1.1.1
7892  * Copyright(c) 2006-2007, Ext JS, LLC.
7893  *
7894  * Originally Released Under LGPL - original licence link has changed is not relivant.
7895  *
7896  * Fork - LGPL
7897  * <script type="text/javascript">
7898  */
7899 /**
7900  * @class Roo.form.VTypes
7901  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7902  * @singleton
7903  */
7904 Roo.form.VTypes = function(){
7905     // closure these in so they are only created once.
7906     var alpha = /^[a-zA-Z_]+$/;
7907     var alphanum = /^[a-zA-Z0-9_]+$/;
7908     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7909     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7910
7911     // All these messages and functions are configurable
7912     return {
7913         /**
7914          * The function used to validate email addresses
7915          * @param {String} value The email address
7916          */
7917         'email' : function(v){
7918             return email.test(v);
7919         },
7920         /**
7921          * The error text to display when the email validation function returns false
7922          * @type String
7923          */
7924         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7925         /**
7926          * The keystroke filter mask to be applied on email input
7927          * @type RegExp
7928          */
7929         'emailMask' : /[a-z0-9_\.\-@]/i,
7930
7931         /**
7932          * The function used to validate URLs
7933          * @param {String} value The URL
7934          */
7935         'url' : function(v){
7936             return url.test(v);
7937         },
7938         /**
7939          * The error text to display when the url validation function returns false
7940          * @type String
7941          */
7942         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7943         
7944         /**
7945          * The function used to validate alpha values
7946          * @param {String} value The value
7947          */
7948         'alpha' : function(v){
7949             return alpha.test(v);
7950         },
7951         /**
7952          * The error text to display when the alpha validation function returns false
7953          * @type String
7954          */
7955         'alphaText' : 'This field should only contain letters and _',
7956         /**
7957          * The keystroke filter mask to be applied on alpha input
7958          * @type RegExp
7959          */
7960         'alphaMask' : /[a-z_]/i,
7961
7962         /**
7963          * The function used to validate alphanumeric values
7964          * @param {String} value The value
7965          */
7966         'alphanum' : function(v){
7967             return alphanum.test(v);
7968         },
7969         /**
7970          * The error text to display when the alphanumeric validation function returns false
7971          * @type String
7972          */
7973         'alphanumText' : 'This field should only contain letters, numbers and _',
7974         /**
7975          * The keystroke filter mask to be applied on alphanumeric input
7976          * @type RegExp
7977          */
7978         'alphanumMask' : /[a-z0-9_]/i
7979     };
7980 }();/*
7981  * - LGPL
7982  *
7983  * Input
7984  * 
7985  */
7986
7987 /**
7988  * @class Roo.bootstrap.Input
7989  * @extends Roo.bootstrap.Component
7990  * Bootstrap Input class
7991  * @cfg {Boolean} disabled is it disabled
7992  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7993  * @cfg {String} name name of the input
7994  * @cfg {string} fieldLabel - the label associated
7995  * @cfg {string} placeholder - placeholder to put in text.
7996  * @cfg {string}  before - input group add on before
7997  * @cfg {string} after - input group add on after
7998  * @cfg {string} size - (lg|sm) or leave empty..
7999  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8000  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8001  * @cfg {Number} md colspan out of 12 for computer-sized screens
8002  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8003  * @cfg {string} value default value of the input
8004  * @cfg {Number} labelWidth set the width of label (0-12)
8005  * @cfg {String} labelAlign (top|left)
8006  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8007  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8008
8009  * @cfg {String} align (left|center|right) Default left
8010  * @cfg {Boolean} forceFeedback (true|false) Default false
8011  * 
8012  * 
8013  * 
8014  * 
8015  * @constructor
8016  * Create a new Input
8017  * @param {Object} config The config object
8018  */
8019
8020 Roo.bootstrap.Input = function(config){
8021     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8022    
8023         this.addEvents({
8024             /**
8025              * @event focus
8026              * Fires when this field receives input focus.
8027              * @param {Roo.form.Field} this
8028              */
8029             focus : true,
8030             /**
8031              * @event blur
8032              * Fires when this field loses input focus.
8033              * @param {Roo.form.Field} this
8034              */
8035             blur : true,
8036             /**
8037              * @event specialkey
8038              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8039              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8040              * @param {Roo.form.Field} this
8041              * @param {Roo.EventObject} e The event object
8042              */
8043             specialkey : true,
8044             /**
8045              * @event change
8046              * Fires just before the field blurs if the field value has changed.
8047              * @param {Roo.form.Field} this
8048              * @param {Mixed} newValue The new value
8049              * @param {Mixed} oldValue The original value
8050              */
8051             change : true,
8052             /**
8053              * @event invalid
8054              * Fires after the field has been marked as invalid.
8055              * @param {Roo.form.Field} this
8056              * @param {String} msg The validation message
8057              */
8058             invalid : true,
8059             /**
8060              * @event valid
8061              * Fires after the field has been validated with no errors.
8062              * @param {Roo.form.Field} this
8063              */
8064             valid : true,
8065              /**
8066              * @event keyup
8067              * Fires after the key up
8068              * @param {Roo.form.Field} this
8069              * @param {Roo.EventObject}  e The event Object
8070              */
8071             keyup : true
8072         });
8073 };
8074
8075 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8076      /**
8077      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8078       automatic validation (defaults to "keyup").
8079      */
8080     validationEvent : "keyup",
8081      /**
8082      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8083      */
8084     validateOnBlur : true,
8085     /**
8086      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8087      */
8088     validationDelay : 250,
8089      /**
8090      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8091      */
8092     focusClass : "x-form-focus",  // not needed???
8093     
8094        
8095     /**
8096      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8097      */
8098     invalidClass : "has-warning",
8099     
8100     /**
8101      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8102      */
8103     validClass : "has-success",
8104     
8105     /**
8106      * @cfg {Boolean} hasFeedback (true|false) default true
8107      */
8108     hasFeedback : true,
8109     
8110     /**
8111      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8112      */
8113     invalidFeedbackClass : "glyphicon-warning-sign",
8114     
8115     /**
8116      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8117      */
8118     validFeedbackClass : "glyphicon-ok",
8119     
8120     /**
8121      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8122      */
8123     selectOnFocus : false,
8124     
8125      /**
8126      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8127      */
8128     maskRe : null,
8129        /**
8130      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8131      */
8132     vtype : null,
8133     
8134       /**
8135      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8136      */
8137     disableKeyFilter : false,
8138     
8139        /**
8140      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8141      */
8142     disabled : false,
8143      /**
8144      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8145      */
8146     allowBlank : true,
8147     /**
8148      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8149      */
8150     blankText : "This field is required",
8151     
8152      /**
8153      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8154      */
8155     minLength : 0,
8156     /**
8157      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8158      */
8159     maxLength : Number.MAX_VALUE,
8160     /**
8161      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8162      */
8163     minLengthText : "The minimum length for this field is {0}",
8164     /**
8165      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8166      */
8167     maxLengthText : "The maximum length for this field is {0}",
8168   
8169     
8170     /**
8171      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8172      * If available, this function will be called only after the basic validators all return true, and will be passed the
8173      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8174      */
8175     validator : null,
8176     /**
8177      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8178      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8179      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8180      */
8181     regex : null,
8182     /**
8183      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8184      */
8185     regexText : "",
8186     
8187     autocomplete: false,
8188     
8189     
8190     fieldLabel : '',
8191     inputType : 'text',
8192     
8193     name : false,
8194     placeholder: false,
8195     before : false,
8196     after : false,
8197     size : false,
8198     hasFocus : false,
8199     preventMark: false,
8200     isFormField : true,
8201     value : '',
8202     labelWidth : 2,
8203     labelAlign : false,
8204     readOnly : false,
8205     align : false,
8206     formatedValue : false,
8207     forceFeedback : false,
8208     
8209     parentLabelAlign : function()
8210     {
8211         var parent = this;
8212         while (parent.parent()) {
8213             parent = parent.parent();
8214             if (typeof(parent.labelAlign) !='undefined') {
8215                 return parent.labelAlign;
8216             }
8217         }
8218         return 'left';
8219         
8220     },
8221     
8222     getAutoCreate : function(){
8223         
8224         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8225         
8226         var id = Roo.id();
8227         
8228         var cfg = {};
8229         
8230        
8231         
8232         if(this.inputType != 'hidden'){
8233             cfg.cls = 'form-group' //input-group
8234         }
8235         
8236         var input =  {
8237             tag: 'input',
8238             id : id,
8239             type : this.inputType,
8240             value : this.value,
8241             cls : 'form-control',
8242             placeholder : this.placeholder || '',
8243             autocomplete : this.autocomplete || 'new-password'
8244         };
8245         
8246         
8247         if(this.align){
8248             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8249         }
8250         
8251         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8252             input.maxLength = this.maxLength;
8253         }
8254         
8255         if (this.disabled) {
8256             input.disabled=true;
8257         }
8258         
8259         if (this.readOnly) {
8260             input.readonly=true;
8261         }
8262         
8263         if (this.name) {
8264             input.name = this.name;
8265         }
8266         if (this.size) {
8267             input.cls += ' input-' + this.size;
8268         }
8269         var settings=this;
8270         ['xs','sm','md','lg'].map(function(size){
8271             if (settings[size]) {
8272                 cfg.cls += ' col-' + size + '-' + settings[size];
8273             }
8274         });
8275         
8276         var inputblock = input;
8277         
8278         var feedback = {
8279             tag: 'span',
8280             cls: 'glyphicon form-control-feedback'
8281         };
8282             
8283         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8284             
8285             inputblock = {
8286                 cls : 'has-feedback',
8287                 cn :  [
8288                     input,
8289                     feedback
8290                 ] 
8291             };  
8292         }
8293         
8294         if (this.before || this.after) {
8295             
8296             inputblock = {
8297                 cls : 'input-group',
8298                 cn :  [] 
8299             };
8300             
8301             if (this.before && typeof(this.before) == 'string') {
8302                 
8303                 inputblock.cn.push({
8304                     tag :'span',
8305                     cls : 'roo-input-before input-group-addon',
8306                     html : this.before
8307                 });
8308             }
8309             if (this.before && typeof(this.before) == 'object') {
8310                 this.before = Roo.factory(this.before);
8311                 
8312                 inputblock.cn.push({
8313                     tag :'span',
8314                     cls : 'roo-input-before input-group-' +
8315                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8316                 });
8317             }
8318             
8319             inputblock.cn.push(input);
8320             
8321             if (this.after && typeof(this.after) == 'string') {
8322                 inputblock.cn.push({
8323                     tag :'span',
8324                     cls : 'roo-input-after input-group-addon',
8325                     html : this.after
8326                 });
8327             }
8328             if (this.after && typeof(this.after) == 'object') {
8329                 this.after = Roo.factory(this.after);
8330                 
8331                 inputblock.cn.push({
8332                     tag :'span',
8333                     cls : 'roo-input-after input-group-' +
8334                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8335                 });
8336             }
8337             
8338             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8339                 inputblock.cls += ' has-feedback';
8340                 inputblock.cn.push(feedback);
8341             }
8342         };
8343         
8344         if (align ==='left' && this.fieldLabel.length) {
8345                 
8346                 cfg.cn = [
8347                     
8348                     {
8349                         tag: 'label',
8350                         'for' :  id,
8351                         cls : 'control-label col-sm-' + this.labelWidth,
8352                         html : this.fieldLabel
8353                         
8354                     },
8355                     {
8356                         cls : "col-sm-" + (12 - this.labelWidth), 
8357                         cn: [
8358                             inputblock
8359                         ]
8360                     }
8361                     
8362                 ];
8363         } else if ( this.fieldLabel.length) {
8364                 
8365                  cfg.cn = [
8366                    
8367                     {
8368                         tag: 'label',
8369                         //cls : 'input-group-addon',
8370                         html : this.fieldLabel
8371                         
8372                     },
8373                     
8374                     inputblock
8375                     
8376                 ];
8377
8378         } else {
8379             
8380                 cfg.cn = [
8381                     
8382                         inputblock
8383                     
8384                 ];
8385                 
8386                 
8387         };
8388         
8389         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8390            cfg.cls += ' navbar-form';
8391         }
8392         if (this.parentType === 'NavGroup') {
8393            cfg.cls += ' navbar-form';
8394            cfg.tag = 'li';
8395         }
8396         return cfg;
8397         
8398     },
8399     /**
8400      * return the real input element.
8401      */
8402     inputEl: function ()
8403     {
8404         return this.el.select('input.form-control',true).first();
8405     },
8406     
8407     tooltipEl : function()
8408     {
8409         return this.inputEl();
8410     },
8411     
8412     setDisabled : function(v)
8413     {
8414         var i  = this.inputEl().dom;
8415         if (!v) {
8416             i.removeAttribute('disabled');
8417             return;
8418             
8419         }
8420         i.setAttribute('disabled','true');
8421     },
8422     initEvents : function()
8423     {
8424           
8425         this.inputEl().on("keydown" , this.fireKey,  this);
8426         this.inputEl().on("focus", this.onFocus,  this);
8427         this.inputEl().on("blur", this.onBlur,  this);
8428         
8429         this.inputEl().relayEvent('keyup', this);
8430  
8431         // reference to original value for reset
8432         this.originalValue = this.getValue();
8433         //Roo.form.TextField.superclass.initEvents.call(this);
8434         if(this.validationEvent == 'keyup'){
8435             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8436             this.inputEl().on('keyup', this.filterValidation, this);
8437         }
8438         else if(this.validationEvent !== false){
8439             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8440         }
8441         
8442         if(this.selectOnFocus){
8443             this.on("focus", this.preFocus, this);
8444             
8445         }
8446         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8447             this.inputEl().on("keypress", this.filterKeys, this);
8448         }
8449        /* if(this.grow){
8450             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8451             this.el.on("click", this.autoSize,  this);
8452         }
8453         */
8454         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8455             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8456         }
8457         
8458         if (typeof(this.before) == 'object') {
8459             this.before.render(this.el.select('.roo-input-before',true).first());
8460         }
8461         if (typeof(this.after) == 'object') {
8462             this.after.render(this.el.select('.roo-input-after',true).first());
8463         }
8464         
8465         
8466     },
8467     filterValidation : function(e){
8468         if(!e.isNavKeyPress()){
8469             this.validationTask.delay(this.validationDelay);
8470         }
8471     },
8472      /**
8473      * Validates the field value
8474      * @return {Boolean} True if the value is valid, else false
8475      */
8476     validate : function(){
8477         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8478         if(this.disabled || this.validateValue(this.getRawValue())){
8479             this.markValid();
8480             return true;
8481         }
8482         
8483         this.markInvalid();
8484         return false;
8485     },
8486     
8487     
8488     /**
8489      * Validates a value according to the field's validation rules and marks the field as invalid
8490      * if the validation fails
8491      * @param {Mixed} value The value to validate
8492      * @return {Boolean} True if the value is valid, else false
8493      */
8494     validateValue : function(value){
8495         if(value.length < 1)  { // if it's blank
8496             if(this.allowBlank){
8497                 return true;
8498             }
8499             return false;
8500         }
8501         
8502         if(value.length < this.minLength){
8503             return false;
8504         }
8505         if(value.length > this.maxLength){
8506             return false;
8507         }
8508         if(this.vtype){
8509             var vt = Roo.form.VTypes;
8510             if(!vt[this.vtype](value, this)){
8511                 return false;
8512             }
8513         }
8514         if(typeof this.validator == "function"){
8515             var msg = this.validator(value);
8516             if(msg !== true){
8517                 return false;
8518             }
8519         }
8520         
8521         if(this.regex && !this.regex.test(value)){
8522             return false;
8523         }
8524         
8525         return true;
8526     },
8527
8528     
8529     
8530      // private
8531     fireKey : function(e){
8532         //Roo.log('field ' + e.getKey());
8533         if(e.isNavKeyPress()){
8534             this.fireEvent("specialkey", this, e);
8535         }
8536     },
8537     focus : function (selectText){
8538         if(this.rendered){
8539             this.inputEl().focus();
8540             if(selectText === true){
8541                 this.inputEl().dom.select();
8542             }
8543         }
8544         return this;
8545     } ,
8546     
8547     onFocus : function(){
8548         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8549            // this.el.addClass(this.focusClass);
8550         }
8551         if(!this.hasFocus){
8552             this.hasFocus = true;
8553             this.startValue = this.getValue();
8554             this.fireEvent("focus", this);
8555         }
8556     },
8557     
8558     beforeBlur : Roo.emptyFn,
8559
8560     
8561     // private
8562     onBlur : function(){
8563         this.beforeBlur();
8564         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8565             //this.el.removeClass(this.focusClass);
8566         }
8567         this.hasFocus = false;
8568         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8569             this.validate();
8570         }
8571         var v = this.getValue();
8572         if(String(v) !== String(this.startValue)){
8573             this.fireEvent('change', this, v, this.startValue);
8574         }
8575         this.fireEvent("blur", this);
8576     },
8577     
8578     /**
8579      * Resets the current field value to the originally loaded value and clears any validation messages
8580      */
8581     reset : function(){
8582         this.setValue(this.originalValue);
8583         this.validate();
8584     },
8585      /**
8586      * Returns the name of the field
8587      * @return {Mixed} name The name field
8588      */
8589     getName: function(){
8590         return this.name;
8591     },
8592      /**
8593      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8594      * @return {Mixed} value The field value
8595      */
8596     getValue : function(){
8597         
8598         var v = this.inputEl().getValue();
8599         
8600         return v;
8601     },
8602     /**
8603      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8604      * @return {Mixed} value The field value
8605      */
8606     getRawValue : function(){
8607         var v = this.inputEl().getValue();
8608         
8609         return v;
8610     },
8611     
8612     /**
8613      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8614      * @param {Mixed} value The value to set
8615      */
8616     setRawValue : function(v){
8617         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8618     },
8619     
8620     selectText : function(start, end){
8621         var v = this.getRawValue();
8622         if(v.length > 0){
8623             start = start === undefined ? 0 : start;
8624             end = end === undefined ? v.length : end;
8625             var d = this.inputEl().dom;
8626             if(d.setSelectionRange){
8627                 d.setSelectionRange(start, end);
8628             }else if(d.createTextRange){
8629                 var range = d.createTextRange();
8630                 range.moveStart("character", start);
8631                 range.moveEnd("character", v.length-end);
8632                 range.select();
8633             }
8634         }
8635     },
8636     
8637     /**
8638      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8639      * @param {Mixed} value The value to set
8640      */
8641     setValue : function(v){
8642         this.value = v;
8643         if(this.rendered){
8644             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8645             this.validate();
8646         }
8647     },
8648     
8649     /*
8650     processValue : function(value){
8651         if(this.stripCharsRe){
8652             var newValue = value.replace(this.stripCharsRe, '');
8653             if(newValue !== value){
8654                 this.setRawValue(newValue);
8655                 return newValue;
8656             }
8657         }
8658         return value;
8659     },
8660   */
8661     preFocus : function(){
8662         
8663         if(this.selectOnFocus){
8664             this.inputEl().dom.select();
8665         }
8666     },
8667     filterKeys : function(e){
8668         var k = e.getKey();
8669         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8670             return;
8671         }
8672         var c = e.getCharCode(), cc = String.fromCharCode(c);
8673         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8674             return;
8675         }
8676         if(!this.maskRe.test(cc)){
8677             e.stopEvent();
8678         }
8679     },
8680      /**
8681      * Clear any invalid styles/messages for this field
8682      */
8683     clearInvalid : function(){
8684         
8685         if(!this.el || this.preventMark){ // not rendered
8686             return;
8687         }
8688         
8689         var label = this.el.select('label', true).first();
8690         var icon = this.el.select('i.fa-star', true).first();
8691         
8692         if(label && icon){
8693             icon.remove();
8694         }
8695         
8696         this.el.removeClass(this.invalidClass);
8697         
8698         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8699             
8700             var feedback = this.el.select('.form-control-feedback', true).first();
8701             
8702             if(feedback){
8703                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8704             }
8705             
8706         }
8707         
8708         this.fireEvent('valid', this);
8709     },
8710     
8711      /**
8712      * Mark this field as valid
8713      */
8714     markValid : function()
8715     {
8716         if(!this.el  || this.preventMark){ // not rendered
8717             return;
8718         }
8719         
8720         this.el.removeClass([this.invalidClass, this.validClass]);
8721         
8722         var feedback = this.el.select('.form-control-feedback', true).first();
8723             
8724         if(feedback){
8725             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8726         }
8727
8728         if(this.disabled || this.allowBlank){
8729             return;
8730         }
8731         
8732         var formGroup = this.el.findParent('.form-group', false, true);
8733         
8734         if(formGroup){
8735             
8736             var label = formGroup.select('label', true).first();
8737             var icon = formGroup.select('i.fa-star', true).first();
8738             
8739             if(label && icon){
8740                 icon.remove();
8741             }
8742         }
8743         
8744         this.el.addClass(this.validClass);
8745         
8746         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8747             
8748             var feedback = this.el.select('.form-control-feedback', true).first();
8749             
8750             if(feedback){
8751                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8752                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8753             }
8754             
8755         }
8756         
8757         this.fireEvent('valid', this);
8758     },
8759     
8760      /**
8761      * Mark this field as invalid
8762      * @param {String} msg The validation message
8763      */
8764     markInvalid : function(msg)
8765     {
8766         if(!this.el  || this.preventMark){ // not rendered
8767             return;
8768         }
8769         
8770         this.el.removeClass([this.invalidClass, this.validClass]);
8771         
8772         var feedback = this.el.select('.form-control-feedback', true).first();
8773             
8774         if(feedback){
8775             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8776         }
8777
8778         if(this.disabled || this.allowBlank){
8779             return;
8780         }
8781         
8782         var formGroup = this.el.findParent('.form-group', false, true);
8783         
8784         if(formGroup){
8785             var label = formGroup.select('label', true).first();
8786             var icon = formGroup.select('i.fa-star', true).first();
8787
8788             if(!this.getValue().length && label && !icon){
8789                 this.el.findParent('.form-group', false, true).createChild({
8790                     tag : 'i',
8791                     cls : 'text-danger fa fa-lg fa-star',
8792                     tooltip : 'This field is required',
8793                     style : 'margin-right:5px;'
8794                 }, label, true);
8795             }
8796         }
8797         
8798         
8799         this.el.addClass(this.invalidClass);
8800         
8801         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8802             
8803             var feedback = this.el.select('.form-control-feedback', true).first();
8804             
8805             if(feedback){
8806                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8807                 
8808                 if(this.getValue().length || this.forceFeedback){
8809                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8810                 }
8811                 
8812             }
8813             
8814         }
8815         
8816         this.fireEvent('invalid', this, msg);
8817     },
8818     // private
8819     SafariOnKeyDown : function(event)
8820     {
8821         // this is a workaround for a password hang bug on chrome/ webkit.
8822         
8823         var isSelectAll = false;
8824         
8825         if(this.inputEl().dom.selectionEnd > 0){
8826             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8827         }
8828         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8829             event.preventDefault();
8830             this.setValue('');
8831             return;
8832         }
8833         
8834         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8835             
8836             event.preventDefault();
8837             // this is very hacky as keydown always get's upper case.
8838             //
8839             var cc = String.fromCharCode(event.getCharCode());
8840             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8841             
8842         }
8843     },
8844     adjustWidth : function(tag, w){
8845         tag = tag.toLowerCase();
8846         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8847             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8848                 if(tag == 'input'){
8849                     return w + 2;
8850                 }
8851                 if(tag == 'textarea'){
8852                     return w-2;
8853                 }
8854             }else if(Roo.isOpera){
8855                 if(tag == 'input'){
8856                     return w + 2;
8857                 }
8858                 if(tag == 'textarea'){
8859                     return w-2;
8860                 }
8861             }
8862         }
8863         return w;
8864     }
8865     
8866 });
8867
8868  
8869 /*
8870  * - LGPL
8871  *
8872  * Input
8873  * 
8874  */
8875
8876 /**
8877  * @class Roo.bootstrap.TextArea
8878  * @extends Roo.bootstrap.Input
8879  * Bootstrap TextArea class
8880  * @cfg {Number} cols Specifies the visible width of a text area
8881  * @cfg {Number} rows Specifies the visible number of lines in a text area
8882  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8883  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8884  * @cfg {string} html text
8885  * 
8886  * @constructor
8887  * Create a new TextArea
8888  * @param {Object} config The config object
8889  */
8890
8891 Roo.bootstrap.TextArea = function(config){
8892     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8893    
8894 };
8895
8896 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8897      
8898     cols : false,
8899     rows : 5,
8900     readOnly : false,
8901     warp : 'soft',
8902     resize : false,
8903     value: false,
8904     html: false,
8905     
8906     getAutoCreate : function(){
8907         
8908         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8909         
8910         var id = Roo.id();
8911         
8912         var cfg = {};
8913         
8914         var input =  {
8915             tag: 'textarea',
8916             id : id,
8917             warp : this.warp,
8918             rows : this.rows,
8919             value : this.value || '',
8920             html: this.html || '',
8921             cls : 'form-control',
8922             placeholder : this.placeholder || '' 
8923             
8924         };
8925         
8926         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8927             input.maxLength = this.maxLength;
8928         }
8929         
8930         if(this.resize){
8931             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8932         }
8933         
8934         if(this.cols){
8935             input.cols = this.cols;
8936         }
8937         
8938         if (this.readOnly) {
8939             input.readonly = true;
8940         }
8941         
8942         if (this.name) {
8943             input.name = this.name;
8944         }
8945         
8946         if (this.size) {
8947             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8948         }
8949         
8950         var settings=this;
8951         ['xs','sm','md','lg'].map(function(size){
8952             if (settings[size]) {
8953                 cfg.cls += ' col-' + size + '-' + settings[size];
8954             }
8955         });
8956         
8957         var inputblock = input;
8958         
8959         if(this.hasFeedback && !this.allowBlank){
8960             
8961             var feedback = {
8962                 tag: 'span',
8963                 cls: 'glyphicon form-control-feedback'
8964             };
8965
8966             inputblock = {
8967                 cls : 'has-feedback',
8968                 cn :  [
8969                     input,
8970                     feedback
8971                 ] 
8972             };  
8973         }
8974         
8975         
8976         if (this.before || this.after) {
8977             
8978             inputblock = {
8979                 cls : 'input-group',
8980                 cn :  [] 
8981             };
8982             if (this.before) {
8983                 inputblock.cn.push({
8984                     tag :'span',
8985                     cls : 'input-group-addon',
8986                     html : this.before
8987                 });
8988             }
8989             
8990             inputblock.cn.push(input);
8991             
8992             if(this.hasFeedback && !this.allowBlank){
8993                 inputblock.cls += ' has-feedback';
8994                 inputblock.cn.push(feedback);
8995             }
8996             
8997             if (this.after) {
8998                 inputblock.cn.push({
8999                     tag :'span',
9000                     cls : 'input-group-addon',
9001                     html : this.after
9002                 });
9003             }
9004             
9005         }
9006         
9007         if (align ==='left' && this.fieldLabel.length) {
9008 //                Roo.log("left and has label");
9009                 cfg.cn = [
9010                     
9011                     {
9012                         tag: 'label',
9013                         'for' :  id,
9014                         cls : 'control-label col-sm-' + this.labelWidth,
9015                         html : this.fieldLabel
9016                         
9017                     },
9018                     {
9019                         cls : "col-sm-" + (12 - this.labelWidth), 
9020                         cn: [
9021                             inputblock
9022                         ]
9023                     }
9024                     
9025                 ];
9026         } else if ( this.fieldLabel.length) {
9027 //                Roo.log(" label");
9028                  cfg.cn = [
9029                    
9030                     {
9031                         tag: 'label',
9032                         //cls : 'input-group-addon',
9033                         html : this.fieldLabel
9034                         
9035                     },
9036                     
9037                     inputblock
9038                     
9039                 ];
9040
9041         } else {
9042             
9043 //                   Roo.log(" no label && no align");
9044                 cfg.cn = [
9045                     
9046                         inputblock
9047                     
9048                 ];
9049                 
9050                 
9051         }
9052         
9053         if (this.disabled) {
9054             input.disabled=true;
9055         }
9056         
9057         return cfg;
9058         
9059     },
9060     /**
9061      * return the real textarea element.
9062      */
9063     inputEl: function ()
9064     {
9065         return this.el.select('textarea.form-control',true).first();
9066     },
9067     
9068     /**
9069      * Clear any invalid styles/messages for this field
9070      */
9071     clearInvalid : function()
9072     {
9073         
9074         if(!this.el || this.preventMark){ // not rendered
9075             return;
9076         }
9077         
9078         var label = this.el.select('label', true).first();
9079         var icon = this.el.select('i.fa-star', true).first();
9080         
9081         if(label && icon){
9082             icon.remove();
9083         }
9084         
9085         this.el.removeClass(this.invalidClass);
9086         
9087         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9088             
9089             var feedback = this.el.select('.form-control-feedback', true).first();
9090             
9091             if(feedback){
9092                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9093             }
9094             
9095         }
9096         
9097         this.fireEvent('valid', this);
9098     },
9099     
9100      /**
9101      * Mark this field as valid
9102      */
9103     markValid : function()
9104     {
9105         if(!this.el  || this.preventMark){ // not rendered
9106             return;
9107         }
9108         
9109         this.el.removeClass([this.invalidClass, this.validClass]);
9110         
9111         var feedback = this.el.select('.form-control-feedback', true).first();
9112             
9113         if(feedback){
9114             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9115         }
9116
9117         if(this.disabled || this.allowBlank){
9118             return;
9119         }
9120         
9121         var label = this.el.select('label', true).first();
9122         var icon = this.el.select('i.fa-star', true).first();
9123         
9124         if(label && icon){
9125             icon.remove();
9126         }
9127         
9128         this.el.addClass(this.validClass);
9129         
9130         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9131             
9132             var feedback = this.el.select('.form-control-feedback', true).first();
9133             
9134             if(feedback){
9135                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9136                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9137             }
9138             
9139         }
9140         
9141         this.fireEvent('valid', this);
9142     },
9143     
9144      /**
9145      * Mark this field as invalid
9146      * @param {String} msg The validation message
9147      */
9148     markInvalid : function(msg)
9149     {
9150         if(!this.el  || this.preventMark){ // not rendered
9151             return;
9152         }
9153         
9154         this.el.removeClass([this.invalidClass, this.validClass]);
9155         
9156         var feedback = this.el.select('.form-control-feedback', true).first();
9157             
9158         if(feedback){
9159             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9160         }
9161
9162         if(this.disabled || this.allowBlank){
9163             return;
9164         }
9165         
9166         var label = this.el.select('label', true).first();
9167         var icon = this.el.select('i.fa-star', true).first();
9168         
9169         if(!this.getValue().length && label && !icon){
9170             this.el.createChild({
9171                 tag : 'i',
9172                 cls : 'text-danger fa fa-lg fa-star',
9173                 tooltip : 'This field is required',
9174                 style : 'margin-right:5px;'
9175             }, label, true);
9176         }
9177
9178         this.el.addClass(this.invalidClass);
9179         
9180         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9181             
9182             var feedback = this.el.select('.form-control-feedback', true).first();
9183             
9184             if(feedback){
9185                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9186                 
9187                 if(this.getValue().length || this.forceFeedback){
9188                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9189                 }
9190                 
9191             }
9192             
9193         }
9194         
9195         this.fireEvent('invalid', this, msg);
9196     }
9197 });
9198
9199  
9200 /*
9201  * - LGPL
9202  *
9203  * trigger field - base class for combo..
9204  * 
9205  */
9206  
9207 /**
9208  * @class Roo.bootstrap.TriggerField
9209  * @extends Roo.bootstrap.Input
9210  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9211  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9212  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9213  * for which you can provide a custom implementation.  For example:
9214  * <pre><code>
9215 var trigger = new Roo.bootstrap.TriggerField();
9216 trigger.onTriggerClick = myTriggerFn;
9217 trigger.applyTo('my-field');
9218 </code></pre>
9219  *
9220  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9221  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9222  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9223  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9224  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9225
9226  * @constructor
9227  * Create a new TriggerField.
9228  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9229  * to the base TextField)
9230  */
9231 Roo.bootstrap.TriggerField = function(config){
9232     this.mimicing = false;
9233     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9234 };
9235
9236 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9237     /**
9238      * @cfg {String} triggerClass A CSS class to apply to the trigger
9239      */
9240      /**
9241      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9242      */
9243     hideTrigger:false,
9244
9245     /**
9246      * @cfg {Boolean} removable (true|false) special filter default false
9247      */
9248     removable : false,
9249     
9250     /** @cfg {Boolean} grow @hide */
9251     /** @cfg {Number} growMin @hide */
9252     /** @cfg {Number} growMax @hide */
9253
9254     /**
9255      * @hide 
9256      * @method
9257      */
9258     autoSize: Roo.emptyFn,
9259     // private
9260     monitorTab : true,
9261     // private
9262     deferHeight : true,
9263
9264     
9265     actionMode : 'wrap',
9266     
9267     caret : false,
9268     
9269     
9270     getAutoCreate : function(){
9271        
9272         var align = this.labelAlign || this.parentLabelAlign();
9273         
9274         var id = Roo.id();
9275         
9276         var cfg = {
9277             cls: 'form-group' //input-group
9278         };
9279         
9280         
9281         var input =  {
9282             tag: 'input',
9283             id : id,
9284             type : this.inputType,
9285             cls : 'form-control',
9286             autocomplete: 'new-password',
9287             placeholder : this.placeholder || '' 
9288             
9289         };
9290         if (this.name) {
9291             input.name = this.name;
9292         }
9293         if (this.size) {
9294             input.cls += ' input-' + this.size;
9295         }
9296         
9297         if (this.disabled) {
9298             input.disabled=true;
9299         }
9300         
9301         var inputblock = input;
9302         
9303         if(this.hasFeedback && !this.allowBlank){
9304             
9305             var feedback = {
9306                 tag: 'span',
9307                 cls: 'glyphicon form-control-feedback'
9308             };
9309             
9310             if(this.removable && !this.editable && !this.tickable){
9311                 inputblock = {
9312                     cls : 'has-feedback',
9313                     cn :  [
9314                         inputblock,
9315                         {
9316                             tag: 'button',
9317                             html : 'x',
9318                             cls : 'roo-combo-removable-btn close'
9319                         },
9320                         feedback
9321                     ] 
9322                 };
9323             } else {
9324                 inputblock = {
9325                     cls : 'has-feedback',
9326                     cn :  [
9327                         inputblock,
9328                         feedback
9329                     ] 
9330                 };
9331             }
9332
9333         } else {
9334             if(this.removable && !this.editable && !this.tickable){
9335                 inputblock = {
9336                     cls : 'roo-removable',
9337                     cn :  [
9338                         inputblock,
9339                         {
9340                             tag: 'button',
9341                             html : 'x',
9342                             cls : 'roo-combo-removable-btn close'
9343                         }
9344                     ] 
9345                 };
9346             }
9347         }
9348         
9349         if (this.before || this.after) {
9350             
9351             inputblock = {
9352                 cls : 'input-group',
9353                 cn :  [] 
9354             };
9355             if (this.before) {
9356                 inputblock.cn.push({
9357                     tag :'span',
9358                     cls : 'input-group-addon',
9359                     html : this.before
9360                 });
9361             }
9362             
9363             inputblock.cn.push(input);
9364             
9365             if(this.hasFeedback && !this.allowBlank){
9366                 inputblock.cls += ' has-feedback';
9367                 inputblock.cn.push(feedback);
9368             }
9369             
9370             if (this.after) {
9371                 inputblock.cn.push({
9372                     tag :'span',
9373                     cls : 'input-group-addon',
9374                     html : this.after
9375                 });
9376             }
9377             
9378         };
9379         
9380         var box = {
9381             tag: 'div',
9382             cn: [
9383                 {
9384                     tag: 'input',
9385                     type : 'hidden',
9386                     cls: 'form-hidden-field'
9387                 },
9388                 inputblock
9389             ]
9390             
9391         };
9392         
9393         if(this.multiple){
9394             box = {
9395                 tag: 'div',
9396                 cn: [
9397                     {
9398                         tag: 'input',
9399                         type : 'hidden',
9400                         cls: 'form-hidden-field'
9401                     },
9402                     {
9403                         tag: 'ul',
9404                         cls: 'roo-select2-choices',
9405                         cn:[
9406                             {
9407                                 tag: 'li',
9408                                 cls: 'roo-select2-search-field',
9409                                 cn: [
9410
9411                                     inputblock
9412                                 ]
9413                             }
9414                         ]
9415                     }
9416                 ]
9417             }
9418         };
9419         
9420         var combobox = {
9421             cls: 'roo-select2-container input-group',
9422             cn: [
9423                 box
9424 //                {
9425 //                    tag: 'ul',
9426 //                    cls: 'typeahead typeahead-long dropdown-menu',
9427 //                    style: 'display:none'
9428 //                }
9429             ]
9430         };
9431         
9432         if(!this.multiple && this.showToggleBtn){
9433             
9434             var caret = {
9435                         tag: 'span',
9436                         cls: 'caret'
9437              };
9438             if (this.caret != false) {
9439                 caret = {
9440                      tag: 'i',
9441                      cls: 'fa fa-' + this.caret
9442                 };
9443                 
9444             }
9445             
9446             combobox.cn.push({
9447                 tag :'span',
9448                 cls : 'input-group-addon btn dropdown-toggle',
9449                 cn : [
9450                     caret,
9451                     {
9452                         tag: 'span',
9453                         cls: 'combobox-clear',
9454                         cn  : [
9455                             {
9456                                 tag : 'i',
9457                                 cls: 'icon-remove'
9458                             }
9459                         ]
9460                     }
9461                 ]
9462
9463             })
9464         }
9465         
9466         if(this.multiple){
9467             combobox.cls += ' roo-select2-container-multi';
9468         }
9469         
9470         if (align ==='left' && this.fieldLabel.length) {
9471             
9472 //                Roo.log("left and has label");
9473                 cfg.cn = [
9474                     
9475                     {
9476                         tag: 'label',
9477                         'for' :  id,
9478                         cls : 'control-label col-sm-' + this.labelWidth,
9479                         html : this.fieldLabel
9480                         
9481                     },
9482                     {
9483                         cls : "col-sm-" + (12 - this.labelWidth), 
9484                         cn: [
9485                             combobox
9486                         ]
9487                     }
9488                     
9489                 ];
9490         } else if ( this.fieldLabel.length) {
9491 //                Roo.log(" label");
9492                  cfg.cn = [
9493                    
9494                     {
9495                         tag: 'label',
9496                         //cls : 'input-group-addon',
9497                         html : this.fieldLabel
9498                         
9499                     },
9500                     
9501                     combobox
9502                     
9503                 ];
9504
9505         } else {
9506             
9507 //                Roo.log(" no label && no align");
9508                 cfg = combobox
9509                      
9510                 
9511         }
9512          
9513         var settings=this;
9514         ['xs','sm','md','lg'].map(function(size){
9515             if (settings[size]) {
9516                 cfg.cls += ' col-' + size + '-' + settings[size];
9517             }
9518         });
9519         
9520         return cfg;
9521         
9522     },
9523     
9524     
9525     
9526     // private
9527     onResize : function(w, h){
9528 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9529 //        if(typeof w == 'number'){
9530 //            var x = w - this.trigger.getWidth();
9531 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9532 //            this.trigger.setStyle('left', x+'px');
9533 //        }
9534     },
9535
9536     // private
9537     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9538
9539     // private
9540     getResizeEl : function(){
9541         return this.inputEl();
9542     },
9543
9544     // private
9545     getPositionEl : function(){
9546         return this.inputEl();
9547     },
9548
9549     // private
9550     alignErrorIcon : function(){
9551         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9552     },
9553
9554     // private
9555     initEvents : function(){
9556         
9557         this.createList();
9558         
9559         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9560         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9561         if(!this.multiple && this.showToggleBtn){
9562             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9563             if(this.hideTrigger){
9564                 this.trigger.setDisplayed(false);
9565             }
9566             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9567         }
9568         
9569         if(this.multiple){
9570             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9571         }
9572         
9573         if(this.removable && !this.editable && !this.tickable){
9574             var close = this.closeTriggerEl();
9575             
9576             if(close){
9577                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9578                 close.on('click', this.removeBtnClick, this, close);
9579             }
9580         }
9581         
9582         //this.trigger.addClassOnOver('x-form-trigger-over');
9583         //this.trigger.addClassOnClick('x-form-trigger-click');
9584         
9585         //if(!this.width){
9586         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9587         //}
9588     },
9589     
9590     closeTriggerEl : function()
9591     {
9592         var close = this.el.select('.roo-combo-removable-btn', true).first();
9593         return close ? close : false;
9594     },
9595     
9596     removeBtnClick : function(e, h, el)
9597     {
9598         e.preventDefault();
9599         
9600         if(this.fireEvent("remove", this) !== false){
9601             this.reset();
9602             this.fireEvent("afterremove", this)
9603         }
9604     },
9605     
9606     createList : function()
9607     {
9608         this.list = Roo.get(document.body).createChild({
9609             tag: 'ul',
9610             cls: 'typeahead typeahead-long dropdown-menu',
9611             style: 'display:none'
9612         });
9613         
9614         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9615         
9616     },
9617
9618     // private
9619     initTrigger : function(){
9620        
9621     },
9622
9623     // private
9624     onDestroy : function(){
9625         if(this.trigger){
9626             this.trigger.removeAllListeners();
9627           //  this.trigger.remove();
9628         }
9629         //if(this.wrap){
9630         //    this.wrap.remove();
9631         //}
9632         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9633     },
9634
9635     // private
9636     onFocus : function(){
9637         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9638         /*
9639         if(!this.mimicing){
9640             this.wrap.addClass('x-trigger-wrap-focus');
9641             this.mimicing = true;
9642             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9643             if(this.monitorTab){
9644                 this.el.on("keydown", this.checkTab, this);
9645             }
9646         }
9647         */
9648     },
9649
9650     // private
9651     checkTab : function(e){
9652         if(e.getKey() == e.TAB){
9653             this.triggerBlur();
9654         }
9655     },
9656
9657     // private
9658     onBlur : function(){
9659         // do nothing
9660     },
9661
9662     // private
9663     mimicBlur : function(e, t){
9664         /*
9665         if(!this.wrap.contains(t) && this.validateBlur()){
9666             this.triggerBlur();
9667         }
9668         */
9669     },
9670
9671     // private
9672     triggerBlur : function(){
9673         this.mimicing = false;
9674         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9675         if(this.monitorTab){
9676             this.el.un("keydown", this.checkTab, this);
9677         }
9678         //this.wrap.removeClass('x-trigger-wrap-focus');
9679         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9680     },
9681
9682     // private
9683     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9684     validateBlur : function(e, t){
9685         return true;
9686     },
9687
9688     // private
9689     onDisable : function(){
9690         this.inputEl().dom.disabled = true;
9691         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9692         //if(this.wrap){
9693         //    this.wrap.addClass('x-item-disabled');
9694         //}
9695     },
9696
9697     // private
9698     onEnable : function(){
9699         this.inputEl().dom.disabled = false;
9700         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9701         //if(this.wrap){
9702         //    this.el.removeClass('x-item-disabled');
9703         //}
9704     },
9705
9706     // private
9707     onShow : function(){
9708         var ae = this.getActionEl();
9709         
9710         if(ae){
9711             ae.dom.style.display = '';
9712             ae.dom.style.visibility = 'visible';
9713         }
9714     },
9715
9716     // private
9717     
9718     onHide : function(){
9719         var ae = this.getActionEl();
9720         ae.dom.style.display = 'none';
9721     },
9722
9723     /**
9724      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9725      * by an implementing function.
9726      * @method
9727      * @param {EventObject} e
9728      */
9729     onTriggerClick : Roo.emptyFn
9730 });
9731  /*
9732  * Based on:
9733  * Ext JS Library 1.1.1
9734  * Copyright(c) 2006-2007, Ext JS, LLC.
9735  *
9736  * Originally Released Under LGPL - original licence link has changed is not relivant.
9737  *
9738  * Fork - LGPL
9739  * <script type="text/javascript">
9740  */
9741
9742
9743 /**
9744  * @class Roo.data.SortTypes
9745  * @singleton
9746  * Defines the default sorting (casting?) comparison functions used when sorting data.
9747  */
9748 Roo.data.SortTypes = {
9749     /**
9750      * Default sort that does nothing
9751      * @param {Mixed} s The value being converted
9752      * @return {Mixed} The comparison value
9753      */
9754     none : function(s){
9755         return s;
9756     },
9757     
9758     /**
9759      * The regular expression used to strip tags
9760      * @type {RegExp}
9761      * @property
9762      */
9763     stripTagsRE : /<\/?[^>]+>/gi,
9764     
9765     /**
9766      * Strips all HTML tags to sort on text only
9767      * @param {Mixed} s The value being converted
9768      * @return {String} The comparison value
9769      */
9770     asText : function(s){
9771         return String(s).replace(this.stripTagsRE, "");
9772     },
9773     
9774     /**
9775      * Strips all HTML tags to sort on text only - Case insensitive
9776      * @param {Mixed} s The value being converted
9777      * @return {String} The comparison value
9778      */
9779     asUCText : function(s){
9780         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9781     },
9782     
9783     /**
9784      * Case insensitive string
9785      * @param {Mixed} s The value being converted
9786      * @return {String} The comparison value
9787      */
9788     asUCString : function(s) {
9789         return String(s).toUpperCase();
9790     },
9791     
9792     /**
9793      * Date sorting
9794      * @param {Mixed} s The value being converted
9795      * @return {Number} The comparison value
9796      */
9797     asDate : function(s) {
9798         if(!s){
9799             return 0;
9800         }
9801         if(s instanceof Date){
9802             return s.getTime();
9803         }
9804         return Date.parse(String(s));
9805     },
9806     
9807     /**
9808      * Float sorting
9809      * @param {Mixed} s The value being converted
9810      * @return {Float} The comparison value
9811      */
9812     asFloat : function(s) {
9813         var val = parseFloat(String(s).replace(/,/g, ""));
9814         if(isNaN(val)) {
9815             val = 0;
9816         }
9817         return val;
9818     },
9819     
9820     /**
9821      * Integer sorting
9822      * @param {Mixed} s The value being converted
9823      * @return {Number} The comparison value
9824      */
9825     asInt : function(s) {
9826         var val = parseInt(String(s).replace(/,/g, ""));
9827         if(isNaN(val)) {
9828             val = 0;
9829         }
9830         return val;
9831     }
9832 };/*
9833  * Based on:
9834  * Ext JS Library 1.1.1
9835  * Copyright(c) 2006-2007, Ext JS, LLC.
9836  *
9837  * Originally Released Under LGPL - original licence link has changed is not relivant.
9838  *
9839  * Fork - LGPL
9840  * <script type="text/javascript">
9841  */
9842
9843 /**
9844 * @class Roo.data.Record
9845  * Instances of this class encapsulate both record <em>definition</em> information, and record
9846  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9847  * to access Records cached in an {@link Roo.data.Store} object.<br>
9848  * <p>
9849  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9850  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9851  * objects.<br>
9852  * <p>
9853  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9854  * @constructor
9855  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9856  * {@link #create}. The parameters are the same.
9857  * @param {Array} data An associative Array of data values keyed by the field name.
9858  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9859  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9860  * not specified an integer id is generated.
9861  */
9862 Roo.data.Record = function(data, id){
9863     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9864     this.data = data;
9865 };
9866
9867 /**
9868  * Generate a constructor for a specific record layout.
9869  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9870  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9871  * Each field definition object may contain the following properties: <ul>
9872  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
9873  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9874  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9875  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9876  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9877  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9878  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9879  * this may be omitted.</p></li>
9880  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9881  * <ul><li>auto (Default, implies no conversion)</li>
9882  * <li>string</li>
9883  * <li>int</li>
9884  * <li>float</li>
9885  * <li>boolean</li>
9886  * <li>date</li></ul></p></li>
9887  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9888  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9889  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9890  * by the Reader into an object that will be stored in the Record. It is passed the
9891  * following parameters:<ul>
9892  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9893  * </ul></p></li>
9894  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9895  * </ul>
9896  * <br>usage:<br><pre><code>
9897 var TopicRecord = Roo.data.Record.create(
9898     {name: 'title', mapping: 'topic_title'},
9899     {name: 'author', mapping: 'username'},
9900     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9901     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9902     {name: 'lastPoster', mapping: 'user2'},
9903     {name: 'excerpt', mapping: 'post_text'}
9904 );
9905
9906 var myNewRecord = new TopicRecord({
9907     title: 'Do my job please',
9908     author: 'noobie',
9909     totalPosts: 1,
9910     lastPost: new Date(),
9911     lastPoster: 'Animal',
9912     excerpt: 'No way dude!'
9913 });
9914 myStore.add(myNewRecord);
9915 </code></pre>
9916  * @method create
9917  * @static
9918  */
9919 Roo.data.Record.create = function(o){
9920     var f = function(){
9921         f.superclass.constructor.apply(this, arguments);
9922     };
9923     Roo.extend(f, Roo.data.Record);
9924     var p = f.prototype;
9925     p.fields = new Roo.util.MixedCollection(false, function(field){
9926         return field.name;
9927     });
9928     for(var i = 0, len = o.length; i < len; i++){
9929         p.fields.add(new Roo.data.Field(o[i]));
9930     }
9931     f.getField = function(name){
9932         return p.fields.get(name);  
9933     };
9934     return f;
9935 };
9936
9937 Roo.data.Record.AUTO_ID = 1000;
9938 Roo.data.Record.EDIT = 'edit';
9939 Roo.data.Record.REJECT = 'reject';
9940 Roo.data.Record.COMMIT = 'commit';
9941
9942 Roo.data.Record.prototype = {
9943     /**
9944      * Readonly flag - true if this record has been modified.
9945      * @type Boolean
9946      */
9947     dirty : false,
9948     editing : false,
9949     error: null,
9950     modified: null,
9951
9952     // private
9953     join : function(store){
9954         this.store = store;
9955     },
9956
9957     /**
9958      * Set the named field to the specified value.
9959      * @param {String} name The name of the field to set.
9960      * @param {Object} value The value to set the field to.
9961      */
9962     set : function(name, value){
9963         if(this.data[name] == value){
9964             return;
9965         }
9966         this.dirty = true;
9967         if(!this.modified){
9968             this.modified = {};
9969         }
9970         if(typeof this.modified[name] == 'undefined'){
9971             this.modified[name] = this.data[name];
9972         }
9973         this.data[name] = value;
9974         if(!this.editing && this.store){
9975             this.store.afterEdit(this);
9976         }       
9977     },
9978
9979     /**
9980      * Get the value of the named field.
9981      * @param {String} name The name of the field to get the value of.
9982      * @return {Object} The value of the field.
9983      */
9984     get : function(name){
9985         return this.data[name]; 
9986     },
9987
9988     // private
9989     beginEdit : function(){
9990         this.editing = true;
9991         this.modified = {}; 
9992     },
9993
9994     // private
9995     cancelEdit : function(){
9996         this.editing = false;
9997         delete this.modified;
9998     },
9999
10000     // private
10001     endEdit : function(){
10002         this.editing = false;
10003         if(this.dirty && this.store){
10004             this.store.afterEdit(this);
10005         }
10006     },
10007
10008     /**
10009      * Usually called by the {@link Roo.data.Store} which owns the Record.
10010      * Rejects all changes made to the Record since either creation, or the last commit operation.
10011      * Modified fields are reverted to their original values.
10012      * <p>
10013      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10014      * of reject operations.
10015      */
10016     reject : function(){
10017         var m = this.modified;
10018         for(var n in m){
10019             if(typeof m[n] != "function"){
10020                 this.data[n] = m[n];
10021             }
10022         }
10023         this.dirty = false;
10024         delete this.modified;
10025         this.editing = false;
10026         if(this.store){
10027             this.store.afterReject(this);
10028         }
10029     },
10030
10031     /**
10032      * Usually called by the {@link Roo.data.Store} which owns the Record.
10033      * Commits all changes made to the Record since either creation, or the last commit operation.
10034      * <p>
10035      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10036      * of commit operations.
10037      */
10038     commit : function(){
10039         this.dirty = false;
10040         delete this.modified;
10041         this.editing = false;
10042         if(this.store){
10043             this.store.afterCommit(this);
10044         }
10045     },
10046
10047     // private
10048     hasError : function(){
10049         return this.error != null;
10050     },
10051
10052     // private
10053     clearError : function(){
10054         this.error = null;
10055     },
10056
10057     /**
10058      * Creates a copy of this record.
10059      * @param {String} id (optional) A new record id if you don't want to use this record's id
10060      * @return {Record}
10061      */
10062     copy : function(newId) {
10063         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10064     }
10065 };/*
10066  * Based on:
10067  * Ext JS Library 1.1.1
10068  * Copyright(c) 2006-2007, Ext JS, LLC.
10069  *
10070  * Originally Released Under LGPL - original licence link has changed is not relivant.
10071  *
10072  * Fork - LGPL
10073  * <script type="text/javascript">
10074  */
10075
10076
10077
10078 /**
10079  * @class Roo.data.Store
10080  * @extends Roo.util.Observable
10081  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10082  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10083  * <p>
10084  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10085  * has no knowledge of the format of the data returned by the Proxy.<br>
10086  * <p>
10087  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10088  * instances from the data object. These records are cached and made available through accessor functions.
10089  * @constructor
10090  * Creates a new Store.
10091  * @param {Object} config A config object containing the objects needed for the Store to access data,
10092  * and read the data into Records.
10093  */
10094 Roo.data.Store = function(config){
10095     this.data = new Roo.util.MixedCollection(false);
10096     this.data.getKey = function(o){
10097         return o.id;
10098     };
10099     this.baseParams = {};
10100     // private
10101     this.paramNames = {
10102         "start" : "start",
10103         "limit" : "limit",
10104         "sort" : "sort",
10105         "dir" : "dir",
10106         "multisort" : "_multisort"
10107     };
10108
10109     if(config && config.data){
10110         this.inlineData = config.data;
10111         delete config.data;
10112     }
10113
10114     Roo.apply(this, config);
10115     
10116     if(this.reader){ // reader passed
10117         this.reader = Roo.factory(this.reader, Roo.data);
10118         this.reader.xmodule = this.xmodule || false;
10119         if(!this.recordType){
10120             this.recordType = this.reader.recordType;
10121         }
10122         if(this.reader.onMetaChange){
10123             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10124         }
10125     }
10126
10127     if(this.recordType){
10128         this.fields = this.recordType.prototype.fields;
10129     }
10130     this.modified = [];
10131
10132     this.addEvents({
10133         /**
10134          * @event datachanged
10135          * Fires when the data cache has changed, and a widget which is using this Store
10136          * as a Record cache should refresh its view.
10137          * @param {Store} this
10138          */
10139         datachanged : true,
10140         /**
10141          * @event metachange
10142          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10143          * @param {Store} this
10144          * @param {Object} meta The JSON metadata
10145          */
10146         metachange : true,
10147         /**
10148          * @event add
10149          * Fires when Records have been added to the Store
10150          * @param {Store} this
10151          * @param {Roo.data.Record[]} records The array of Records added
10152          * @param {Number} index The index at which the record(s) were added
10153          */
10154         add : true,
10155         /**
10156          * @event remove
10157          * Fires when a Record has been removed from the Store
10158          * @param {Store} this
10159          * @param {Roo.data.Record} record The Record that was removed
10160          * @param {Number} index The index at which the record was removed
10161          */
10162         remove : true,
10163         /**
10164          * @event update
10165          * Fires when a Record has been updated
10166          * @param {Store} this
10167          * @param {Roo.data.Record} record The Record that was updated
10168          * @param {String} operation The update operation being performed.  Value may be one of:
10169          * <pre><code>
10170  Roo.data.Record.EDIT
10171  Roo.data.Record.REJECT
10172  Roo.data.Record.COMMIT
10173          * </code></pre>
10174          */
10175         update : true,
10176         /**
10177          * @event clear
10178          * Fires when the data cache has been cleared.
10179          * @param {Store} this
10180          */
10181         clear : true,
10182         /**
10183          * @event beforeload
10184          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10185          * the load action will be canceled.
10186          * @param {Store} this
10187          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10188          */
10189         beforeload : true,
10190         /**
10191          * @event beforeloadadd
10192          * Fires after a new set of Records has been loaded.
10193          * @param {Store} this
10194          * @param {Roo.data.Record[]} records The Records that were loaded
10195          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10196          */
10197         beforeloadadd : true,
10198         /**
10199          * @event load
10200          * Fires after a new set of Records has been loaded, before they are added to the store.
10201          * @param {Store} this
10202          * @param {Roo.data.Record[]} records The Records that were loaded
10203          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10204          * @params {Object} return from reader
10205          */
10206         load : true,
10207         /**
10208          * @event loadexception
10209          * Fires if an exception occurs in the Proxy during loading.
10210          * Called with the signature of the Proxy's "loadexception" event.
10211          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10212          * 
10213          * @param {Proxy} 
10214          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10215          * @param {Object} load options 
10216          * @param {Object} jsonData from your request (normally this contains the Exception)
10217          */
10218         loadexception : true
10219     });
10220     
10221     if(this.proxy){
10222         this.proxy = Roo.factory(this.proxy, Roo.data);
10223         this.proxy.xmodule = this.xmodule || false;
10224         this.relayEvents(this.proxy,  ["loadexception"]);
10225     }
10226     this.sortToggle = {};
10227     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10228
10229     Roo.data.Store.superclass.constructor.call(this);
10230
10231     if(this.inlineData){
10232         this.loadData(this.inlineData);
10233         delete this.inlineData;
10234     }
10235 };
10236
10237 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10238      /**
10239     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10240     * without a remote query - used by combo/forms at present.
10241     */
10242     
10243     /**
10244     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10245     */
10246     /**
10247     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10248     */
10249     /**
10250     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10251     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10252     */
10253     /**
10254     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10255     * on any HTTP request
10256     */
10257     /**
10258     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10259     */
10260     /**
10261     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10262     */
10263     multiSort: false,
10264     /**
10265     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10266     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10267     */
10268     remoteSort : false,
10269
10270     /**
10271     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10272      * loaded or when a record is removed. (defaults to false).
10273     */
10274     pruneModifiedRecords : false,
10275
10276     // private
10277     lastOptions : null,
10278
10279     /**
10280      * Add Records to the Store and fires the add event.
10281      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10282      */
10283     add : function(records){
10284         records = [].concat(records);
10285         for(var i = 0, len = records.length; i < len; i++){
10286             records[i].join(this);
10287         }
10288         var index = this.data.length;
10289         this.data.addAll(records);
10290         this.fireEvent("add", this, records, index);
10291     },
10292
10293     /**
10294      * Remove a Record from the Store and fires the remove event.
10295      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10296      */
10297     remove : function(record){
10298         var index = this.data.indexOf(record);
10299         this.data.removeAt(index);
10300         if(this.pruneModifiedRecords){
10301             this.modified.remove(record);
10302         }
10303         this.fireEvent("remove", this, record, index);
10304     },
10305
10306     /**
10307      * Remove all Records from the Store and fires the clear event.
10308      */
10309     removeAll : function(){
10310         this.data.clear();
10311         if(this.pruneModifiedRecords){
10312             this.modified = [];
10313         }
10314         this.fireEvent("clear", this);
10315     },
10316
10317     /**
10318      * Inserts Records to the Store at the given index and fires the add event.
10319      * @param {Number} index The start index at which to insert the passed Records.
10320      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10321      */
10322     insert : function(index, records){
10323         records = [].concat(records);
10324         for(var i = 0, len = records.length; i < len; i++){
10325             this.data.insert(index, records[i]);
10326             records[i].join(this);
10327         }
10328         this.fireEvent("add", this, records, index);
10329     },
10330
10331     /**
10332      * Get the index within the cache of the passed Record.
10333      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10334      * @return {Number} The index of the passed Record. Returns -1 if not found.
10335      */
10336     indexOf : function(record){
10337         return this.data.indexOf(record);
10338     },
10339
10340     /**
10341      * Get the index within the cache of the Record with the passed id.
10342      * @param {String} id The id of the Record to find.
10343      * @return {Number} The index of the Record. Returns -1 if not found.
10344      */
10345     indexOfId : function(id){
10346         return this.data.indexOfKey(id);
10347     },
10348
10349     /**
10350      * Get the Record with the specified id.
10351      * @param {String} id The id of the Record to find.
10352      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10353      */
10354     getById : function(id){
10355         return this.data.key(id);
10356     },
10357
10358     /**
10359      * Get the Record at the specified index.
10360      * @param {Number} index The index of the Record to find.
10361      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10362      */
10363     getAt : function(index){
10364         return this.data.itemAt(index);
10365     },
10366
10367     /**
10368      * Returns a range of Records between specified indices.
10369      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10370      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10371      * @return {Roo.data.Record[]} An array of Records
10372      */
10373     getRange : function(start, end){
10374         return this.data.getRange(start, end);
10375     },
10376
10377     // private
10378     storeOptions : function(o){
10379         o = Roo.apply({}, o);
10380         delete o.callback;
10381         delete o.scope;
10382         this.lastOptions = o;
10383     },
10384
10385     /**
10386      * Loads the Record cache from the configured Proxy using the configured Reader.
10387      * <p>
10388      * If using remote paging, then the first load call must specify the <em>start</em>
10389      * and <em>limit</em> properties in the options.params property to establish the initial
10390      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10391      * <p>
10392      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10393      * and this call will return before the new data has been loaded. Perform any post-processing
10394      * in a callback function, or in a "load" event handler.</strong>
10395      * <p>
10396      * @param {Object} options An object containing properties which control loading options:<ul>
10397      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10398      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10399      * passed the following arguments:<ul>
10400      * <li>r : Roo.data.Record[]</li>
10401      * <li>options: Options object from the load call</li>
10402      * <li>success: Boolean success indicator</li></ul></li>
10403      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10404      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10405      * </ul>
10406      */
10407     load : function(options){
10408         options = options || {};
10409         if(this.fireEvent("beforeload", this, options) !== false){
10410             this.storeOptions(options);
10411             var p = Roo.apply(options.params || {}, this.baseParams);
10412             // if meta was not loaded from remote source.. try requesting it.
10413             if (!this.reader.metaFromRemote) {
10414                 p._requestMeta = 1;
10415             }
10416             if(this.sortInfo && this.remoteSort){
10417                 var pn = this.paramNames;
10418                 p[pn["sort"]] = this.sortInfo.field;
10419                 p[pn["dir"]] = this.sortInfo.direction;
10420             }
10421             if (this.multiSort) {
10422                 var pn = this.paramNames;
10423                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10424             }
10425             
10426             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10427         }
10428     },
10429
10430     /**
10431      * Reloads the Record cache from the configured Proxy using the configured Reader and
10432      * the options from the last load operation performed.
10433      * @param {Object} options (optional) An object containing properties which may override the options
10434      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10435      * the most recently used options are reused).
10436      */
10437     reload : function(options){
10438         this.load(Roo.applyIf(options||{}, this.lastOptions));
10439     },
10440
10441     // private
10442     // Called as a callback by the Reader during a load operation.
10443     loadRecords : function(o, options, success){
10444         if(!o || success === false){
10445             if(success !== false){
10446                 this.fireEvent("load", this, [], options, o);
10447             }
10448             if(options.callback){
10449                 options.callback.call(options.scope || this, [], options, false);
10450             }
10451             return;
10452         }
10453         // if data returned failure - throw an exception.
10454         if (o.success === false) {
10455             // show a message if no listener is registered.
10456             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10457                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10458             }
10459             // loadmask wil be hooked into this..
10460             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10461             return;
10462         }
10463         var r = o.records, t = o.totalRecords || r.length;
10464         
10465         this.fireEvent("beforeloadadd", this, r, options, o);
10466         
10467         if(!options || options.add !== true){
10468             if(this.pruneModifiedRecords){
10469                 this.modified = [];
10470             }
10471             for(var i = 0, len = r.length; i < len; i++){
10472                 r[i].join(this);
10473             }
10474             if(this.snapshot){
10475                 this.data = this.snapshot;
10476                 delete this.snapshot;
10477             }
10478             this.data.clear();
10479             this.data.addAll(r);
10480             this.totalLength = t;
10481             this.applySort();
10482             this.fireEvent("datachanged", this);
10483         }else{
10484             this.totalLength = Math.max(t, this.data.length+r.length);
10485             this.add(r);
10486         }
10487         this.fireEvent("load", this, r, options, o);
10488         if(options.callback){
10489             options.callback.call(options.scope || this, r, options, true);
10490         }
10491     },
10492
10493
10494     /**
10495      * Loads data from a passed data block. A Reader which understands the format of the data
10496      * must have been configured in the constructor.
10497      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10498      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10499      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10500      */
10501     loadData : function(o, append){
10502         var r = this.reader.readRecords(o);
10503         this.loadRecords(r, {add: append}, true);
10504     },
10505
10506     /**
10507      * Gets the number of cached records.
10508      * <p>
10509      * <em>If using paging, this may not be the total size of the dataset. If the data object
10510      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10511      * the data set size</em>
10512      */
10513     getCount : function(){
10514         return this.data.length || 0;
10515     },
10516
10517     /**
10518      * Gets the total number of records in the dataset as returned by the server.
10519      * <p>
10520      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10521      * the dataset size</em>
10522      */
10523     getTotalCount : function(){
10524         return this.totalLength || 0;
10525     },
10526
10527     /**
10528      * Returns the sort state of the Store as an object with two properties:
10529      * <pre><code>
10530  field {String} The name of the field by which the Records are sorted
10531  direction {String} The sort order, "ASC" or "DESC"
10532      * </code></pre>
10533      */
10534     getSortState : function(){
10535         return this.sortInfo;
10536     },
10537
10538     // private
10539     applySort : function(){
10540         if(this.sortInfo && !this.remoteSort){
10541             var s = this.sortInfo, f = s.field;
10542             var st = this.fields.get(f).sortType;
10543             var fn = function(r1, r2){
10544                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10545                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10546             };
10547             this.data.sort(s.direction, fn);
10548             if(this.snapshot && this.snapshot != this.data){
10549                 this.snapshot.sort(s.direction, fn);
10550             }
10551         }
10552     },
10553
10554     /**
10555      * Sets the default sort column and order to be used by the next load operation.
10556      * @param {String} fieldName The name of the field to sort by.
10557      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10558      */
10559     setDefaultSort : function(field, dir){
10560         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10561     },
10562
10563     /**
10564      * Sort the Records.
10565      * If remote sorting is used, the sort is performed on the server, and the cache is
10566      * reloaded. If local sorting is used, the cache is sorted internally.
10567      * @param {String} fieldName The name of the field to sort by.
10568      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10569      */
10570     sort : function(fieldName, dir){
10571         var f = this.fields.get(fieldName);
10572         if(!dir){
10573             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10574             
10575             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10576                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10577             }else{
10578                 dir = f.sortDir;
10579             }
10580         }
10581         this.sortToggle[f.name] = dir;
10582         this.sortInfo = {field: f.name, direction: dir};
10583         if(!this.remoteSort){
10584             this.applySort();
10585             this.fireEvent("datachanged", this);
10586         }else{
10587             this.load(this.lastOptions);
10588         }
10589     },
10590
10591     /**
10592      * Calls the specified function for each of the Records in the cache.
10593      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10594      * Returning <em>false</em> aborts and exits the iteration.
10595      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10596      */
10597     each : function(fn, scope){
10598         this.data.each(fn, scope);
10599     },
10600
10601     /**
10602      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10603      * (e.g., during paging).
10604      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10605      */
10606     getModifiedRecords : function(){
10607         return this.modified;
10608     },
10609
10610     // private
10611     createFilterFn : function(property, value, anyMatch){
10612         if(!value.exec){ // not a regex
10613             value = String(value);
10614             if(value.length == 0){
10615                 return false;
10616             }
10617             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10618         }
10619         return function(r){
10620             return value.test(r.data[property]);
10621         };
10622     },
10623
10624     /**
10625      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10626      * @param {String} property A field on your records
10627      * @param {Number} start The record index to start at (defaults to 0)
10628      * @param {Number} end The last record index to include (defaults to length - 1)
10629      * @return {Number} The sum
10630      */
10631     sum : function(property, start, end){
10632         var rs = this.data.items, v = 0;
10633         start = start || 0;
10634         end = (end || end === 0) ? end : rs.length-1;
10635
10636         for(var i = start; i <= end; i++){
10637             v += (rs[i].data[property] || 0);
10638         }
10639         return v;
10640     },
10641
10642     /**
10643      * Filter the records by a specified property.
10644      * @param {String} field A field on your records
10645      * @param {String/RegExp} value Either a string that the field
10646      * should start with or a RegExp to test against the field
10647      * @param {Boolean} anyMatch True to match any part not just the beginning
10648      */
10649     filter : function(property, value, anyMatch){
10650         var fn = this.createFilterFn(property, value, anyMatch);
10651         return fn ? this.filterBy(fn) : this.clearFilter();
10652     },
10653
10654     /**
10655      * Filter by a function. The specified function will be called with each
10656      * record in this data source. If the function returns true the record is included,
10657      * otherwise it is filtered.
10658      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10659      * @param {Object} scope (optional) The scope of the function (defaults to this)
10660      */
10661     filterBy : function(fn, scope){
10662         this.snapshot = this.snapshot || this.data;
10663         this.data = this.queryBy(fn, scope||this);
10664         this.fireEvent("datachanged", this);
10665     },
10666
10667     /**
10668      * Query the records by a specified property.
10669      * @param {String} field A field on your records
10670      * @param {String/RegExp} value Either a string that the field
10671      * should start with or a RegExp to test against the field
10672      * @param {Boolean} anyMatch True to match any part not just the beginning
10673      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10674      */
10675     query : function(property, value, anyMatch){
10676         var fn = this.createFilterFn(property, value, anyMatch);
10677         return fn ? this.queryBy(fn) : this.data.clone();
10678     },
10679
10680     /**
10681      * Query by a function. The specified function will be called with each
10682      * record in this data source. If the function returns true the record is included
10683      * in the results.
10684      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10685      * @param {Object} scope (optional) The scope of the function (defaults to this)
10686       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10687      **/
10688     queryBy : function(fn, scope){
10689         var data = this.snapshot || this.data;
10690         return data.filterBy(fn, scope||this);
10691     },
10692
10693     /**
10694      * Collects unique values for a particular dataIndex from this store.
10695      * @param {String} dataIndex The property to collect
10696      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10697      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10698      * @return {Array} An array of the unique values
10699      **/
10700     collect : function(dataIndex, allowNull, bypassFilter){
10701         var d = (bypassFilter === true && this.snapshot) ?
10702                 this.snapshot.items : this.data.items;
10703         var v, sv, r = [], l = {};
10704         for(var i = 0, len = d.length; i < len; i++){
10705             v = d[i].data[dataIndex];
10706             sv = String(v);
10707             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10708                 l[sv] = true;
10709                 r[r.length] = v;
10710             }
10711         }
10712         return r;
10713     },
10714
10715     /**
10716      * Revert to a view of the Record cache with no filtering applied.
10717      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10718      */
10719     clearFilter : function(suppressEvent){
10720         if(this.snapshot && this.snapshot != this.data){
10721             this.data = this.snapshot;
10722             delete this.snapshot;
10723             if(suppressEvent !== true){
10724                 this.fireEvent("datachanged", this);
10725             }
10726         }
10727     },
10728
10729     // private
10730     afterEdit : function(record){
10731         if(this.modified.indexOf(record) == -1){
10732             this.modified.push(record);
10733         }
10734         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10735     },
10736     
10737     // private
10738     afterReject : function(record){
10739         this.modified.remove(record);
10740         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10741     },
10742
10743     // private
10744     afterCommit : function(record){
10745         this.modified.remove(record);
10746         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10747     },
10748
10749     /**
10750      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10751      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10752      */
10753     commitChanges : function(){
10754         var m = this.modified.slice(0);
10755         this.modified = [];
10756         for(var i = 0, len = m.length; i < len; i++){
10757             m[i].commit();
10758         }
10759     },
10760
10761     /**
10762      * Cancel outstanding changes on all changed records.
10763      */
10764     rejectChanges : function(){
10765         var m = this.modified.slice(0);
10766         this.modified = [];
10767         for(var i = 0, len = m.length; i < len; i++){
10768             m[i].reject();
10769         }
10770     },
10771
10772     onMetaChange : function(meta, rtype, o){
10773         this.recordType = rtype;
10774         this.fields = rtype.prototype.fields;
10775         delete this.snapshot;
10776         this.sortInfo = meta.sortInfo || this.sortInfo;
10777         this.modified = [];
10778         this.fireEvent('metachange', this, this.reader.meta);
10779     },
10780     
10781     moveIndex : function(data, type)
10782     {
10783         var index = this.indexOf(data);
10784         
10785         var newIndex = index + type;
10786         
10787         this.remove(data);
10788         
10789         this.insert(newIndex, data);
10790         
10791     }
10792 });/*
10793  * Based on:
10794  * Ext JS Library 1.1.1
10795  * Copyright(c) 2006-2007, Ext JS, LLC.
10796  *
10797  * Originally Released Under LGPL - original licence link has changed is not relivant.
10798  *
10799  * Fork - LGPL
10800  * <script type="text/javascript">
10801  */
10802
10803 /**
10804  * @class Roo.data.SimpleStore
10805  * @extends Roo.data.Store
10806  * Small helper class to make creating Stores from Array data easier.
10807  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10808  * @cfg {Array} fields An array of field definition objects, or field name strings.
10809  * @cfg {Array} data The multi-dimensional array of data
10810  * @constructor
10811  * @param {Object} config
10812  */
10813 Roo.data.SimpleStore = function(config){
10814     Roo.data.SimpleStore.superclass.constructor.call(this, {
10815         isLocal : true,
10816         reader: new Roo.data.ArrayReader({
10817                 id: config.id
10818             },
10819             Roo.data.Record.create(config.fields)
10820         ),
10821         proxy : new Roo.data.MemoryProxy(config.data)
10822     });
10823     this.load();
10824 };
10825 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10826  * Based on:
10827  * Ext JS Library 1.1.1
10828  * Copyright(c) 2006-2007, Ext JS, LLC.
10829  *
10830  * Originally Released Under LGPL - original licence link has changed is not relivant.
10831  *
10832  * Fork - LGPL
10833  * <script type="text/javascript">
10834  */
10835
10836 /**
10837 /**
10838  * @extends Roo.data.Store
10839  * @class Roo.data.JsonStore
10840  * Small helper class to make creating Stores for JSON data easier. <br/>
10841 <pre><code>
10842 var store = new Roo.data.JsonStore({
10843     url: 'get-images.php',
10844     root: 'images',
10845     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10846 });
10847 </code></pre>
10848  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10849  * JsonReader and HttpProxy (unless inline data is provided).</b>
10850  * @cfg {Array} fields An array of field definition objects, or field name strings.
10851  * @constructor
10852  * @param {Object} config
10853  */
10854 Roo.data.JsonStore = function(c){
10855     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10856         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10857         reader: new Roo.data.JsonReader(c, c.fields)
10858     }));
10859 };
10860 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10861  * Based on:
10862  * Ext JS Library 1.1.1
10863  * Copyright(c) 2006-2007, Ext JS, LLC.
10864  *
10865  * Originally Released Under LGPL - original licence link has changed is not relivant.
10866  *
10867  * Fork - LGPL
10868  * <script type="text/javascript">
10869  */
10870
10871  
10872 Roo.data.Field = function(config){
10873     if(typeof config == "string"){
10874         config = {name: config};
10875     }
10876     Roo.apply(this, config);
10877     
10878     if(!this.type){
10879         this.type = "auto";
10880     }
10881     
10882     var st = Roo.data.SortTypes;
10883     // named sortTypes are supported, here we look them up
10884     if(typeof this.sortType == "string"){
10885         this.sortType = st[this.sortType];
10886     }
10887     
10888     // set default sortType for strings and dates
10889     if(!this.sortType){
10890         switch(this.type){
10891             case "string":
10892                 this.sortType = st.asUCString;
10893                 break;
10894             case "date":
10895                 this.sortType = st.asDate;
10896                 break;
10897             default:
10898                 this.sortType = st.none;
10899         }
10900     }
10901
10902     // define once
10903     var stripRe = /[\$,%]/g;
10904
10905     // prebuilt conversion function for this field, instead of
10906     // switching every time we're reading a value
10907     if(!this.convert){
10908         var cv, dateFormat = this.dateFormat;
10909         switch(this.type){
10910             case "":
10911             case "auto":
10912             case undefined:
10913                 cv = function(v){ return v; };
10914                 break;
10915             case "string":
10916                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10917                 break;
10918             case "int":
10919                 cv = function(v){
10920                     return v !== undefined && v !== null && v !== '' ?
10921                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10922                     };
10923                 break;
10924             case "float":
10925                 cv = function(v){
10926                     return v !== undefined && v !== null && v !== '' ?
10927                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10928                     };
10929                 break;
10930             case "bool":
10931             case "boolean":
10932                 cv = function(v){ return v === true || v === "true" || v == 1; };
10933                 break;
10934             case "date":
10935                 cv = function(v){
10936                     if(!v){
10937                         return '';
10938                     }
10939                     if(v instanceof Date){
10940                         return v;
10941                     }
10942                     if(dateFormat){
10943                         if(dateFormat == "timestamp"){
10944                             return new Date(v*1000);
10945                         }
10946                         return Date.parseDate(v, dateFormat);
10947                     }
10948                     var parsed = Date.parse(v);
10949                     return parsed ? new Date(parsed) : null;
10950                 };
10951              break;
10952             
10953         }
10954         this.convert = cv;
10955     }
10956 };
10957
10958 Roo.data.Field.prototype = {
10959     dateFormat: null,
10960     defaultValue: "",
10961     mapping: null,
10962     sortType : null,
10963     sortDir : "ASC"
10964 };/*
10965  * Based on:
10966  * Ext JS Library 1.1.1
10967  * Copyright(c) 2006-2007, Ext JS, LLC.
10968  *
10969  * Originally Released Under LGPL - original licence link has changed is not relivant.
10970  *
10971  * Fork - LGPL
10972  * <script type="text/javascript">
10973  */
10974  
10975 // Base class for reading structured data from a data source.  This class is intended to be
10976 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10977
10978 /**
10979  * @class Roo.data.DataReader
10980  * Base class for reading structured data from a data source.  This class is intended to be
10981  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10982  */
10983
10984 Roo.data.DataReader = function(meta, recordType){
10985     
10986     this.meta = meta;
10987     
10988     this.recordType = recordType instanceof Array ? 
10989         Roo.data.Record.create(recordType) : recordType;
10990 };
10991
10992 Roo.data.DataReader.prototype = {
10993      /**
10994      * Create an empty record
10995      * @param {Object} data (optional) - overlay some values
10996      * @return {Roo.data.Record} record created.
10997      */
10998     newRow :  function(d) {
10999         var da =  {};
11000         this.recordType.prototype.fields.each(function(c) {
11001             switch( c.type) {
11002                 case 'int' : da[c.name] = 0; break;
11003                 case 'date' : da[c.name] = new Date(); break;
11004                 case 'float' : da[c.name] = 0.0; break;
11005                 case 'boolean' : da[c.name] = false; break;
11006                 default : da[c.name] = ""; break;
11007             }
11008             
11009         });
11010         return new this.recordType(Roo.apply(da, d));
11011     }
11012     
11013 };/*
11014  * Based on:
11015  * Ext JS Library 1.1.1
11016  * Copyright(c) 2006-2007, Ext JS, LLC.
11017  *
11018  * Originally Released Under LGPL - original licence link has changed is not relivant.
11019  *
11020  * Fork - LGPL
11021  * <script type="text/javascript">
11022  */
11023
11024 /**
11025  * @class Roo.data.DataProxy
11026  * @extends Roo.data.Observable
11027  * This class is an abstract base class for implementations which provide retrieval of
11028  * unformatted data objects.<br>
11029  * <p>
11030  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11031  * (of the appropriate type which knows how to parse the data object) to provide a block of
11032  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11033  * <p>
11034  * Custom implementations must implement the load method as described in
11035  * {@link Roo.data.HttpProxy#load}.
11036  */
11037 Roo.data.DataProxy = function(){
11038     this.addEvents({
11039         /**
11040          * @event beforeload
11041          * Fires before a network request is made to retrieve a data object.
11042          * @param {Object} This DataProxy object.
11043          * @param {Object} params The params parameter to the load function.
11044          */
11045         beforeload : true,
11046         /**
11047          * @event load
11048          * Fires before the load method's callback is called.
11049          * @param {Object} This DataProxy object.
11050          * @param {Object} o The data object.
11051          * @param {Object} arg The callback argument object passed to the load function.
11052          */
11053         load : true,
11054         /**
11055          * @event loadexception
11056          * Fires if an Exception occurs during data retrieval.
11057          * @param {Object} This DataProxy object.
11058          * @param {Object} o The data object.
11059          * @param {Object} arg The callback argument object passed to the load function.
11060          * @param {Object} e The Exception.
11061          */
11062         loadexception : true
11063     });
11064     Roo.data.DataProxy.superclass.constructor.call(this);
11065 };
11066
11067 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11068
11069     /**
11070      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11071      */
11072 /*
11073  * Based on:
11074  * Ext JS Library 1.1.1
11075  * Copyright(c) 2006-2007, Ext JS, LLC.
11076  *
11077  * Originally Released Under LGPL - original licence link has changed is not relivant.
11078  *
11079  * Fork - LGPL
11080  * <script type="text/javascript">
11081  */
11082 /**
11083  * @class Roo.data.MemoryProxy
11084  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11085  * to the Reader when its load method is called.
11086  * @constructor
11087  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11088  */
11089 Roo.data.MemoryProxy = function(data){
11090     if (data.data) {
11091         data = data.data;
11092     }
11093     Roo.data.MemoryProxy.superclass.constructor.call(this);
11094     this.data = data;
11095 };
11096
11097 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11098     
11099     /**
11100      * Load data from the requested source (in this case an in-memory
11101      * data object passed to the constructor), read the data object into
11102      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11103      * process that block using the passed callback.
11104      * @param {Object} params This parameter is not used by the MemoryProxy class.
11105      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11106      * object into a block of Roo.data.Records.
11107      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11108      * The function must be passed <ul>
11109      * <li>The Record block object</li>
11110      * <li>The "arg" argument from the load function</li>
11111      * <li>A boolean success indicator</li>
11112      * </ul>
11113      * @param {Object} scope The scope in which to call the callback
11114      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11115      */
11116     load : function(params, reader, callback, scope, arg){
11117         params = params || {};
11118         var result;
11119         try {
11120             result = reader.readRecords(this.data);
11121         }catch(e){
11122             this.fireEvent("loadexception", this, arg, null, e);
11123             callback.call(scope, null, arg, false);
11124             return;
11125         }
11126         callback.call(scope, result, arg, true);
11127     },
11128     
11129     // private
11130     update : function(params, records){
11131         
11132     }
11133 });/*
11134  * Based on:
11135  * Ext JS Library 1.1.1
11136  * Copyright(c) 2006-2007, Ext JS, LLC.
11137  *
11138  * Originally Released Under LGPL - original licence link has changed is not relivant.
11139  *
11140  * Fork - LGPL
11141  * <script type="text/javascript">
11142  */
11143 /**
11144  * @class Roo.data.HttpProxy
11145  * @extends Roo.data.DataProxy
11146  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11147  * configured to reference a certain URL.<br><br>
11148  * <p>
11149  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11150  * from which the running page was served.<br><br>
11151  * <p>
11152  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11153  * <p>
11154  * Be aware that to enable the browser to parse an XML document, the server must set
11155  * the Content-Type header in the HTTP response to "text/xml".
11156  * @constructor
11157  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11158  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11159  * will be used to make the request.
11160  */
11161 Roo.data.HttpProxy = function(conn){
11162     Roo.data.HttpProxy.superclass.constructor.call(this);
11163     // is conn a conn config or a real conn?
11164     this.conn = conn;
11165     this.useAjax = !conn || !conn.events;
11166   
11167 };
11168
11169 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11170     // thse are take from connection...
11171     
11172     /**
11173      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11174      */
11175     /**
11176      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11177      * extra parameters to each request made by this object. (defaults to undefined)
11178      */
11179     /**
11180      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11181      *  to each request made by this object. (defaults to undefined)
11182      */
11183     /**
11184      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11185      */
11186     /**
11187      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11188      */
11189      /**
11190      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11191      * @type Boolean
11192      */
11193   
11194
11195     /**
11196      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11197      * @type Boolean
11198      */
11199     /**
11200      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11201      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11202      * a finer-grained basis than the DataProxy events.
11203      */
11204     getConnection : function(){
11205         return this.useAjax ? Roo.Ajax : this.conn;
11206     },
11207
11208     /**
11209      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11210      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11211      * process that block using the passed callback.
11212      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11213      * for the request to the remote server.
11214      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11215      * object into a block of Roo.data.Records.
11216      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11217      * The function must be passed <ul>
11218      * <li>The Record block object</li>
11219      * <li>The "arg" argument from the load function</li>
11220      * <li>A boolean success indicator</li>
11221      * </ul>
11222      * @param {Object} scope The scope in which to call the callback
11223      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11224      */
11225     load : function(params, reader, callback, scope, arg){
11226         if(this.fireEvent("beforeload", this, params) !== false){
11227             var  o = {
11228                 params : params || {},
11229                 request: {
11230                     callback : callback,
11231                     scope : scope,
11232                     arg : arg
11233                 },
11234                 reader: reader,
11235                 callback : this.loadResponse,
11236                 scope: this
11237             };
11238             if(this.useAjax){
11239                 Roo.applyIf(o, this.conn);
11240                 if(this.activeRequest){
11241                     Roo.Ajax.abort(this.activeRequest);
11242                 }
11243                 this.activeRequest = Roo.Ajax.request(o);
11244             }else{
11245                 this.conn.request(o);
11246             }
11247         }else{
11248             callback.call(scope||this, null, arg, false);
11249         }
11250     },
11251
11252     // private
11253     loadResponse : function(o, success, response){
11254         delete this.activeRequest;
11255         if(!success){
11256             this.fireEvent("loadexception", this, o, response);
11257             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11258             return;
11259         }
11260         var result;
11261         try {
11262             result = o.reader.read(response);
11263         }catch(e){
11264             this.fireEvent("loadexception", this, o, response, e);
11265             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11266             return;
11267         }
11268         
11269         this.fireEvent("load", this, o, o.request.arg);
11270         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11271     },
11272
11273     // private
11274     update : function(dataSet){
11275
11276     },
11277
11278     // private
11279     updateResponse : function(dataSet){
11280
11281     }
11282 });/*
11283  * Based on:
11284  * Ext JS Library 1.1.1
11285  * Copyright(c) 2006-2007, Ext JS, LLC.
11286  *
11287  * Originally Released Under LGPL - original licence link has changed is not relivant.
11288  *
11289  * Fork - LGPL
11290  * <script type="text/javascript">
11291  */
11292
11293 /**
11294  * @class Roo.data.ScriptTagProxy
11295  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11296  * other than the originating domain of the running page.<br><br>
11297  * <p>
11298  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
11299  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11300  * <p>
11301  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11302  * source code that is used as the source inside a &lt;script> tag.<br><br>
11303  * <p>
11304  * In order for the browser to process the returned data, the server must wrap the data object
11305  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11306  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11307  * depending on whether the callback name was passed:
11308  * <p>
11309  * <pre><code>
11310 boolean scriptTag = false;
11311 String cb = request.getParameter("callback");
11312 if (cb != null) {
11313     scriptTag = true;
11314     response.setContentType("text/javascript");
11315 } else {
11316     response.setContentType("application/x-json");
11317 }
11318 Writer out = response.getWriter();
11319 if (scriptTag) {
11320     out.write(cb + "(");
11321 }
11322 out.print(dataBlock.toJsonString());
11323 if (scriptTag) {
11324     out.write(");");
11325 }
11326 </pre></code>
11327  *
11328  * @constructor
11329  * @param {Object} config A configuration object.
11330  */
11331 Roo.data.ScriptTagProxy = function(config){
11332     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11333     Roo.apply(this, config);
11334     this.head = document.getElementsByTagName("head")[0];
11335 };
11336
11337 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11338
11339 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11340     /**
11341      * @cfg {String} url The URL from which to request the data object.
11342      */
11343     /**
11344      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11345      */
11346     timeout : 30000,
11347     /**
11348      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11349      * the server the name of the callback function set up by the load call to process the returned data object.
11350      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11351      * javascript output which calls this named function passing the data object as its only parameter.
11352      */
11353     callbackParam : "callback",
11354     /**
11355      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11356      * name to the request.
11357      */
11358     nocache : true,
11359
11360     /**
11361      * Load data from the configured URL, read the data object into
11362      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11363      * process that block using the passed callback.
11364      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11365      * for the request to the remote server.
11366      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11367      * object into a block of Roo.data.Records.
11368      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11369      * The function must be passed <ul>
11370      * <li>The Record block object</li>
11371      * <li>The "arg" argument from the load function</li>
11372      * <li>A boolean success indicator</li>
11373      * </ul>
11374      * @param {Object} scope The scope in which to call the callback
11375      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11376      */
11377     load : function(params, reader, callback, scope, arg){
11378         if(this.fireEvent("beforeload", this, params) !== false){
11379
11380             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11381
11382             var url = this.url;
11383             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11384             if(this.nocache){
11385                 url += "&_dc=" + (new Date().getTime());
11386             }
11387             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11388             var trans = {
11389                 id : transId,
11390                 cb : "stcCallback"+transId,
11391                 scriptId : "stcScript"+transId,
11392                 params : params,
11393                 arg : arg,
11394                 url : url,
11395                 callback : callback,
11396                 scope : scope,
11397                 reader : reader
11398             };
11399             var conn = this;
11400
11401             window[trans.cb] = function(o){
11402                 conn.handleResponse(o, trans);
11403             };
11404
11405             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11406
11407             if(this.autoAbort !== false){
11408                 this.abort();
11409             }
11410
11411             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11412
11413             var script = document.createElement("script");
11414             script.setAttribute("src", url);
11415             script.setAttribute("type", "text/javascript");
11416             script.setAttribute("id", trans.scriptId);
11417             this.head.appendChild(script);
11418
11419             this.trans = trans;
11420         }else{
11421             callback.call(scope||this, null, arg, false);
11422         }
11423     },
11424
11425     // private
11426     isLoading : function(){
11427         return this.trans ? true : false;
11428     },
11429
11430     /**
11431      * Abort the current server request.
11432      */
11433     abort : function(){
11434         if(this.isLoading()){
11435             this.destroyTrans(this.trans);
11436         }
11437     },
11438
11439     // private
11440     destroyTrans : function(trans, isLoaded){
11441         this.head.removeChild(document.getElementById(trans.scriptId));
11442         clearTimeout(trans.timeoutId);
11443         if(isLoaded){
11444             window[trans.cb] = undefined;
11445             try{
11446                 delete window[trans.cb];
11447             }catch(e){}
11448         }else{
11449             // if hasn't been loaded, wait for load to remove it to prevent script error
11450             window[trans.cb] = function(){
11451                 window[trans.cb] = undefined;
11452                 try{
11453                     delete window[trans.cb];
11454                 }catch(e){}
11455             };
11456         }
11457     },
11458
11459     // private
11460     handleResponse : function(o, trans){
11461         this.trans = false;
11462         this.destroyTrans(trans, true);
11463         var result;
11464         try {
11465             result = trans.reader.readRecords(o);
11466         }catch(e){
11467             this.fireEvent("loadexception", this, o, trans.arg, e);
11468             trans.callback.call(trans.scope||window, null, trans.arg, false);
11469             return;
11470         }
11471         this.fireEvent("load", this, o, trans.arg);
11472         trans.callback.call(trans.scope||window, result, trans.arg, true);
11473     },
11474
11475     // private
11476     handleFailure : function(trans){
11477         this.trans = false;
11478         this.destroyTrans(trans, false);
11479         this.fireEvent("loadexception", this, null, trans.arg);
11480         trans.callback.call(trans.scope||window, null, trans.arg, false);
11481     }
11482 });/*
11483  * Based on:
11484  * Ext JS Library 1.1.1
11485  * Copyright(c) 2006-2007, Ext JS, LLC.
11486  *
11487  * Originally Released Under LGPL - original licence link has changed is not relivant.
11488  *
11489  * Fork - LGPL
11490  * <script type="text/javascript">
11491  */
11492
11493 /**
11494  * @class Roo.data.JsonReader
11495  * @extends Roo.data.DataReader
11496  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11497  * based on mappings in a provided Roo.data.Record constructor.
11498  * 
11499  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11500  * in the reply previously. 
11501  * 
11502  * <p>
11503  * Example code:
11504  * <pre><code>
11505 var RecordDef = Roo.data.Record.create([
11506     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11507     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11508 ]);
11509 var myReader = new Roo.data.JsonReader({
11510     totalProperty: "results",    // The property which contains the total dataset size (optional)
11511     root: "rows",                // The property which contains an Array of row objects
11512     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11513 }, RecordDef);
11514 </code></pre>
11515  * <p>
11516  * This would consume a JSON file like this:
11517  * <pre><code>
11518 { 'results': 2, 'rows': [
11519     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11520     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11521 }
11522 </code></pre>
11523  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11524  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11525  * paged from the remote server.
11526  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11527  * @cfg {String} root name of the property which contains the Array of row objects.
11528  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11529  * @cfg {Array} fields Array of field definition objects
11530  * @constructor
11531  * Create a new JsonReader
11532  * @param {Object} meta Metadata configuration options
11533  * @param {Object} recordType Either an Array of field definition objects,
11534  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11535  */
11536 Roo.data.JsonReader = function(meta, recordType){
11537     
11538     meta = meta || {};
11539     // set some defaults:
11540     Roo.applyIf(meta, {
11541         totalProperty: 'total',
11542         successProperty : 'success',
11543         root : 'data',
11544         id : 'id'
11545     });
11546     
11547     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11548 };
11549 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11550     
11551     /**
11552      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11553      * Used by Store query builder to append _requestMeta to params.
11554      * 
11555      */
11556     metaFromRemote : false,
11557     /**
11558      * This method is only used by a DataProxy which has retrieved data from a remote server.
11559      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11560      * @return {Object} data A data block which is used by an Roo.data.Store object as
11561      * a cache of Roo.data.Records.
11562      */
11563     read : function(response){
11564         var json = response.responseText;
11565        
11566         var o = /* eval:var:o */ eval("("+json+")");
11567         if(!o) {
11568             throw {message: "JsonReader.read: Json object not found"};
11569         }
11570         
11571         if(o.metaData){
11572             
11573             delete this.ef;
11574             this.metaFromRemote = true;
11575             this.meta = o.metaData;
11576             this.recordType = Roo.data.Record.create(o.metaData.fields);
11577             this.onMetaChange(this.meta, this.recordType, o);
11578         }
11579         return this.readRecords(o);
11580     },
11581
11582     // private function a store will implement
11583     onMetaChange : function(meta, recordType, o){
11584
11585     },
11586
11587     /**
11588          * @ignore
11589          */
11590     simpleAccess: function(obj, subsc) {
11591         return obj[subsc];
11592     },
11593
11594         /**
11595          * @ignore
11596          */
11597     getJsonAccessor: function(){
11598         var re = /[\[\.]/;
11599         return function(expr) {
11600             try {
11601                 return(re.test(expr))
11602                     ? new Function("obj", "return obj." + expr)
11603                     : function(obj){
11604                         return obj[expr];
11605                     };
11606             } catch(e){}
11607             return Roo.emptyFn;
11608         };
11609     }(),
11610
11611     /**
11612      * Create a data block containing Roo.data.Records from an XML document.
11613      * @param {Object} o An object which contains an Array of row objects in the property specified
11614      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11615      * which contains the total size of the dataset.
11616      * @return {Object} data A data block which is used by an Roo.data.Store object as
11617      * a cache of Roo.data.Records.
11618      */
11619     readRecords : function(o){
11620         /**
11621          * After any data loads, the raw JSON data is available for further custom processing.
11622          * @type Object
11623          */
11624         this.o = o;
11625         var s = this.meta, Record = this.recordType,
11626             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11627
11628 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11629         if (!this.ef) {
11630             if(s.totalProperty) {
11631                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11632                 }
11633                 if(s.successProperty) {
11634                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11635                 }
11636                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11637                 if (s.id) {
11638                         var g = this.getJsonAccessor(s.id);
11639                         this.getId = function(rec) {
11640                                 var r = g(rec);  
11641                                 return (r === undefined || r === "") ? null : r;
11642                         };
11643                 } else {
11644                         this.getId = function(){return null;};
11645                 }
11646             this.ef = [];
11647             for(var jj = 0; jj < fl; jj++){
11648                 f = fi[jj];
11649                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11650                 this.ef[jj] = this.getJsonAccessor(map);
11651             }
11652         }
11653
11654         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11655         if(s.totalProperty){
11656             var vt = parseInt(this.getTotal(o), 10);
11657             if(!isNaN(vt)){
11658                 totalRecords = vt;
11659             }
11660         }
11661         if(s.successProperty){
11662             var vs = this.getSuccess(o);
11663             if(vs === false || vs === 'false'){
11664                 success = false;
11665             }
11666         }
11667         var records = [];
11668         for(var i = 0; i < c; i++){
11669                 var n = root[i];
11670             var values = {};
11671             var id = this.getId(n);
11672             for(var j = 0; j < fl; j++){
11673                 f = fi[j];
11674             var v = this.ef[j](n);
11675             if (!f.convert) {
11676                 Roo.log('missing convert for ' + f.name);
11677                 Roo.log(f);
11678                 continue;
11679             }
11680             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11681             }
11682             var record = new Record(values, id);
11683             record.json = n;
11684             records[i] = record;
11685         }
11686         return {
11687             raw : o,
11688             success : success,
11689             records : records,
11690             totalRecords : totalRecords
11691         };
11692     }
11693 });/*
11694  * Based on:
11695  * Ext JS Library 1.1.1
11696  * Copyright(c) 2006-2007, Ext JS, LLC.
11697  *
11698  * Originally Released Under LGPL - original licence link has changed is not relivant.
11699  *
11700  * Fork - LGPL
11701  * <script type="text/javascript">
11702  */
11703
11704 /**
11705  * @class Roo.data.ArrayReader
11706  * @extends Roo.data.DataReader
11707  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11708  * Each element of that Array represents a row of data fields. The
11709  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11710  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11711  * <p>
11712  * Example code:.
11713  * <pre><code>
11714 var RecordDef = Roo.data.Record.create([
11715     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11716     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11717 ]);
11718 var myReader = new Roo.data.ArrayReader({
11719     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11720 }, RecordDef);
11721 </code></pre>
11722  * <p>
11723  * This would consume an Array like this:
11724  * <pre><code>
11725 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11726   </code></pre>
11727  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11728  * @constructor
11729  * Create a new JsonReader
11730  * @param {Object} meta Metadata configuration options.
11731  * @param {Object} recordType Either an Array of field definition objects
11732  * as specified to {@link Roo.data.Record#create},
11733  * or an {@link Roo.data.Record} object
11734  * created using {@link Roo.data.Record#create}.
11735  */
11736 Roo.data.ArrayReader = function(meta, recordType){
11737     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11738 };
11739
11740 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11741     /**
11742      * Create a data block containing Roo.data.Records from an XML document.
11743      * @param {Object} o An Array of row objects which represents the dataset.
11744      * @return {Object} data A data block which is used by an Roo.data.Store object as
11745      * a cache of Roo.data.Records.
11746      */
11747     readRecords : function(o){
11748         var sid = this.meta ? this.meta.id : null;
11749         var recordType = this.recordType, fields = recordType.prototype.fields;
11750         var records = [];
11751         var root = o;
11752             for(var i = 0; i < root.length; i++){
11753                     var n = root[i];
11754                 var values = {};
11755                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11756                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11757                 var f = fields.items[j];
11758                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11759                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11760                 v = f.convert(v);
11761                 values[f.name] = v;
11762             }
11763                 var record = new recordType(values, id);
11764                 record.json = n;
11765                 records[records.length] = record;
11766             }
11767             return {
11768                 records : records,
11769                 totalRecords : records.length
11770             };
11771     }
11772 });/*
11773  * - LGPL
11774  * * 
11775  */
11776
11777 /**
11778  * @class Roo.bootstrap.ComboBox
11779  * @extends Roo.bootstrap.TriggerField
11780  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11781  * @cfg {Boolean} append (true|false) default false
11782  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11783  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11784  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11785  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11786  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11787  * @cfg {Boolean} animate default true
11788  * @cfg {Boolean} emptyResultText only for touch device
11789  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11790  * @constructor
11791  * Create a new ComboBox.
11792  * @param {Object} config Configuration options
11793  */
11794 Roo.bootstrap.ComboBox = function(config){
11795     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11796     this.addEvents({
11797         /**
11798          * @event expand
11799          * Fires when the dropdown list is expanded
11800              * @param {Roo.bootstrap.ComboBox} combo This combo box
11801              */
11802         'expand' : true,
11803         /**
11804          * @event collapse
11805          * Fires when the dropdown list is collapsed
11806              * @param {Roo.bootstrap.ComboBox} combo This combo box
11807              */
11808         'collapse' : true,
11809         /**
11810          * @event beforeselect
11811          * Fires before a list item is selected. Return false to cancel the selection.
11812              * @param {Roo.bootstrap.ComboBox} combo This combo box
11813              * @param {Roo.data.Record} record The data record returned from the underlying store
11814              * @param {Number} index The index of the selected item in the dropdown list
11815              */
11816         'beforeselect' : true,
11817         /**
11818          * @event select
11819          * Fires when a list item is selected
11820              * @param {Roo.bootstrap.ComboBox} combo This combo box
11821              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11822              * @param {Number} index The index of the selected item in the dropdown list
11823              */
11824         'select' : true,
11825         /**
11826          * @event beforequery
11827          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11828          * The event object passed has these properties:
11829              * @param {Roo.bootstrap.ComboBox} combo This combo box
11830              * @param {String} query The query
11831              * @param {Boolean} forceAll true to force "all" query
11832              * @param {Boolean} cancel true to cancel the query
11833              * @param {Object} e The query event object
11834              */
11835         'beforequery': true,
11836          /**
11837          * @event add
11838          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11839              * @param {Roo.bootstrap.ComboBox} combo This combo box
11840              */
11841         'add' : true,
11842         /**
11843          * @event edit
11844          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11845              * @param {Roo.bootstrap.ComboBox} combo This combo box
11846              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11847              */
11848         'edit' : true,
11849         /**
11850          * @event remove
11851          * Fires when the remove value from the combobox array
11852              * @param {Roo.bootstrap.ComboBox} combo This combo box
11853              */
11854         'remove' : true,
11855         /**
11856          * @event afterremove
11857          * Fires when the remove value from the combobox array
11858              * @param {Roo.bootstrap.ComboBox} combo This combo box
11859              */
11860         'afterremove' : true,
11861         /**
11862          * @event specialfilter
11863          * Fires when specialfilter
11864             * @param {Roo.bootstrap.ComboBox} combo This combo box
11865             */
11866         'specialfilter' : true,
11867         /**
11868          * @event tick
11869          * Fires when tick the element
11870             * @param {Roo.bootstrap.ComboBox} combo This combo box
11871             */
11872         'tick' : true,
11873         /**
11874          * @event touchviewdisplay
11875          * Fires when touch view require special display (default is using displayField)
11876             * @param {Roo.bootstrap.ComboBox} combo This combo box
11877             * @param {Object} cfg set html .
11878             */
11879         'touchviewdisplay' : true
11880         
11881     });
11882     
11883     this.item = [];
11884     this.tickItems = [];
11885     
11886     this.selectedIndex = -1;
11887     if(this.mode == 'local'){
11888         if(config.queryDelay === undefined){
11889             this.queryDelay = 10;
11890         }
11891         if(config.minChars === undefined){
11892             this.minChars = 0;
11893         }
11894     }
11895 };
11896
11897 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11898      
11899     /**
11900      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11901      * rendering into an Roo.Editor, defaults to false)
11902      */
11903     /**
11904      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11905      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11906      */
11907     /**
11908      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11909      */
11910     /**
11911      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11912      * the dropdown list (defaults to undefined, with no header element)
11913      */
11914
11915      /**
11916      * @cfg {String/Roo.Template} tpl The template to use to render the output
11917      */
11918      
11919      /**
11920      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11921      */
11922     listWidth: undefined,
11923     /**
11924      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11925      * mode = 'remote' or 'text' if mode = 'local')
11926      */
11927     displayField: undefined,
11928     
11929     /**
11930      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11931      * mode = 'remote' or 'value' if mode = 'local'). 
11932      * Note: use of a valueField requires the user make a selection
11933      * in order for a value to be mapped.
11934      */
11935     valueField: undefined,
11936     
11937     
11938     /**
11939      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11940      * field's data value (defaults to the underlying DOM element's name)
11941      */
11942     hiddenName: undefined,
11943     /**
11944      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11945      */
11946     listClass: '',
11947     /**
11948      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11949      */
11950     selectedClass: 'active',
11951     
11952     /**
11953      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11954      */
11955     shadow:'sides',
11956     /**
11957      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11958      * anchor positions (defaults to 'tl-bl')
11959      */
11960     listAlign: 'tl-bl?',
11961     /**
11962      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11963      */
11964     maxHeight: 300,
11965     /**
11966      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11967      * query specified by the allQuery config option (defaults to 'query')
11968      */
11969     triggerAction: 'query',
11970     /**
11971      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11972      * (defaults to 4, does not apply if editable = false)
11973      */
11974     minChars : 4,
11975     /**
11976      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11977      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11978      */
11979     typeAhead: false,
11980     /**
11981      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11982      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11983      */
11984     queryDelay: 500,
11985     /**
11986      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11987      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11988      */
11989     pageSize: 0,
11990     /**
11991      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11992      * when editable = true (defaults to false)
11993      */
11994     selectOnFocus:false,
11995     /**
11996      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11997      */
11998     queryParam: 'query',
11999     /**
12000      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12001      * when mode = 'remote' (defaults to 'Loading...')
12002      */
12003     loadingText: 'Loading...',
12004     /**
12005      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12006      */
12007     resizable: false,
12008     /**
12009      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12010      */
12011     handleHeight : 8,
12012     /**
12013      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12014      * traditional select (defaults to true)
12015      */
12016     editable: true,
12017     /**
12018      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12019      */
12020     allQuery: '',
12021     /**
12022      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12023      */
12024     mode: 'remote',
12025     /**
12026      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12027      * listWidth has a higher value)
12028      */
12029     minListWidth : 70,
12030     /**
12031      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12032      * allow the user to set arbitrary text into the field (defaults to false)
12033      */
12034     forceSelection:false,
12035     /**
12036      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12037      * if typeAhead = true (defaults to 250)
12038      */
12039     typeAheadDelay : 250,
12040     /**
12041      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12042      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12043      */
12044     valueNotFoundText : undefined,
12045     /**
12046      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12047      */
12048     blockFocus : false,
12049     
12050     /**
12051      * @cfg {Boolean} disableClear Disable showing of clear button.
12052      */
12053     disableClear : false,
12054     /**
12055      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12056      */
12057     alwaysQuery : false,
12058     
12059     /**
12060      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12061      */
12062     multiple : false,
12063     
12064     /**
12065      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12066      */
12067     invalidClass : "has-warning",
12068     
12069     /**
12070      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12071      */
12072     validClass : "has-success",
12073     
12074     /**
12075      * @cfg {Boolean} specialFilter (true|false) special filter default false
12076      */
12077     specialFilter : false,
12078     
12079     /**
12080      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12081      */
12082     mobileTouchView : true,
12083     
12084     //private
12085     addicon : false,
12086     editicon: false,
12087     
12088     page: 0,
12089     hasQuery: false,
12090     append: false,
12091     loadNext: false,
12092     autoFocus : true,
12093     tickable : false,
12094     btnPosition : 'right',
12095     triggerList : true,
12096     showToggleBtn : true,
12097     animate : true,
12098     emptyResultText: 'Empty',
12099     triggerText : 'Select',
12100     
12101     // element that contains real text value.. (when hidden is used..)
12102     
12103     getAutoCreate : function()
12104     {
12105         var cfg = false;
12106         
12107         /*
12108          * Touch Devices
12109          */
12110         
12111         if(Roo.isTouch && this.mobileTouchView){
12112             cfg = this.getAutoCreateTouchView();
12113             return cfg;;
12114         }
12115         
12116         /*
12117          *  Normal ComboBox
12118          */
12119         if(!this.tickable){
12120             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12121             return cfg;
12122         }
12123         
12124         /*
12125          *  ComboBox with tickable selections
12126          */
12127              
12128         var align = this.labelAlign || this.parentLabelAlign();
12129         
12130         cfg = {
12131             cls : 'form-group roo-combobox-tickable' //input-group
12132         };
12133         
12134         var buttons = {
12135             tag : 'div',
12136             cls : 'tickable-buttons',
12137             cn : [
12138                 {
12139                     tag : 'button',
12140                     type : 'button',
12141                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12142                     html : this.triggerText
12143                 },
12144                 {
12145                     tag : 'button',
12146                     type : 'button',
12147                     name : 'ok',
12148                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12149                     html : 'Done'
12150                 },
12151                 {
12152                     tag : 'button',
12153                     type : 'button',
12154                     name : 'cancel',
12155                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12156                     html : 'Cancel'
12157                 }
12158             ]
12159         };
12160         
12161         if(this.editable){
12162             buttons.cn.unshift({
12163                 tag: 'input',
12164                 cls: 'roo-select2-search-field-input'
12165             });
12166         }
12167         
12168         var _this = this;
12169         
12170         Roo.each(buttons.cn, function(c){
12171             if (_this.size) {
12172                 c.cls += ' btn-' + _this.size;
12173             }
12174
12175             if (_this.disabled) {
12176                 c.disabled = true;
12177             }
12178         });
12179         
12180         var box = {
12181             tag: 'div',
12182             cn: [
12183                 {
12184                     tag: 'input',
12185                     type : 'hidden',
12186                     cls: 'form-hidden-field'
12187                 },
12188                 {
12189                     tag: 'ul',
12190                     cls: 'roo-select2-choices',
12191                     cn:[
12192                         {
12193                             tag: 'li',
12194                             cls: 'roo-select2-search-field',
12195                             cn: [
12196
12197                                 buttons
12198                             ]
12199                         }
12200                     ]
12201                 }
12202             ]
12203         };
12204         
12205         var combobox = {
12206             cls: 'roo-select2-container input-group roo-select2-container-multi',
12207             cn: [
12208                 box
12209 //                {
12210 //                    tag: 'ul',
12211 //                    cls: 'typeahead typeahead-long dropdown-menu',
12212 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12213 //                }
12214             ]
12215         };
12216         
12217         if(this.hasFeedback && !this.allowBlank){
12218             
12219             var feedback = {
12220                 tag: 'span',
12221                 cls: 'glyphicon form-control-feedback'
12222             };
12223
12224             combobox.cn.push(feedback);
12225         }
12226         
12227         if (align ==='left' && this.fieldLabel.length) {
12228             
12229 //                Roo.log("left and has label");
12230                 cfg.cn = [
12231                     
12232                     {
12233                         tag: 'label',
12234                         'for' :  id,
12235                         cls : 'control-label col-sm-' + this.labelWidth,
12236                         html : this.fieldLabel
12237                         
12238                     },
12239                     {
12240                         cls : "col-sm-" + (12 - this.labelWidth), 
12241                         cn: [
12242                             combobox
12243                         ]
12244                     }
12245                     
12246                 ];
12247         } else if ( this.fieldLabel.length) {
12248 //                Roo.log(" label");
12249                  cfg.cn = [
12250                    
12251                     {
12252                         tag: 'label',
12253                         //cls : 'input-group-addon',
12254                         html : this.fieldLabel
12255                         
12256                     },
12257                     
12258                     combobox
12259                     
12260                 ];
12261
12262         } else {
12263             
12264 //                Roo.log(" no label && no align");
12265                 cfg = combobox
12266                      
12267                 
12268         }
12269          
12270         var settings=this;
12271         ['xs','sm','md','lg'].map(function(size){
12272             if (settings[size]) {
12273                 cfg.cls += ' col-' + size + '-' + settings[size];
12274             }
12275         });
12276         
12277         return cfg;
12278         
12279     },
12280     
12281     _initEventsCalled : false,
12282     
12283     // private
12284     initEvents: function()
12285     {
12286         
12287         if (this._initEventsCalled) { // as we call render... prevent looping...
12288             return;
12289         }
12290         this._initEventsCalled = true;
12291         
12292         if (!this.store) {
12293             throw "can not find store for combo";
12294         }
12295         
12296         this.store = Roo.factory(this.store, Roo.data);
12297         
12298         // if we are building from html. then this element is so complex, that we can not really
12299         // use the rendered HTML.
12300         // so we have to trash and replace the previous code.
12301         if (Roo.XComponent.build_from_html) {
12302             
12303             // remove this element....
12304             var e = this.el.dom, k=0;
12305             while (e ) { e = e.previousSibling;  ++k;}
12306
12307             this.el.remove();
12308             
12309             this.el=false;
12310             this.rendered = false;
12311             
12312             this.render(this.parent().getChildContainer(true), k);
12313             
12314             
12315             
12316         }
12317         
12318         
12319         /*
12320          * Touch Devices
12321          */
12322         
12323         if(Roo.isTouch && this.mobileTouchView){
12324             this.initTouchView();
12325             return;
12326         }
12327         
12328         if(this.tickable){
12329             this.initTickableEvents();
12330             return;
12331         }
12332         
12333         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12334         
12335         if(this.hiddenName){
12336             
12337             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12338             
12339             this.hiddenField.dom.value =
12340                 this.hiddenValue !== undefined ? this.hiddenValue :
12341                 this.value !== undefined ? this.value : '';
12342
12343             // prevent input submission
12344             this.el.dom.removeAttribute('name');
12345             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12346              
12347              
12348         }
12349         //if(Roo.isGecko){
12350         //    this.el.dom.setAttribute('autocomplete', 'off');
12351         //}
12352         
12353         var cls = 'x-combo-list';
12354         
12355         //this.list = new Roo.Layer({
12356         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12357         //});
12358         
12359         var _this = this;
12360         
12361         (function(){
12362             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12363             _this.list.setWidth(lw);
12364         }).defer(100);
12365         
12366         this.list.on('mouseover', this.onViewOver, this);
12367         this.list.on('mousemove', this.onViewMove, this);
12368         
12369         this.list.on('scroll', this.onViewScroll, this);
12370         
12371         /*
12372         this.list.swallowEvent('mousewheel');
12373         this.assetHeight = 0;
12374
12375         if(this.title){
12376             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12377             this.assetHeight += this.header.getHeight();
12378         }
12379
12380         this.innerList = this.list.createChild({cls:cls+'-inner'});
12381         this.innerList.on('mouseover', this.onViewOver, this);
12382         this.innerList.on('mousemove', this.onViewMove, this);
12383         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12384         
12385         if(this.allowBlank && !this.pageSize && !this.disableClear){
12386             this.footer = this.list.createChild({cls:cls+'-ft'});
12387             this.pageTb = new Roo.Toolbar(this.footer);
12388            
12389         }
12390         if(this.pageSize){
12391             this.footer = this.list.createChild({cls:cls+'-ft'});
12392             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12393                     {pageSize: this.pageSize});
12394             
12395         }
12396         
12397         if (this.pageTb && this.allowBlank && !this.disableClear) {
12398             var _this = this;
12399             this.pageTb.add(new Roo.Toolbar.Fill(), {
12400                 cls: 'x-btn-icon x-btn-clear',
12401                 text: '&#160;',
12402                 handler: function()
12403                 {
12404                     _this.collapse();
12405                     _this.clearValue();
12406                     _this.onSelect(false, -1);
12407                 }
12408             });
12409         }
12410         if (this.footer) {
12411             this.assetHeight += this.footer.getHeight();
12412         }
12413         */
12414             
12415         if(!this.tpl){
12416             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12417         }
12418
12419         this.view = new Roo.View(this.list, this.tpl, {
12420             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12421         });
12422         //this.view.wrapEl.setDisplayed(false);
12423         this.view.on('click', this.onViewClick, this);
12424         
12425         
12426         
12427         this.store.on('beforeload', this.onBeforeLoad, this);
12428         this.store.on('load', this.onLoad, this);
12429         this.store.on('loadexception', this.onLoadException, this);
12430         /*
12431         if(this.resizable){
12432             this.resizer = new Roo.Resizable(this.list,  {
12433                pinned:true, handles:'se'
12434             });
12435             this.resizer.on('resize', function(r, w, h){
12436                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12437                 this.listWidth = w;
12438                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12439                 this.restrictHeight();
12440             }, this);
12441             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12442         }
12443         */
12444         if(!this.editable){
12445             this.editable = true;
12446             this.setEditable(false);
12447         }
12448         
12449         /*
12450         
12451         if (typeof(this.events.add.listeners) != 'undefined') {
12452             
12453             this.addicon = this.wrap.createChild(
12454                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12455        
12456             this.addicon.on('click', function(e) {
12457                 this.fireEvent('add', this);
12458             }, this);
12459         }
12460         if (typeof(this.events.edit.listeners) != 'undefined') {
12461             
12462             this.editicon = this.wrap.createChild(
12463                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12464             if (this.addicon) {
12465                 this.editicon.setStyle('margin-left', '40px');
12466             }
12467             this.editicon.on('click', function(e) {
12468                 
12469                 // we fire even  if inothing is selected..
12470                 this.fireEvent('edit', this, this.lastData );
12471                 
12472             }, this);
12473         }
12474         */
12475         
12476         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12477             "up" : function(e){
12478                 this.inKeyMode = true;
12479                 this.selectPrev();
12480             },
12481
12482             "down" : function(e){
12483                 if(!this.isExpanded()){
12484                     this.onTriggerClick();
12485                 }else{
12486                     this.inKeyMode = true;
12487                     this.selectNext();
12488                 }
12489             },
12490
12491             "enter" : function(e){
12492 //                this.onViewClick();
12493                 //return true;
12494                 this.collapse();
12495                 
12496                 if(this.fireEvent("specialkey", this, e)){
12497                     this.onViewClick(false);
12498                 }
12499                 
12500                 return true;
12501             },
12502
12503             "esc" : function(e){
12504                 this.collapse();
12505             },
12506
12507             "tab" : function(e){
12508                 this.collapse();
12509                 
12510                 if(this.fireEvent("specialkey", this, e)){
12511                     this.onViewClick(false);
12512                 }
12513                 
12514                 return true;
12515             },
12516
12517             scope : this,
12518
12519             doRelay : function(foo, bar, hname){
12520                 if(hname == 'down' || this.scope.isExpanded()){
12521                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12522                 }
12523                 return true;
12524             },
12525
12526             forceKeyDown: true
12527         });
12528         
12529         
12530         this.queryDelay = Math.max(this.queryDelay || 10,
12531                 this.mode == 'local' ? 10 : 250);
12532         
12533         
12534         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12535         
12536         if(this.typeAhead){
12537             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12538         }
12539         if(this.editable !== false){
12540             this.inputEl().on("keyup", this.onKeyUp, this);
12541         }
12542         if(this.forceSelection){
12543             this.inputEl().on('blur', this.doForce, this);
12544         }
12545         
12546         if(this.multiple){
12547             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12548             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12549         }
12550     },
12551     
12552     initTickableEvents: function()
12553     {   
12554         this.createList();
12555         
12556         if(this.hiddenName){
12557             
12558             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12559             
12560             this.hiddenField.dom.value =
12561                 this.hiddenValue !== undefined ? this.hiddenValue :
12562                 this.value !== undefined ? this.value : '';
12563
12564             // prevent input submission
12565             this.el.dom.removeAttribute('name');
12566             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12567              
12568              
12569         }
12570         
12571 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12572         
12573         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12574         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12575         if(this.triggerList){
12576             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12577         }
12578          
12579         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12580         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12581         
12582         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12583         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12584         
12585         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12586         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12587         
12588         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12589         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12590         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12591         
12592         this.okBtn.hide();
12593         this.cancelBtn.hide();
12594         
12595         var _this = this;
12596         
12597         (function(){
12598             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12599             _this.list.setWidth(lw);
12600         }).defer(100);
12601         
12602         this.list.on('mouseover', this.onViewOver, this);
12603         this.list.on('mousemove', this.onViewMove, this);
12604         
12605         this.list.on('scroll', this.onViewScroll, this);
12606         
12607         if(!this.tpl){
12608             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12609         }
12610
12611         this.view = new Roo.View(this.list, this.tpl, {
12612             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12613         });
12614         
12615         //this.view.wrapEl.setDisplayed(false);
12616         this.view.on('click', this.onViewClick, this);
12617         
12618         
12619         
12620         this.store.on('beforeload', this.onBeforeLoad, this);
12621         this.store.on('load', this.onLoad, this);
12622         this.store.on('loadexception', this.onLoadException, this);
12623         
12624         if(this.editable){
12625             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12626                 "up" : function(e){
12627                     this.inKeyMode = true;
12628                     this.selectPrev();
12629                 },
12630
12631                 "down" : function(e){
12632                     this.inKeyMode = true;
12633                     this.selectNext();
12634                 },
12635
12636                 "enter" : function(e){
12637                     if(this.fireEvent("specialkey", this, e)){
12638                         this.onViewClick(false);
12639                     }
12640                     
12641                     return true;
12642                 },
12643
12644                 "esc" : function(e){
12645                     this.onTickableFooterButtonClick(e, false, false);
12646                 },
12647
12648                 "tab" : function(e){
12649                     this.fireEvent("specialkey", this, e);
12650                     
12651                     this.onTickableFooterButtonClick(e, false, false);
12652                     
12653                     return true;
12654                 },
12655
12656                 scope : this,
12657
12658                 doRelay : function(e, fn, key){
12659                     if(this.scope.isExpanded()){
12660                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12661                     }
12662                     return true;
12663                 },
12664
12665                 forceKeyDown: true
12666             });
12667         }
12668         
12669         this.queryDelay = Math.max(this.queryDelay || 10,
12670                 this.mode == 'local' ? 10 : 250);
12671         
12672         
12673         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12674         
12675         if(this.typeAhead){
12676             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12677         }
12678         
12679         if(this.editable !== false){
12680             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12681         }
12682         
12683     },
12684
12685     onDestroy : function(){
12686         if(this.view){
12687             this.view.setStore(null);
12688             this.view.el.removeAllListeners();
12689             this.view.el.remove();
12690             this.view.purgeListeners();
12691         }
12692         if(this.list){
12693             this.list.dom.innerHTML  = '';
12694         }
12695         
12696         if(this.store){
12697             this.store.un('beforeload', this.onBeforeLoad, this);
12698             this.store.un('load', this.onLoad, this);
12699             this.store.un('loadexception', this.onLoadException, this);
12700         }
12701         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12702     },
12703
12704     // private
12705     fireKey : function(e){
12706         if(e.isNavKeyPress() && !this.list.isVisible()){
12707             this.fireEvent("specialkey", this, e);
12708         }
12709     },
12710
12711     // private
12712     onResize: function(w, h){
12713 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12714 //        
12715 //        if(typeof w != 'number'){
12716 //            // we do not handle it!?!?
12717 //            return;
12718 //        }
12719 //        var tw = this.trigger.getWidth();
12720 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12721 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12722 //        var x = w - tw;
12723 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12724 //            
12725 //        //this.trigger.setStyle('left', x+'px');
12726 //        
12727 //        if(this.list && this.listWidth === undefined){
12728 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12729 //            this.list.setWidth(lw);
12730 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12731 //        }
12732         
12733     
12734         
12735     },
12736
12737     /**
12738      * Allow or prevent the user from directly editing the field text.  If false is passed,
12739      * the user will only be able to select from the items defined in the dropdown list.  This method
12740      * is the runtime equivalent of setting the 'editable' config option at config time.
12741      * @param {Boolean} value True to allow the user to directly edit the field text
12742      */
12743     setEditable : function(value){
12744         if(value == this.editable){
12745             return;
12746         }
12747         this.editable = value;
12748         if(!value){
12749             this.inputEl().dom.setAttribute('readOnly', true);
12750             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12751             this.inputEl().addClass('x-combo-noedit');
12752         }else{
12753             this.inputEl().dom.setAttribute('readOnly', false);
12754             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12755             this.inputEl().removeClass('x-combo-noedit');
12756         }
12757     },
12758
12759     // private
12760     
12761     onBeforeLoad : function(combo,opts){
12762         if(!this.hasFocus){
12763             return;
12764         }
12765          if (!opts.add) {
12766             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12767          }
12768         this.restrictHeight();
12769         this.selectedIndex = -1;
12770     },
12771
12772     // private
12773     onLoad : function(){
12774         
12775         this.hasQuery = false;
12776         
12777         if(!this.hasFocus){
12778             return;
12779         }
12780         
12781         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12782             this.loading.hide();
12783         }
12784              
12785         if(this.store.getCount() > 0){
12786             this.expand();
12787             this.restrictHeight();
12788             if(this.lastQuery == this.allQuery){
12789                 if(this.editable && !this.tickable){
12790                     this.inputEl().dom.select();
12791                 }
12792                 
12793                 if(
12794                     !this.selectByValue(this.value, true) &&
12795                     this.autoFocus && 
12796                     (
12797                         !this.store.lastOptions ||
12798                         typeof(this.store.lastOptions.add) == 'undefined' || 
12799                         this.store.lastOptions.add != true
12800                     )
12801                 ){
12802                     this.select(0, true);
12803                 }
12804             }else{
12805                 if(this.autoFocus){
12806                     this.selectNext();
12807                 }
12808                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12809                     this.taTask.delay(this.typeAheadDelay);
12810                 }
12811             }
12812         }else{
12813             this.onEmptyResults();
12814         }
12815         
12816         //this.el.focus();
12817     },
12818     // private
12819     onLoadException : function()
12820     {
12821         this.hasQuery = false;
12822         
12823         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12824             this.loading.hide();
12825         }
12826         
12827         if(this.tickable && this.editable){
12828             return;
12829         }
12830         
12831         this.collapse();
12832         // only causes errors at present
12833         //Roo.log(this.store.reader.jsonData);
12834         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12835             // fixme
12836             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12837         //}
12838         
12839         
12840     },
12841     // private
12842     onTypeAhead : function(){
12843         if(this.store.getCount() > 0){
12844             var r = this.store.getAt(0);
12845             var newValue = r.data[this.displayField];
12846             var len = newValue.length;
12847             var selStart = this.getRawValue().length;
12848             
12849             if(selStart != len){
12850                 this.setRawValue(newValue);
12851                 this.selectText(selStart, newValue.length);
12852             }
12853         }
12854     },
12855
12856     // private
12857     onSelect : function(record, index){
12858         
12859         if(this.fireEvent('beforeselect', this, record, index) !== false){
12860         
12861             this.setFromData(index > -1 ? record.data : false);
12862             
12863             this.collapse();
12864             this.fireEvent('select', this, record, index);
12865         }
12866     },
12867
12868     /**
12869      * Returns the currently selected field value or empty string if no value is set.
12870      * @return {String} value The selected value
12871      */
12872     getValue : function(){
12873         
12874         if(this.multiple){
12875             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12876         }
12877         
12878         if(this.valueField){
12879             return typeof this.value != 'undefined' ? this.value : '';
12880         }else{
12881             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12882         }
12883     },
12884
12885     /**
12886      * Clears any text/value currently set in the field
12887      */
12888     clearValue : function(){
12889         if(this.hiddenField){
12890             this.hiddenField.dom.value = '';
12891         }
12892         this.value = '';
12893         this.setRawValue('');
12894         this.lastSelectionText = '';
12895         this.lastData = false;
12896         
12897         var close = this.closeTriggerEl();
12898         
12899         if(close){
12900             close.hide();
12901         }
12902         
12903     },
12904
12905     /**
12906      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12907      * will be displayed in the field.  If the value does not match the data value of an existing item,
12908      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12909      * Otherwise the field will be blank (although the value will still be set).
12910      * @param {String} value The value to match
12911      */
12912     setValue : function(v){
12913         if(this.multiple){
12914             this.syncValue();
12915             return;
12916         }
12917         
12918         var text = v;
12919         if(this.valueField){
12920             var r = this.findRecord(this.valueField, v);
12921             if(r){
12922                 text = r.data[this.displayField];
12923             }else if(this.valueNotFoundText !== undefined){
12924                 text = this.valueNotFoundText;
12925             }
12926         }
12927         this.lastSelectionText = text;
12928         if(this.hiddenField){
12929             this.hiddenField.dom.value = v;
12930         }
12931         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12932         this.value = v;
12933         
12934         var close = this.closeTriggerEl();
12935         
12936         if(close){
12937             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12938         }
12939     },
12940     /**
12941      * @property {Object} the last set data for the element
12942      */
12943     
12944     lastData : false,
12945     /**
12946      * Sets the value of the field based on a object which is related to the record format for the store.
12947      * @param {Object} value the value to set as. or false on reset?
12948      */
12949     setFromData : function(o){
12950         
12951         if(this.multiple){
12952             this.addItem(o);
12953             return;
12954         }
12955             
12956         var dv = ''; // display value
12957         var vv = ''; // value value..
12958         this.lastData = o;
12959         if (this.displayField) {
12960             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12961         } else {
12962             // this is an error condition!!!
12963             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12964         }
12965         
12966         if(this.valueField){
12967             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12968         }
12969         
12970         var close = this.closeTriggerEl();
12971         
12972         if(close){
12973             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12974         }
12975         
12976         if(this.hiddenField){
12977             this.hiddenField.dom.value = vv;
12978             
12979             this.lastSelectionText = dv;
12980             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12981             this.value = vv;
12982             return;
12983         }
12984         // no hidden field.. - we store the value in 'value', but still display
12985         // display field!!!!
12986         this.lastSelectionText = dv;
12987         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12988         this.value = vv;
12989         
12990         
12991         
12992     },
12993     // private
12994     reset : function(){
12995         // overridden so that last data is reset..
12996         
12997         if(this.multiple){
12998             this.clearItem();
12999             return;
13000         }
13001         
13002         this.setValue(this.originalValue);
13003         this.clearInvalid();
13004         this.lastData = false;
13005         if (this.view) {
13006             this.view.clearSelections();
13007         }
13008     },
13009     // private
13010     findRecord : function(prop, value){
13011         var record;
13012         if(this.store.getCount() > 0){
13013             this.store.each(function(r){
13014                 if(r.data[prop] == value){
13015                     record = r;
13016                     return false;
13017                 }
13018                 return true;
13019             });
13020         }
13021         return record;
13022     },
13023     
13024     getName: function()
13025     {
13026         // returns hidden if it's set..
13027         if (!this.rendered) {return ''};
13028         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13029         
13030     },
13031     // private
13032     onViewMove : function(e, t){
13033         this.inKeyMode = false;
13034     },
13035
13036     // private
13037     onViewOver : function(e, t){
13038         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13039             return;
13040         }
13041         var item = this.view.findItemFromChild(t);
13042         
13043         if(item){
13044             var index = this.view.indexOf(item);
13045             this.select(index, false);
13046         }
13047     },
13048
13049     // private
13050     onViewClick : function(view, doFocus, el, e)
13051     {
13052         var index = this.view.getSelectedIndexes()[0];
13053         
13054         var r = this.store.getAt(index);
13055         
13056         if(this.tickable){
13057             
13058             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13059                 return;
13060             }
13061             
13062             var rm = false;
13063             var _this = this;
13064             
13065             Roo.each(this.tickItems, function(v,k){
13066                 
13067                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13068                     Roo.log(v);
13069                     _this.tickItems.splice(k, 1);
13070                     
13071                     if(typeof(e) == 'undefined' && view == false){
13072                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13073                     }
13074                     
13075                     rm = true;
13076                     return;
13077                 }
13078             });
13079             
13080             if(rm){
13081                 return;
13082             }
13083             
13084             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13085                 this.tickItems.push(r.data);
13086             }
13087             
13088             if(typeof(e) == 'undefined' && view == false){
13089                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13090             }
13091                     
13092             return;
13093         }
13094         
13095         if(r){
13096             this.onSelect(r, index);
13097         }
13098         if(doFocus !== false && !this.blockFocus){
13099             this.inputEl().focus();
13100         }
13101     },
13102
13103     // private
13104     restrictHeight : function(){
13105         //this.innerList.dom.style.height = '';
13106         //var inner = this.innerList.dom;
13107         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13108         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13109         //this.list.beginUpdate();
13110         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13111         this.list.alignTo(this.inputEl(), this.listAlign);
13112         this.list.alignTo(this.inputEl(), this.listAlign);
13113         //this.list.endUpdate();
13114     },
13115
13116     // private
13117     onEmptyResults : function(){
13118         
13119         if(this.tickable && this.editable){
13120             this.restrictHeight();
13121             return;
13122         }
13123         
13124         this.collapse();
13125     },
13126
13127     /**
13128      * Returns true if the dropdown list is expanded, else false.
13129      */
13130     isExpanded : function(){
13131         return this.list.isVisible();
13132     },
13133
13134     /**
13135      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13136      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13137      * @param {String} value The data value of the item to select
13138      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13139      * selected item if it is not currently in view (defaults to true)
13140      * @return {Boolean} True if the value matched an item in the list, else false
13141      */
13142     selectByValue : function(v, scrollIntoView){
13143         if(v !== undefined && v !== null){
13144             var r = this.findRecord(this.valueField || this.displayField, v);
13145             if(r){
13146                 this.select(this.store.indexOf(r), scrollIntoView);
13147                 return true;
13148             }
13149         }
13150         return false;
13151     },
13152
13153     /**
13154      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13155      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13156      * @param {Number} index The zero-based index of the list item to select
13157      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13158      * selected item if it is not currently in view (defaults to true)
13159      */
13160     select : function(index, scrollIntoView){
13161         this.selectedIndex = index;
13162         this.view.select(index);
13163         if(scrollIntoView !== false){
13164             var el = this.view.getNode(index);
13165             /*
13166              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13167              */
13168             if(el){
13169                 this.list.scrollChildIntoView(el, false);
13170             }
13171         }
13172     },
13173
13174     // private
13175     selectNext : function(){
13176         var ct = this.store.getCount();
13177         if(ct > 0){
13178             if(this.selectedIndex == -1){
13179                 this.select(0);
13180             }else if(this.selectedIndex < ct-1){
13181                 this.select(this.selectedIndex+1);
13182             }
13183         }
13184     },
13185
13186     // private
13187     selectPrev : function(){
13188         var ct = this.store.getCount();
13189         if(ct > 0){
13190             if(this.selectedIndex == -1){
13191                 this.select(0);
13192             }else if(this.selectedIndex != 0){
13193                 this.select(this.selectedIndex-1);
13194             }
13195         }
13196     },
13197
13198     // private
13199     onKeyUp : function(e){
13200         if(this.editable !== false && !e.isSpecialKey()){
13201             this.lastKey = e.getKey();
13202             this.dqTask.delay(this.queryDelay);
13203         }
13204     },
13205
13206     // private
13207     validateBlur : function(){
13208         return !this.list || !this.list.isVisible();   
13209     },
13210
13211     // private
13212     initQuery : function(){
13213         
13214         var v = this.getRawValue();
13215         
13216         if(this.tickable && this.editable){
13217             v = this.tickableInputEl().getValue();
13218         }
13219         
13220         this.doQuery(v);
13221     },
13222
13223     // private
13224     doForce : function(){
13225         if(this.inputEl().dom.value.length > 0){
13226             this.inputEl().dom.value =
13227                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13228              
13229         }
13230     },
13231
13232     /**
13233      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13234      * query allowing the query action to be canceled if needed.
13235      * @param {String} query The SQL query to execute
13236      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13237      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13238      * saved in the current store (defaults to false)
13239      */
13240     doQuery : function(q, forceAll){
13241         
13242         if(q === undefined || q === null){
13243             q = '';
13244         }
13245         var qe = {
13246             query: q,
13247             forceAll: forceAll,
13248             combo: this,
13249             cancel:false
13250         };
13251         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13252             return false;
13253         }
13254         q = qe.query;
13255         
13256         forceAll = qe.forceAll;
13257         if(forceAll === true || (q.length >= this.minChars)){
13258             
13259             this.hasQuery = true;
13260             
13261             if(this.lastQuery != q || this.alwaysQuery){
13262                 this.lastQuery = q;
13263                 if(this.mode == 'local'){
13264                     this.selectedIndex = -1;
13265                     if(forceAll){
13266                         this.store.clearFilter();
13267                     }else{
13268                         
13269                         if(this.specialFilter){
13270                             this.fireEvent('specialfilter', this);
13271                             this.onLoad();
13272                             return;
13273                         }
13274                         
13275                         this.store.filter(this.displayField, q);
13276                     }
13277                     
13278                     this.store.fireEvent("datachanged", this.store);
13279                     
13280                     this.onLoad();
13281                     
13282                     
13283                 }else{
13284                     
13285                     this.store.baseParams[this.queryParam] = q;
13286                     
13287                     var options = {params : this.getParams(q)};
13288                     
13289                     if(this.loadNext){
13290                         options.add = true;
13291                         options.params.start = this.page * this.pageSize;
13292                     }
13293                     
13294                     this.store.load(options);
13295                     
13296                     /*
13297                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13298                      *  we should expand the list on onLoad
13299                      *  so command out it
13300                      */
13301 //                    this.expand();
13302                 }
13303             }else{
13304                 this.selectedIndex = -1;
13305                 this.onLoad();   
13306             }
13307         }
13308         
13309         this.loadNext = false;
13310     },
13311     
13312     // private
13313     getParams : function(q){
13314         var p = {};
13315         //p[this.queryParam] = q;
13316         
13317         if(this.pageSize){
13318             p.start = 0;
13319             p.limit = this.pageSize;
13320         }
13321         return p;
13322     },
13323
13324     /**
13325      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13326      */
13327     collapse : function(){
13328         if(!this.isExpanded()){
13329             return;
13330         }
13331         
13332         this.list.hide();
13333         
13334         if(this.tickable){
13335             this.hasFocus = false;
13336             this.okBtn.hide();
13337             this.cancelBtn.hide();
13338             this.trigger.show();
13339             
13340             if(this.editable){
13341                 this.tickableInputEl().dom.value = '';
13342                 this.tickableInputEl().blur();
13343             }
13344             
13345         }
13346         
13347         Roo.get(document).un('mousedown', this.collapseIf, this);
13348         Roo.get(document).un('mousewheel', this.collapseIf, this);
13349         if (!this.editable) {
13350             Roo.get(document).un('keydown', this.listKeyPress, this);
13351         }
13352         this.fireEvent('collapse', this);
13353     },
13354
13355     // private
13356     collapseIf : function(e){
13357         var in_combo  = e.within(this.el);
13358         var in_list =  e.within(this.list);
13359         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13360         
13361         if (in_combo || in_list || is_list) {
13362             //e.stopPropagation();
13363             return;
13364         }
13365         
13366         if(this.tickable){
13367             this.onTickableFooterButtonClick(e, false, false);
13368         }
13369
13370         this.collapse();
13371         
13372     },
13373
13374     /**
13375      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13376      */
13377     expand : function(){
13378        
13379         if(this.isExpanded() || !this.hasFocus){
13380             return;
13381         }
13382         
13383         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13384         this.list.setWidth(lw);
13385         
13386         
13387          Roo.log('expand');
13388         
13389         this.list.show();
13390         
13391         this.restrictHeight();
13392         
13393         if(this.tickable){
13394             
13395             this.tickItems = Roo.apply([], this.item);
13396             
13397             this.okBtn.show();
13398             this.cancelBtn.show();
13399             this.trigger.hide();
13400             
13401             if(this.editable){
13402                 this.tickableInputEl().focus();
13403             }
13404             
13405         }
13406         
13407         Roo.get(document).on('mousedown', this.collapseIf, this);
13408         Roo.get(document).on('mousewheel', this.collapseIf, this);
13409         if (!this.editable) {
13410             Roo.get(document).on('keydown', this.listKeyPress, this);
13411         }
13412         
13413         this.fireEvent('expand', this);
13414     },
13415
13416     // private
13417     // Implements the default empty TriggerField.onTriggerClick function
13418     onTriggerClick : function(e)
13419     {
13420         Roo.log('trigger click');
13421         
13422         if(this.disabled || !this.triggerList){
13423             return;
13424         }
13425         
13426         this.page = 0;
13427         this.loadNext = false;
13428         
13429         if(this.isExpanded()){
13430             this.collapse();
13431             if (!this.blockFocus) {
13432                 this.inputEl().focus();
13433             }
13434             
13435         }else {
13436             this.hasFocus = true;
13437             if(this.triggerAction == 'all') {
13438                 this.doQuery(this.allQuery, true);
13439             } else {
13440                 this.doQuery(this.getRawValue());
13441             }
13442             if (!this.blockFocus) {
13443                 this.inputEl().focus();
13444             }
13445         }
13446     },
13447     
13448     onTickableTriggerClick : function(e)
13449     {
13450         if(this.disabled){
13451             return;
13452         }
13453         
13454         this.page = 0;
13455         this.loadNext = false;
13456         this.hasFocus = true;
13457         
13458         if(this.triggerAction == 'all') {
13459             this.doQuery(this.allQuery, true);
13460         } else {
13461             this.doQuery(this.getRawValue());
13462         }
13463     },
13464     
13465     onSearchFieldClick : function(e)
13466     {
13467         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13468             this.onTickableFooterButtonClick(e, false, false);
13469             return;
13470         }
13471         
13472         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13473             return;
13474         }
13475         
13476         this.page = 0;
13477         this.loadNext = false;
13478         this.hasFocus = true;
13479         
13480         if(this.triggerAction == 'all') {
13481             this.doQuery(this.allQuery, true);
13482         } else {
13483             this.doQuery(this.getRawValue());
13484         }
13485     },
13486     
13487     listKeyPress : function(e)
13488     {
13489         //Roo.log('listkeypress');
13490         // scroll to first matching element based on key pres..
13491         if (e.isSpecialKey()) {
13492             return false;
13493         }
13494         var k = String.fromCharCode(e.getKey()).toUpperCase();
13495         //Roo.log(k);
13496         var match  = false;
13497         var csel = this.view.getSelectedNodes();
13498         var cselitem = false;
13499         if (csel.length) {
13500             var ix = this.view.indexOf(csel[0]);
13501             cselitem  = this.store.getAt(ix);
13502             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13503                 cselitem = false;
13504             }
13505             
13506         }
13507         
13508         this.store.each(function(v) { 
13509             if (cselitem) {
13510                 // start at existing selection.
13511                 if (cselitem.id == v.id) {
13512                     cselitem = false;
13513                 }
13514                 return true;
13515             }
13516                 
13517             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13518                 match = this.store.indexOf(v);
13519                 return false;
13520             }
13521             return true;
13522         }, this);
13523         
13524         if (match === false) {
13525             return true; // no more action?
13526         }
13527         // scroll to?
13528         this.view.select(match);
13529         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13530         sn.scrollIntoView(sn.dom.parentNode, false);
13531     },
13532     
13533     onViewScroll : function(e, t){
13534         
13535         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13536             return;
13537         }
13538         
13539         this.hasQuery = true;
13540         
13541         this.loading = this.list.select('.loading', true).first();
13542         
13543         if(this.loading === null){
13544             this.list.createChild({
13545                 tag: 'div',
13546                 cls: 'loading roo-select2-more-results roo-select2-active',
13547                 html: 'Loading more results...'
13548             });
13549             
13550             this.loading = this.list.select('.loading', true).first();
13551             
13552             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13553             
13554             this.loading.hide();
13555         }
13556         
13557         this.loading.show();
13558         
13559         var _combo = this;
13560         
13561         this.page++;
13562         this.loadNext = true;
13563         
13564         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13565         
13566         return;
13567     },
13568     
13569     addItem : function(o)
13570     {   
13571         var dv = ''; // display value
13572         
13573         if (this.displayField) {
13574             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13575         } else {
13576             // this is an error condition!!!
13577             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13578         }
13579         
13580         if(!dv.length){
13581             return;
13582         }
13583         
13584         var choice = this.choices.createChild({
13585             tag: 'li',
13586             cls: 'roo-select2-search-choice',
13587             cn: [
13588                 {
13589                     tag: 'div',
13590                     html: dv
13591                 },
13592                 {
13593                     tag: 'a',
13594                     href: '#',
13595                     cls: 'roo-select2-search-choice-close',
13596                     tabindex: '-1'
13597                 }
13598             ]
13599             
13600         }, this.searchField);
13601         
13602         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13603         
13604         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13605         
13606         this.item.push(o);
13607         
13608         this.lastData = o;
13609         
13610         this.syncValue();
13611         
13612         this.inputEl().dom.value = '';
13613         
13614         this.validate();
13615     },
13616     
13617     onRemoveItem : function(e, _self, o)
13618     {
13619         e.preventDefault();
13620         
13621         this.lastItem = Roo.apply([], this.item);
13622         
13623         var index = this.item.indexOf(o.data) * 1;
13624         
13625         if( index < 0){
13626             Roo.log('not this item?!');
13627             return;
13628         }
13629         
13630         this.item.splice(index, 1);
13631         o.item.remove();
13632         
13633         this.syncValue();
13634         
13635         this.fireEvent('remove', this, e);
13636         
13637         this.validate();
13638         
13639     },
13640     
13641     syncValue : function()
13642     {
13643         if(!this.item.length){
13644             this.clearValue();
13645             return;
13646         }
13647             
13648         var value = [];
13649         var _this = this;
13650         Roo.each(this.item, function(i){
13651             if(_this.valueField){
13652                 value.push(i[_this.valueField]);
13653                 return;
13654             }
13655
13656             value.push(i);
13657         });
13658
13659         this.value = value.join(',');
13660
13661         if(this.hiddenField){
13662             this.hiddenField.dom.value = this.value;
13663         }
13664         
13665         this.store.fireEvent("datachanged", this.store);
13666     },
13667     
13668     clearItem : function()
13669     {
13670         if(!this.multiple){
13671             return;
13672         }
13673         
13674         this.item = [];
13675         
13676         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13677            c.remove();
13678         });
13679         
13680         this.syncValue();
13681         
13682         this.validate();
13683         
13684         if(this.tickable && !Roo.isTouch){
13685             this.view.refresh();
13686         }
13687     },
13688     
13689     inputEl: function ()
13690     {
13691         if(Roo.isTouch && this.mobileTouchView){
13692             return this.el.select('input.form-control',true).first();
13693         }
13694         
13695         if(this.tickable){
13696             return this.searchField;
13697         }
13698         
13699         return this.el.select('input.form-control',true).first();
13700     },
13701     
13702     
13703     onTickableFooterButtonClick : function(e, btn, el)
13704     {
13705         e.preventDefault();
13706         
13707         this.lastItem = Roo.apply([], this.item);
13708         
13709         if(btn && btn.name == 'cancel'){
13710             this.tickItems = Roo.apply([], this.item);
13711             this.collapse();
13712             return;
13713         }
13714         
13715         this.clearItem();
13716         
13717         var _this = this;
13718         
13719         Roo.each(this.tickItems, function(o){
13720             _this.addItem(o);
13721         });
13722         
13723         this.collapse();
13724         
13725     },
13726     
13727     validate : function()
13728     {
13729         var v = this.getRawValue();
13730         
13731         if(this.multiple){
13732             v = this.getValue();
13733         }
13734         
13735         if(this.disabled || this.allowBlank || v.length){
13736             this.markValid();
13737             return true;
13738         }
13739         
13740         this.markInvalid();
13741         return false;
13742     },
13743     
13744     tickableInputEl : function()
13745     {
13746         if(!this.tickable || !this.editable){
13747             return this.inputEl();
13748         }
13749         
13750         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13751     },
13752     
13753     
13754     getAutoCreateTouchView : function()
13755     {
13756         var id = Roo.id();
13757         
13758         var cfg = {
13759             cls: 'form-group' //input-group
13760         };
13761         
13762         var input =  {
13763             tag: 'input',
13764             id : id,
13765             type : this.inputType,
13766             cls : 'form-control x-combo-noedit',
13767             autocomplete: 'new-password',
13768             placeholder : this.placeholder || '',
13769             readonly : true
13770         };
13771         
13772         if (this.name) {
13773             input.name = this.name;
13774         }
13775         
13776         if (this.size) {
13777             input.cls += ' input-' + this.size;
13778         }
13779         
13780         if (this.disabled) {
13781             input.disabled = true;
13782         }
13783         
13784         var inputblock = {
13785             cls : '',
13786             cn : [
13787                 input
13788             ]
13789         };
13790         
13791         if(this.before){
13792             inputblock.cls += ' input-group';
13793             
13794             inputblock.cn.unshift({
13795                 tag :'span',
13796                 cls : 'input-group-addon',
13797                 html : this.before
13798             });
13799         }
13800         
13801         if(this.removable && !this.multiple){
13802             inputblock.cls += ' roo-removable';
13803             
13804             inputblock.cn.push({
13805                 tag: 'button',
13806                 html : 'x',
13807                 cls : 'roo-combo-removable-btn close'
13808             });
13809         }
13810
13811         if(this.hasFeedback && !this.allowBlank){
13812             
13813             inputblock.cls += ' has-feedback';
13814             
13815             inputblock.cn.push({
13816                 tag: 'span',
13817                 cls: 'glyphicon form-control-feedback'
13818             });
13819             
13820         }
13821         
13822         if (this.after) {
13823             
13824             inputblock.cls += (this.before) ? '' : ' input-group';
13825             
13826             inputblock.cn.push({
13827                 tag :'span',
13828                 cls : 'input-group-addon',
13829                 html : this.after
13830             });
13831         }
13832
13833         var box = {
13834             tag: 'div',
13835             cn: [
13836                 {
13837                     tag: 'input',
13838                     type : 'hidden',
13839                     cls: 'form-hidden-field'
13840                 },
13841                 inputblock
13842             ]
13843             
13844         };
13845         
13846         if(this.multiple){
13847             box = {
13848                 tag: 'div',
13849                 cn: [
13850                     {
13851                         tag: 'input',
13852                         type : 'hidden',
13853                         cls: 'form-hidden-field'
13854                     },
13855                     {
13856                         tag: 'ul',
13857                         cls: 'roo-select2-choices',
13858                         cn:[
13859                             {
13860                                 tag: 'li',
13861                                 cls: 'roo-select2-search-field',
13862                                 cn: [
13863
13864                                     inputblock
13865                                 ]
13866                             }
13867                         ]
13868                     }
13869                 ]
13870             }
13871         };
13872         
13873         var combobox = {
13874             cls: 'roo-select2-container input-group',
13875             cn: [
13876                 box
13877             ]
13878         };
13879         
13880         if(this.multiple){
13881             combobox.cls += ' roo-select2-container-multi';
13882         }
13883         
13884         var align = this.labelAlign || this.parentLabelAlign();
13885         
13886         cfg.cn = combobox;
13887         
13888         if(this.fieldLabel.length){
13889             
13890             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13891             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13892             
13893             cfg.cn = [
13894                 {
13895                     tag: 'label',
13896                     cls : 'control-label ' + lw,
13897                     html : this.fieldLabel
13898
13899                 },
13900                 {
13901                     cls : cw, 
13902                     cn: [
13903                         combobox
13904                     ]
13905                 }
13906             ];
13907         }
13908         
13909         var settings = this;
13910         
13911         ['xs','sm','md','lg'].map(function(size){
13912             if (settings[size]) {
13913                 cfg.cls += ' col-' + size + '-' + settings[size];
13914             }
13915         });
13916         
13917         return cfg;
13918     },
13919     
13920     initTouchView : function()
13921     {
13922         this.renderTouchView();
13923         
13924         this.touchViewEl.on('scroll', function(){
13925             this.el.dom.scrollTop = 0;
13926         }, this);
13927         
13928         this.originalValue = this.getValue();
13929         
13930         this.inputEl().on("click", this.showTouchView, this);
13931         
13932         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13933         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13934         
13935         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13936         
13937         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13938         this.store.on('load', this.onTouchViewLoad, this);
13939         this.store.on('loadexception', this.onTouchViewLoadException, this);
13940         
13941         if(this.hiddenName){
13942             
13943             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13944             
13945             this.hiddenField.dom.value =
13946                 this.hiddenValue !== undefined ? this.hiddenValue :
13947                 this.value !== undefined ? this.value : '';
13948         
13949             this.el.dom.removeAttribute('name');
13950             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13951         }
13952         
13953         if(this.multiple){
13954             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13955             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13956         }
13957         
13958         if(this.removable && !this.multiple){
13959             var close = this.closeTriggerEl();
13960             if(close){
13961                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13962                 close.on('click', this.removeBtnClick, this, close);
13963             }
13964         }
13965         /*
13966          * fix the bug in Safari iOS8
13967          */
13968         this.inputEl().on("focus", function(e){
13969             document.activeElement.blur();
13970         }, this);
13971         
13972         return;
13973         
13974         
13975     },
13976     
13977     renderTouchView : function()
13978     {
13979         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13980         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13981         
13982         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13983         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13984         
13985         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13986         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13987         this.touchViewBodyEl.setStyle('overflow', 'auto');
13988         
13989         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13990         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13991         
13992         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13993         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13994         
13995     },
13996     
13997     showTouchView : function()
13998     {
13999         if(this.disabled){
14000             return;
14001         }
14002         
14003         this.touchViewHeaderEl.hide();
14004
14005         if(this.fieldLabel.length){
14006             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
14007             this.touchViewHeaderEl.show();
14008         }
14009
14010         this.touchViewEl.show();
14011
14012         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14013         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14014
14015         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14016
14017         if(this.fieldLabel.length){
14018             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14019         }
14020         
14021         this.touchViewBodyEl.setHeight(bodyHeight);
14022
14023         if(this.animate){
14024             var _this = this;
14025             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14026         }else{
14027             this.touchViewEl.addClass('in');
14028         }
14029
14030         this.doTouchViewQuery();
14031         
14032     },
14033     
14034     hideTouchView : function()
14035     {
14036         this.touchViewEl.removeClass('in');
14037
14038         if(this.animate){
14039             var _this = this;
14040             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14041         }else{
14042             this.touchViewEl.setStyle('display', 'none');
14043         }
14044         
14045     },
14046     
14047     setTouchViewValue : function()
14048     {
14049         if(this.multiple){
14050             this.clearItem();
14051         
14052             var _this = this;
14053
14054             Roo.each(this.tickItems, function(o){
14055                 this.addItem(o);
14056             }, this);
14057         }
14058         
14059         this.hideTouchView();
14060     },
14061     
14062     doTouchViewQuery : function()
14063     {
14064         var qe = {
14065             query: '',
14066             forceAll: true,
14067             combo: this,
14068             cancel:false
14069         };
14070         
14071         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14072             return false;
14073         }
14074         
14075         if(!this.alwaysQuery || this.mode == 'local'){
14076             this.onTouchViewLoad();
14077             return;
14078         }
14079         
14080         this.store.load();
14081     },
14082     
14083     onTouchViewBeforeLoad : function(combo,opts)
14084     {
14085         return;
14086     },
14087
14088     // private
14089     onTouchViewLoad : function()
14090     {
14091         if(this.store.getCount() < 1){
14092             this.onTouchViewEmptyResults();
14093             return;
14094         }
14095         
14096         this.clearTouchView();
14097         
14098         var rawValue = this.getRawValue();
14099         
14100         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14101         
14102         this.tickItems = [];
14103         
14104         this.store.data.each(function(d, rowIndex){
14105             var row = this.touchViewListGroup.createChild(template);
14106             
14107             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14108                 row.addClass(d.data.cls);
14109             }
14110             
14111             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14112                 var cfg = {
14113                     data : d.data,
14114                     html : d.data[this.displayField]
14115                 };
14116                 
14117                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14118                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14119                 }
14120             }
14121             
14122             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
14123                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14124             }
14125             
14126             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
14127                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14128                 this.tickItems.push(d.data);
14129             }
14130             
14131             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14132             
14133         }, this);
14134         
14135         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14136         
14137         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14138
14139         if(this.fieldLabel.length){
14140             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14141         }
14142
14143         var listHeight = this.touchViewListGroup.getHeight();
14144         
14145         var _this = this;
14146         
14147         if(firstChecked && listHeight > bodyHeight){
14148             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14149         }
14150         
14151     },
14152     
14153     onTouchViewLoadException : function()
14154     {
14155         this.hideTouchView();
14156     },
14157     
14158     onTouchViewEmptyResults : function()
14159     {
14160         this.clearTouchView();
14161         
14162         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14163         
14164         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14165         
14166     },
14167     
14168     clearTouchView : function()
14169     {
14170         this.touchViewListGroup.dom.innerHTML = '';
14171     },
14172     
14173     onTouchViewClick : function(e, el, o)
14174     {
14175         e.preventDefault();
14176         
14177         var row = o.row;
14178         var rowIndex = o.rowIndex;
14179         
14180         var r = this.store.getAt(rowIndex);
14181         
14182         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14183             
14184             if(!this.multiple){
14185                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14186                     c.dom.removeAttribute('checked');
14187                 }, this);
14188
14189                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14190
14191                 this.setFromData(r.data);
14192
14193                 var close = this.closeTriggerEl();
14194
14195                 if(close){
14196                     close.show();
14197                 }
14198
14199                 this.hideTouchView();
14200
14201                 this.fireEvent('select', this, r, rowIndex);
14202
14203                 return;
14204             }
14205
14206             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14207                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14208                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14209                 return;
14210             }
14211
14212             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14213             this.addItem(r.data);
14214             this.tickItems.push(r.data);
14215         }
14216     }
14217     
14218
14219     /** 
14220     * @cfg {Boolean} grow 
14221     * @hide 
14222     */
14223     /** 
14224     * @cfg {Number} growMin 
14225     * @hide 
14226     */
14227     /** 
14228     * @cfg {Number} growMax 
14229     * @hide 
14230     */
14231     /**
14232      * @hide
14233      * @method autoSize
14234      */
14235 });
14236
14237 Roo.apply(Roo.bootstrap.ComboBox,  {
14238     
14239     header : {
14240         tag: 'div',
14241         cls: 'modal-header',
14242         cn: [
14243             {
14244                 tag: 'h4',
14245                 cls: 'modal-title'
14246             }
14247         ]
14248     },
14249     
14250     body : {
14251         tag: 'div',
14252         cls: 'modal-body',
14253         cn: [
14254             {
14255                 tag: 'ul',
14256                 cls: 'list-group'
14257             }
14258         ]
14259     },
14260     
14261     listItemRadio : {
14262         tag: 'li',
14263         cls: 'list-group-item',
14264         cn: [
14265             {
14266                 tag: 'span',
14267                 cls: 'roo-combobox-list-group-item-value'
14268             },
14269             {
14270                 tag: 'div',
14271                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14272                 cn: [
14273                     {
14274                         tag: 'input',
14275                         type: 'radio'
14276                     },
14277                     {
14278                         tag: 'label'
14279                     }
14280                 ]
14281             }
14282         ]
14283     },
14284     
14285     listItemCheckbox : {
14286         tag: 'li',
14287         cls: 'list-group-item',
14288         cn: [
14289             {
14290                 tag: 'span',
14291                 cls: 'roo-combobox-list-group-item-value'
14292             },
14293             {
14294                 tag: 'div',
14295                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14296                 cn: [
14297                     {
14298                         tag: 'input',
14299                         type: 'checkbox'
14300                     },
14301                     {
14302                         tag: 'label'
14303                     }
14304                 ]
14305             }
14306         ]
14307     },
14308     
14309     emptyResult : {
14310         tag: 'div',
14311         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14312     },
14313     
14314     footer : {
14315         tag: 'div',
14316         cls: 'modal-footer',
14317         cn: [
14318             {
14319                 tag: 'div',
14320                 cls: 'row',
14321                 cn: [
14322                     {
14323                         tag: 'div',
14324                         cls: 'col-xs-6 text-left',
14325                         cn: {
14326                             tag: 'button',
14327                             cls: 'btn btn-danger roo-touch-view-cancel',
14328                             html: 'Cancel'
14329                         }
14330                     },
14331                     {
14332                         tag: 'div',
14333                         cls: 'col-xs-6 text-right',
14334                         cn: {
14335                             tag: 'button',
14336                             cls: 'btn btn-success roo-touch-view-ok',
14337                             html: 'OK'
14338                         }
14339                     }
14340                 ]
14341             }
14342         ]
14343         
14344     }
14345 });
14346
14347 Roo.apply(Roo.bootstrap.ComboBox,  {
14348     
14349     touchViewTemplate : {
14350         tag: 'div',
14351         cls: 'modal fade roo-combobox-touch-view',
14352         cn: [
14353             {
14354                 tag: 'div',
14355                 cls: 'modal-dialog',
14356                 style : 'position:fixed', // we have to fix position....
14357                 cn: [
14358                     {
14359                         tag: 'div',
14360                         cls: 'modal-content',
14361                         cn: [
14362                             Roo.bootstrap.ComboBox.header,
14363                             Roo.bootstrap.ComboBox.body,
14364                             Roo.bootstrap.ComboBox.footer
14365                         ]
14366                     }
14367                 ]
14368             }
14369         ]
14370     }
14371 });/*
14372  * Based on:
14373  * Ext JS Library 1.1.1
14374  * Copyright(c) 2006-2007, Ext JS, LLC.
14375  *
14376  * Originally Released Under LGPL - original licence link has changed is not relivant.
14377  *
14378  * Fork - LGPL
14379  * <script type="text/javascript">
14380  */
14381
14382 /**
14383  * @class Roo.View
14384  * @extends Roo.util.Observable
14385  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14386  * This class also supports single and multi selection modes. <br>
14387  * Create a data model bound view:
14388  <pre><code>
14389  var store = new Roo.data.Store(...);
14390
14391  var view = new Roo.View({
14392     el : "my-element",
14393     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14394  
14395     singleSelect: true,
14396     selectedClass: "ydataview-selected",
14397     store: store
14398  });
14399
14400  // listen for node click?
14401  view.on("click", function(vw, index, node, e){
14402  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14403  });
14404
14405  // load XML data
14406  dataModel.load("foobar.xml");
14407  </code></pre>
14408  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14409  * <br><br>
14410  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14411  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14412  * 
14413  * Note: old style constructor is still suported (container, template, config)
14414  * 
14415  * @constructor
14416  * Create a new View
14417  * @param {Object} config The config object
14418  * 
14419  */
14420 Roo.View = function(config, depreciated_tpl, depreciated_config){
14421     
14422     this.parent = false;
14423     
14424     if (typeof(depreciated_tpl) == 'undefined') {
14425         // new way.. - universal constructor.
14426         Roo.apply(this, config);
14427         this.el  = Roo.get(this.el);
14428     } else {
14429         // old format..
14430         this.el  = Roo.get(config);
14431         this.tpl = depreciated_tpl;
14432         Roo.apply(this, depreciated_config);
14433     }
14434     this.wrapEl  = this.el.wrap().wrap();
14435     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14436     
14437     
14438     if(typeof(this.tpl) == "string"){
14439         this.tpl = new Roo.Template(this.tpl);
14440     } else {
14441         // support xtype ctors..
14442         this.tpl = new Roo.factory(this.tpl, Roo);
14443     }
14444     
14445     
14446     this.tpl.compile();
14447     
14448     /** @private */
14449     this.addEvents({
14450         /**
14451          * @event beforeclick
14452          * Fires before a click is processed. Returns false to cancel the default action.
14453          * @param {Roo.View} this
14454          * @param {Number} index The index of the target node
14455          * @param {HTMLElement} node The target node
14456          * @param {Roo.EventObject} e The raw event object
14457          */
14458             "beforeclick" : true,
14459         /**
14460          * @event click
14461          * Fires when a template node is clicked.
14462          * @param {Roo.View} this
14463          * @param {Number} index The index of the target node
14464          * @param {HTMLElement} node The target node
14465          * @param {Roo.EventObject} e The raw event object
14466          */
14467             "click" : true,
14468         /**
14469          * @event dblclick
14470          * Fires when a template node is double clicked.
14471          * @param {Roo.View} this
14472          * @param {Number} index The index of the target node
14473          * @param {HTMLElement} node The target node
14474          * @param {Roo.EventObject} e The raw event object
14475          */
14476             "dblclick" : true,
14477         /**
14478          * @event contextmenu
14479          * Fires when a template node is right clicked.
14480          * @param {Roo.View} this
14481          * @param {Number} index The index of the target node
14482          * @param {HTMLElement} node The target node
14483          * @param {Roo.EventObject} e The raw event object
14484          */
14485             "contextmenu" : true,
14486         /**
14487          * @event selectionchange
14488          * Fires when the selected nodes change.
14489          * @param {Roo.View} this
14490          * @param {Array} selections Array of the selected nodes
14491          */
14492             "selectionchange" : true,
14493     
14494         /**
14495          * @event beforeselect
14496          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14497          * @param {Roo.View} this
14498          * @param {HTMLElement} node The node to be selected
14499          * @param {Array} selections Array of currently selected nodes
14500          */
14501             "beforeselect" : true,
14502         /**
14503          * @event preparedata
14504          * Fires on every row to render, to allow you to change the data.
14505          * @param {Roo.View} this
14506          * @param {Object} data to be rendered (change this)
14507          */
14508           "preparedata" : true
14509           
14510           
14511         });
14512
14513
14514
14515     this.el.on({
14516         "click": this.onClick,
14517         "dblclick": this.onDblClick,
14518         "contextmenu": this.onContextMenu,
14519         scope:this
14520     });
14521
14522     this.selections = [];
14523     this.nodes = [];
14524     this.cmp = new Roo.CompositeElementLite([]);
14525     if(this.store){
14526         this.store = Roo.factory(this.store, Roo.data);
14527         this.setStore(this.store, true);
14528     }
14529     
14530     if ( this.footer && this.footer.xtype) {
14531            
14532          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14533         
14534         this.footer.dataSource = this.store;
14535         this.footer.container = fctr;
14536         this.footer = Roo.factory(this.footer, Roo);
14537         fctr.insertFirst(this.el);
14538         
14539         // this is a bit insane - as the paging toolbar seems to detach the el..
14540 //        dom.parentNode.parentNode.parentNode
14541          // they get detached?
14542     }
14543     
14544     
14545     Roo.View.superclass.constructor.call(this);
14546     
14547     
14548 };
14549
14550 Roo.extend(Roo.View, Roo.util.Observable, {
14551     
14552      /**
14553      * @cfg {Roo.data.Store} store Data store to load data from.
14554      */
14555     store : false,
14556     
14557     /**
14558      * @cfg {String|Roo.Element} el The container element.
14559      */
14560     el : '',
14561     
14562     /**
14563      * @cfg {String|Roo.Template} tpl The template used by this View 
14564      */
14565     tpl : false,
14566     /**
14567      * @cfg {String} dataName the named area of the template to use as the data area
14568      *                          Works with domtemplates roo-name="name"
14569      */
14570     dataName: false,
14571     /**
14572      * @cfg {String} selectedClass The css class to add to selected nodes
14573      */
14574     selectedClass : "x-view-selected",
14575      /**
14576      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14577      */
14578     emptyText : "",
14579     
14580     /**
14581      * @cfg {String} text to display on mask (default Loading)
14582      */
14583     mask : false,
14584     /**
14585      * @cfg {Boolean} multiSelect Allow multiple selection
14586      */
14587     multiSelect : false,
14588     /**
14589      * @cfg {Boolean} singleSelect Allow single selection
14590      */
14591     singleSelect:  false,
14592     
14593     /**
14594      * @cfg {Boolean} toggleSelect - selecting 
14595      */
14596     toggleSelect : false,
14597     
14598     /**
14599      * @cfg {Boolean} tickable - selecting 
14600      */
14601     tickable : false,
14602     
14603     /**
14604      * Returns the element this view is bound to.
14605      * @return {Roo.Element}
14606      */
14607     getEl : function(){
14608         return this.wrapEl;
14609     },
14610     
14611     
14612
14613     /**
14614      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14615      */
14616     refresh : function(){
14617         //Roo.log('refresh');
14618         var t = this.tpl;
14619         
14620         // if we are using something like 'domtemplate', then
14621         // the what gets used is:
14622         // t.applySubtemplate(NAME, data, wrapping data..)
14623         // the outer template then get' applied with
14624         //     the store 'extra data'
14625         // and the body get's added to the
14626         //      roo-name="data" node?
14627         //      <span class='roo-tpl-{name}'></span> ?????
14628         
14629         
14630         
14631         this.clearSelections();
14632         this.el.update("");
14633         var html = [];
14634         var records = this.store.getRange();
14635         if(records.length < 1) {
14636             
14637             // is this valid??  = should it render a template??
14638             
14639             this.el.update(this.emptyText);
14640             return;
14641         }
14642         var el = this.el;
14643         if (this.dataName) {
14644             this.el.update(t.apply(this.store.meta)); //????
14645             el = this.el.child('.roo-tpl-' + this.dataName);
14646         }
14647         
14648         for(var i = 0, len = records.length; i < len; i++){
14649             var data = this.prepareData(records[i].data, i, records[i]);
14650             this.fireEvent("preparedata", this, data, i, records[i]);
14651             
14652             var d = Roo.apply({}, data);
14653             
14654             if(this.tickable){
14655                 Roo.apply(d, {'roo-id' : Roo.id()});
14656                 
14657                 var _this = this;
14658             
14659                 Roo.each(this.parent.item, function(item){
14660                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14661                         return;
14662                     }
14663                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14664                 });
14665             }
14666             
14667             html[html.length] = Roo.util.Format.trim(
14668                 this.dataName ?
14669                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14670                     t.apply(d)
14671             );
14672         }
14673         
14674         
14675         
14676         el.update(html.join(""));
14677         this.nodes = el.dom.childNodes;
14678         this.updateIndexes(0);
14679     },
14680     
14681
14682     /**
14683      * Function to override to reformat the data that is sent to
14684      * the template for each node.
14685      * DEPRICATED - use the preparedata event handler.
14686      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14687      * a JSON object for an UpdateManager bound view).
14688      */
14689     prepareData : function(data, index, record)
14690     {
14691         this.fireEvent("preparedata", this, data, index, record);
14692         return data;
14693     },
14694
14695     onUpdate : function(ds, record){
14696         // Roo.log('on update');   
14697         this.clearSelections();
14698         var index = this.store.indexOf(record);
14699         var n = this.nodes[index];
14700         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14701         n.parentNode.removeChild(n);
14702         this.updateIndexes(index, index);
14703     },
14704
14705     
14706     
14707 // --------- FIXME     
14708     onAdd : function(ds, records, index)
14709     {
14710         //Roo.log(['on Add', ds, records, index] );        
14711         this.clearSelections();
14712         if(this.nodes.length == 0){
14713             this.refresh();
14714             return;
14715         }
14716         var n = this.nodes[index];
14717         for(var i = 0, len = records.length; i < len; i++){
14718             var d = this.prepareData(records[i].data, i, records[i]);
14719             if(n){
14720                 this.tpl.insertBefore(n, d);
14721             }else{
14722                 
14723                 this.tpl.append(this.el, d);
14724             }
14725         }
14726         this.updateIndexes(index);
14727     },
14728
14729     onRemove : function(ds, record, index){
14730        // Roo.log('onRemove');
14731         this.clearSelections();
14732         var el = this.dataName  ?
14733             this.el.child('.roo-tpl-' + this.dataName) :
14734             this.el; 
14735         
14736         el.dom.removeChild(this.nodes[index]);
14737         this.updateIndexes(index);
14738     },
14739
14740     /**
14741      * Refresh an individual node.
14742      * @param {Number} index
14743      */
14744     refreshNode : function(index){
14745         this.onUpdate(this.store, this.store.getAt(index));
14746     },
14747
14748     updateIndexes : function(startIndex, endIndex){
14749         var ns = this.nodes;
14750         startIndex = startIndex || 0;
14751         endIndex = endIndex || ns.length - 1;
14752         for(var i = startIndex; i <= endIndex; i++){
14753             ns[i].nodeIndex = i;
14754         }
14755     },
14756
14757     /**
14758      * Changes the data store this view uses and refresh the view.
14759      * @param {Store} store
14760      */
14761     setStore : function(store, initial){
14762         if(!initial && this.store){
14763             this.store.un("datachanged", this.refresh);
14764             this.store.un("add", this.onAdd);
14765             this.store.un("remove", this.onRemove);
14766             this.store.un("update", this.onUpdate);
14767             this.store.un("clear", this.refresh);
14768             this.store.un("beforeload", this.onBeforeLoad);
14769             this.store.un("load", this.onLoad);
14770             this.store.un("loadexception", this.onLoad);
14771         }
14772         if(store){
14773           
14774             store.on("datachanged", this.refresh, this);
14775             store.on("add", this.onAdd, this);
14776             store.on("remove", this.onRemove, this);
14777             store.on("update", this.onUpdate, this);
14778             store.on("clear", this.refresh, this);
14779             store.on("beforeload", this.onBeforeLoad, this);
14780             store.on("load", this.onLoad, this);
14781             store.on("loadexception", this.onLoad, this);
14782         }
14783         
14784         if(store){
14785             this.refresh();
14786         }
14787     },
14788     /**
14789      * onbeforeLoad - masks the loading area.
14790      *
14791      */
14792     onBeforeLoad : function(store,opts)
14793     {
14794          //Roo.log('onBeforeLoad');   
14795         if (!opts.add) {
14796             this.el.update("");
14797         }
14798         this.el.mask(this.mask ? this.mask : "Loading" ); 
14799     },
14800     onLoad : function ()
14801     {
14802         this.el.unmask();
14803     },
14804     
14805
14806     /**
14807      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14808      * @param {HTMLElement} node
14809      * @return {HTMLElement} The template node
14810      */
14811     findItemFromChild : function(node){
14812         var el = this.dataName  ?
14813             this.el.child('.roo-tpl-' + this.dataName,true) :
14814             this.el.dom; 
14815         
14816         if(!node || node.parentNode == el){
14817                     return node;
14818             }
14819             var p = node.parentNode;
14820             while(p && p != el){
14821             if(p.parentNode == el){
14822                 return p;
14823             }
14824             p = p.parentNode;
14825         }
14826             return null;
14827     },
14828
14829     /** @ignore */
14830     onClick : function(e){
14831         var item = this.findItemFromChild(e.getTarget());
14832         if(item){
14833             var index = this.indexOf(item);
14834             if(this.onItemClick(item, index, e) !== false){
14835                 this.fireEvent("click", this, index, item, e);
14836             }
14837         }else{
14838             this.clearSelections();
14839         }
14840     },
14841
14842     /** @ignore */
14843     onContextMenu : function(e){
14844         var item = this.findItemFromChild(e.getTarget());
14845         if(item){
14846             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14847         }
14848     },
14849
14850     /** @ignore */
14851     onDblClick : function(e){
14852         var item = this.findItemFromChild(e.getTarget());
14853         if(item){
14854             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14855         }
14856     },
14857
14858     onItemClick : function(item, index, e)
14859     {
14860         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14861             return false;
14862         }
14863         if (this.toggleSelect) {
14864             var m = this.isSelected(item) ? 'unselect' : 'select';
14865             //Roo.log(m);
14866             var _t = this;
14867             _t[m](item, true, false);
14868             return true;
14869         }
14870         if(this.multiSelect || this.singleSelect){
14871             if(this.multiSelect && e.shiftKey && this.lastSelection){
14872                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14873             }else{
14874                 this.select(item, this.multiSelect && e.ctrlKey);
14875                 this.lastSelection = item;
14876             }
14877             
14878             if(!this.tickable){
14879                 e.preventDefault();
14880             }
14881             
14882         }
14883         return true;
14884     },
14885
14886     /**
14887      * Get the number of selected nodes.
14888      * @return {Number}
14889      */
14890     getSelectionCount : function(){
14891         return this.selections.length;
14892     },
14893
14894     /**
14895      * Get the currently selected nodes.
14896      * @return {Array} An array of HTMLElements
14897      */
14898     getSelectedNodes : function(){
14899         return this.selections;
14900     },
14901
14902     /**
14903      * Get the indexes of the selected nodes.
14904      * @return {Array}
14905      */
14906     getSelectedIndexes : function(){
14907         var indexes = [], s = this.selections;
14908         for(var i = 0, len = s.length; i < len; i++){
14909             indexes.push(s[i].nodeIndex);
14910         }
14911         return indexes;
14912     },
14913
14914     /**
14915      * Clear all selections
14916      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14917      */
14918     clearSelections : function(suppressEvent){
14919         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14920             this.cmp.elements = this.selections;
14921             this.cmp.removeClass(this.selectedClass);
14922             this.selections = [];
14923             if(!suppressEvent){
14924                 this.fireEvent("selectionchange", this, this.selections);
14925             }
14926         }
14927     },
14928
14929     /**
14930      * Returns true if the passed node is selected
14931      * @param {HTMLElement/Number} node The node or node index
14932      * @return {Boolean}
14933      */
14934     isSelected : function(node){
14935         var s = this.selections;
14936         if(s.length < 1){
14937             return false;
14938         }
14939         node = this.getNode(node);
14940         return s.indexOf(node) !== -1;
14941     },
14942
14943     /**
14944      * Selects nodes.
14945      * @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
14946      * @param {Boolean} keepExisting (optional) true to keep existing selections
14947      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14948      */
14949     select : function(nodeInfo, keepExisting, suppressEvent){
14950         if(nodeInfo instanceof Array){
14951             if(!keepExisting){
14952                 this.clearSelections(true);
14953             }
14954             for(var i = 0, len = nodeInfo.length; i < len; i++){
14955                 this.select(nodeInfo[i], true, true);
14956             }
14957             return;
14958         } 
14959         var node = this.getNode(nodeInfo);
14960         if(!node || this.isSelected(node)){
14961             return; // already selected.
14962         }
14963         if(!keepExisting){
14964             this.clearSelections(true);
14965         }
14966         
14967         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14968             Roo.fly(node).addClass(this.selectedClass);
14969             this.selections.push(node);
14970             if(!suppressEvent){
14971                 this.fireEvent("selectionchange", this, this.selections);
14972             }
14973         }
14974         
14975         
14976     },
14977       /**
14978      * Unselects nodes.
14979      * @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
14980      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14981      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14982      */
14983     unselect : function(nodeInfo, keepExisting, suppressEvent)
14984     {
14985         if(nodeInfo instanceof Array){
14986             Roo.each(this.selections, function(s) {
14987                 this.unselect(s, nodeInfo);
14988             }, this);
14989             return;
14990         }
14991         var node = this.getNode(nodeInfo);
14992         if(!node || !this.isSelected(node)){
14993             //Roo.log("not selected");
14994             return; // not selected.
14995         }
14996         // fireevent???
14997         var ns = [];
14998         Roo.each(this.selections, function(s) {
14999             if (s == node ) {
15000                 Roo.fly(node).removeClass(this.selectedClass);
15001
15002                 return;
15003             }
15004             ns.push(s);
15005         },this);
15006         
15007         this.selections= ns;
15008         this.fireEvent("selectionchange", this, this.selections);
15009     },
15010
15011     /**
15012      * Gets a template node.
15013      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15014      * @return {HTMLElement} The node or null if it wasn't found
15015      */
15016     getNode : function(nodeInfo){
15017         if(typeof nodeInfo == "string"){
15018             return document.getElementById(nodeInfo);
15019         }else if(typeof nodeInfo == "number"){
15020             return this.nodes[nodeInfo];
15021         }
15022         return nodeInfo;
15023     },
15024
15025     /**
15026      * Gets a range template nodes.
15027      * @param {Number} startIndex
15028      * @param {Number} endIndex
15029      * @return {Array} An array of nodes
15030      */
15031     getNodes : function(start, end){
15032         var ns = this.nodes;
15033         start = start || 0;
15034         end = typeof end == "undefined" ? ns.length - 1 : end;
15035         var nodes = [];
15036         if(start <= end){
15037             for(var i = start; i <= end; i++){
15038                 nodes.push(ns[i]);
15039             }
15040         } else{
15041             for(var i = start; i >= end; i--){
15042                 nodes.push(ns[i]);
15043             }
15044         }
15045         return nodes;
15046     },
15047
15048     /**
15049      * Finds the index of the passed node
15050      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15051      * @return {Number} The index of the node or -1
15052      */
15053     indexOf : function(node){
15054         node = this.getNode(node);
15055         if(typeof node.nodeIndex == "number"){
15056             return node.nodeIndex;
15057         }
15058         var ns = this.nodes;
15059         for(var i = 0, len = ns.length; i < len; i++){
15060             if(ns[i] == node){
15061                 return i;
15062             }
15063         }
15064         return -1;
15065     }
15066 });
15067 /*
15068  * - LGPL
15069  *
15070  * based on jquery fullcalendar
15071  * 
15072  */
15073
15074 Roo.bootstrap = Roo.bootstrap || {};
15075 /**
15076  * @class Roo.bootstrap.Calendar
15077  * @extends Roo.bootstrap.Component
15078  * Bootstrap Calendar class
15079  * @cfg {Boolean} loadMask (true|false) default false
15080  * @cfg {Object} header generate the user specific header of the calendar, default false
15081
15082  * @constructor
15083  * Create a new Container
15084  * @param {Object} config The config object
15085  */
15086
15087
15088
15089 Roo.bootstrap.Calendar = function(config){
15090     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15091      this.addEvents({
15092         /**
15093              * @event select
15094              * Fires when a date is selected
15095              * @param {DatePicker} this
15096              * @param {Date} date The selected date
15097              */
15098         'select': true,
15099         /**
15100              * @event monthchange
15101              * Fires when the displayed month changes 
15102              * @param {DatePicker} this
15103              * @param {Date} date The selected month
15104              */
15105         'monthchange': true,
15106         /**
15107              * @event evententer
15108              * Fires when mouse over an event
15109              * @param {Calendar} this
15110              * @param {event} Event
15111              */
15112         'evententer': true,
15113         /**
15114              * @event eventleave
15115              * Fires when the mouse leaves an
15116              * @param {Calendar} this
15117              * @param {event}
15118              */
15119         'eventleave': true,
15120         /**
15121              * @event eventclick
15122              * Fires when the mouse click an
15123              * @param {Calendar} this
15124              * @param {event}
15125              */
15126         'eventclick': true
15127         
15128     });
15129
15130 };
15131
15132 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15133     
15134      /**
15135      * @cfg {Number} startDay
15136      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15137      */
15138     startDay : 0,
15139     
15140     loadMask : false,
15141     
15142     header : false,
15143       
15144     getAutoCreate : function(){
15145         
15146         
15147         var fc_button = function(name, corner, style, content ) {
15148             return Roo.apply({},{
15149                 tag : 'span',
15150                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15151                          (corner.length ?
15152                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15153                             ''
15154                         ),
15155                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15156                 unselectable: 'on'
15157             });
15158         };
15159         
15160         var header = {};
15161         
15162         if(!this.header){
15163             header = {
15164                 tag : 'table',
15165                 cls : 'fc-header',
15166                 style : 'width:100%',
15167                 cn : [
15168                     {
15169                         tag: 'tr',
15170                         cn : [
15171                             {
15172                                 tag : 'td',
15173                                 cls : 'fc-header-left',
15174                                 cn : [
15175                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15176                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15177                                     { tag: 'span', cls: 'fc-header-space' },
15178                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15179
15180
15181                                 ]
15182                             },
15183
15184                             {
15185                                 tag : 'td',
15186                                 cls : 'fc-header-center',
15187                                 cn : [
15188                                     {
15189                                         tag: 'span',
15190                                         cls: 'fc-header-title',
15191                                         cn : {
15192                                             tag: 'H2',
15193                                             html : 'month / year'
15194                                         }
15195                                     }
15196
15197                                 ]
15198                             },
15199                             {
15200                                 tag : 'td',
15201                                 cls : 'fc-header-right',
15202                                 cn : [
15203                               /*      fc_button('month', 'left', '', 'month' ),
15204                                     fc_button('week', '', '', 'week' ),
15205                                     fc_button('day', 'right', '', 'day' )
15206                                 */    
15207
15208                                 ]
15209                             }
15210
15211                         ]
15212                     }
15213                 ]
15214             };
15215         }
15216         
15217         header = this.header;
15218         
15219        
15220         var cal_heads = function() {
15221             var ret = [];
15222             // fixme - handle this.
15223             
15224             for (var i =0; i < Date.dayNames.length; i++) {
15225                 var d = Date.dayNames[i];
15226                 ret.push({
15227                     tag: 'th',
15228                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15229                     html : d.substring(0,3)
15230                 });
15231                 
15232             }
15233             ret[0].cls += ' fc-first';
15234             ret[6].cls += ' fc-last';
15235             return ret;
15236         };
15237         var cal_cell = function(n) {
15238             return  {
15239                 tag: 'td',
15240                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15241                 cn : [
15242                     {
15243                         cn : [
15244                             {
15245                                 cls: 'fc-day-number',
15246                                 html: 'D'
15247                             },
15248                             {
15249                                 cls: 'fc-day-content',
15250                              
15251                                 cn : [
15252                                      {
15253                                         style: 'position: relative;' // height: 17px;
15254                                     }
15255                                 ]
15256                             }
15257                             
15258                             
15259                         ]
15260                     }
15261                 ]
15262                 
15263             }
15264         };
15265         var cal_rows = function() {
15266             
15267             var ret = [];
15268             for (var r = 0; r < 6; r++) {
15269                 var row= {
15270                     tag : 'tr',
15271                     cls : 'fc-week',
15272                     cn : []
15273                 };
15274                 
15275                 for (var i =0; i < Date.dayNames.length; i++) {
15276                     var d = Date.dayNames[i];
15277                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15278
15279                 }
15280                 row.cn[0].cls+=' fc-first';
15281                 row.cn[0].cn[0].style = 'min-height:90px';
15282                 row.cn[6].cls+=' fc-last';
15283                 ret.push(row);
15284                 
15285             }
15286             ret[0].cls += ' fc-first';
15287             ret[4].cls += ' fc-prev-last';
15288             ret[5].cls += ' fc-last';
15289             return ret;
15290             
15291         };
15292         
15293         var cal_table = {
15294             tag: 'table',
15295             cls: 'fc-border-separate',
15296             style : 'width:100%',
15297             cellspacing  : 0,
15298             cn : [
15299                 { 
15300                     tag: 'thead',
15301                     cn : [
15302                         { 
15303                             tag: 'tr',
15304                             cls : 'fc-first fc-last',
15305                             cn : cal_heads()
15306                         }
15307                     ]
15308                 },
15309                 { 
15310                     tag: 'tbody',
15311                     cn : cal_rows()
15312                 }
15313                   
15314             ]
15315         };
15316          
15317          var cfg = {
15318             cls : 'fc fc-ltr',
15319             cn : [
15320                 header,
15321                 {
15322                     cls : 'fc-content',
15323                     style : "position: relative;",
15324                     cn : [
15325                         {
15326                             cls : 'fc-view fc-view-month fc-grid',
15327                             style : 'position: relative',
15328                             unselectable : 'on',
15329                             cn : [
15330                                 {
15331                                     cls : 'fc-event-container',
15332                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15333                                 },
15334                                 cal_table
15335                             ]
15336                         }
15337                     ]
15338     
15339                 }
15340            ] 
15341             
15342         };
15343         
15344          
15345         
15346         return cfg;
15347     },
15348     
15349     
15350     initEvents : function()
15351     {
15352         if(!this.store){
15353             throw "can not find store for calendar";
15354         }
15355         
15356         var mark = {
15357             tag: "div",
15358             cls:"x-dlg-mask",
15359             style: "text-align:center",
15360             cn: [
15361                 {
15362                     tag: "div",
15363                     style: "background-color:white;width:50%;margin:250 auto",
15364                     cn: [
15365                         {
15366                             tag: "img",
15367                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15368                         },
15369                         {
15370                             tag: "span",
15371                             html: "Loading"
15372                         }
15373                         
15374                     ]
15375                 }
15376             ]
15377         };
15378         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15379         
15380         var size = this.el.select('.fc-content', true).first().getSize();
15381         this.maskEl.setSize(size.width, size.height);
15382         this.maskEl.enableDisplayMode("block");
15383         if(!this.loadMask){
15384             this.maskEl.hide();
15385         }
15386         
15387         this.store = Roo.factory(this.store, Roo.data);
15388         this.store.on('load', this.onLoad, this);
15389         this.store.on('beforeload', this.onBeforeLoad, this);
15390         
15391         this.resize();
15392         
15393         this.cells = this.el.select('.fc-day',true);
15394         //Roo.log(this.cells);
15395         this.textNodes = this.el.query('.fc-day-number');
15396         this.cells.addClassOnOver('fc-state-hover');
15397         
15398         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15399         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15400         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15401         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15402         
15403         this.on('monthchange', this.onMonthChange, this);
15404         
15405         this.update(new Date().clearTime());
15406     },
15407     
15408     resize : function() {
15409         var sz  = this.el.getSize();
15410         
15411         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15412         this.el.select('.fc-day-content div',true).setHeight(34);
15413     },
15414     
15415     
15416     // private
15417     showPrevMonth : function(e){
15418         this.update(this.activeDate.add("mo", -1));
15419     },
15420     showToday : function(e){
15421         this.update(new Date().clearTime());
15422     },
15423     // private
15424     showNextMonth : function(e){
15425         this.update(this.activeDate.add("mo", 1));
15426     },
15427
15428     // private
15429     showPrevYear : function(){
15430         this.update(this.activeDate.add("y", -1));
15431     },
15432
15433     // private
15434     showNextYear : function(){
15435         this.update(this.activeDate.add("y", 1));
15436     },
15437
15438     
15439    // private
15440     update : function(date)
15441     {
15442         var vd = this.activeDate;
15443         this.activeDate = date;
15444 //        if(vd && this.el){
15445 //            var t = date.getTime();
15446 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15447 //                Roo.log('using add remove');
15448 //                
15449 //                this.fireEvent('monthchange', this, date);
15450 //                
15451 //                this.cells.removeClass("fc-state-highlight");
15452 //                this.cells.each(function(c){
15453 //                   if(c.dateValue == t){
15454 //                       c.addClass("fc-state-highlight");
15455 //                       setTimeout(function(){
15456 //                            try{c.dom.firstChild.focus();}catch(e){}
15457 //                       }, 50);
15458 //                       return false;
15459 //                   }
15460 //                   return true;
15461 //                });
15462 //                return;
15463 //            }
15464 //        }
15465         
15466         var days = date.getDaysInMonth();
15467         
15468         var firstOfMonth = date.getFirstDateOfMonth();
15469         var startingPos = firstOfMonth.getDay()-this.startDay;
15470         
15471         if(startingPos < this.startDay){
15472             startingPos += 7;
15473         }
15474         
15475         var pm = date.add(Date.MONTH, -1);
15476         var prevStart = pm.getDaysInMonth()-startingPos;
15477 //        
15478         this.cells = this.el.select('.fc-day',true);
15479         this.textNodes = this.el.query('.fc-day-number');
15480         this.cells.addClassOnOver('fc-state-hover');
15481         
15482         var cells = this.cells.elements;
15483         var textEls = this.textNodes;
15484         
15485         Roo.each(cells, function(cell){
15486             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15487         });
15488         
15489         days += startingPos;
15490
15491         // convert everything to numbers so it's fast
15492         var day = 86400000;
15493         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15494         //Roo.log(d);
15495         //Roo.log(pm);
15496         //Roo.log(prevStart);
15497         
15498         var today = new Date().clearTime().getTime();
15499         var sel = date.clearTime().getTime();
15500         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15501         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15502         var ddMatch = this.disabledDatesRE;
15503         var ddText = this.disabledDatesText;
15504         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15505         var ddaysText = this.disabledDaysText;
15506         var format = this.format;
15507         
15508         var setCellClass = function(cal, cell){
15509             cell.row = 0;
15510             cell.events = [];
15511             cell.more = [];
15512             //Roo.log('set Cell Class');
15513             cell.title = "";
15514             var t = d.getTime();
15515             
15516             //Roo.log(d);
15517             
15518             cell.dateValue = t;
15519             if(t == today){
15520                 cell.className += " fc-today";
15521                 cell.className += " fc-state-highlight";
15522                 cell.title = cal.todayText;
15523             }
15524             if(t == sel){
15525                 // disable highlight in other month..
15526                 //cell.className += " fc-state-highlight";
15527                 
15528             }
15529             // disabling
15530             if(t < min) {
15531                 cell.className = " fc-state-disabled";
15532                 cell.title = cal.minText;
15533                 return;
15534             }
15535             if(t > max) {
15536                 cell.className = " fc-state-disabled";
15537                 cell.title = cal.maxText;
15538                 return;
15539             }
15540             if(ddays){
15541                 if(ddays.indexOf(d.getDay()) != -1){
15542                     cell.title = ddaysText;
15543                     cell.className = " fc-state-disabled";
15544                 }
15545             }
15546             if(ddMatch && format){
15547                 var fvalue = d.dateFormat(format);
15548                 if(ddMatch.test(fvalue)){
15549                     cell.title = ddText.replace("%0", fvalue);
15550                     cell.className = " fc-state-disabled";
15551                 }
15552             }
15553             
15554             if (!cell.initialClassName) {
15555                 cell.initialClassName = cell.dom.className;
15556             }
15557             
15558             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15559         };
15560
15561         var i = 0;
15562         
15563         for(; i < startingPos; i++) {
15564             textEls[i].innerHTML = (++prevStart);
15565             d.setDate(d.getDate()+1);
15566             
15567             cells[i].className = "fc-past fc-other-month";
15568             setCellClass(this, cells[i]);
15569         }
15570         
15571         var intDay = 0;
15572         
15573         for(; i < days; i++){
15574             intDay = i - startingPos + 1;
15575             textEls[i].innerHTML = (intDay);
15576             d.setDate(d.getDate()+1);
15577             
15578             cells[i].className = ''; // "x-date-active";
15579             setCellClass(this, cells[i]);
15580         }
15581         var extraDays = 0;
15582         
15583         for(; i < 42; i++) {
15584             textEls[i].innerHTML = (++extraDays);
15585             d.setDate(d.getDate()+1);
15586             
15587             cells[i].className = "fc-future fc-other-month";
15588             setCellClass(this, cells[i]);
15589         }
15590         
15591         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15592         
15593         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15594         
15595         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15596         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15597         
15598         if(totalRows != 6){
15599             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15600             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15601         }
15602         
15603         this.fireEvent('monthchange', this, date);
15604         
15605         
15606         /*
15607         if(!this.internalRender){
15608             var main = this.el.dom.firstChild;
15609             var w = main.offsetWidth;
15610             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15611             Roo.fly(main).setWidth(w);
15612             this.internalRender = true;
15613             // opera does not respect the auto grow header center column
15614             // then, after it gets a width opera refuses to recalculate
15615             // without a second pass
15616             if(Roo.isOpera && !this.secondPass){
15617                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15618                 this.secondPass = true;
15619                 this.update.defer(10, this, [date]);
15620             }
15621         }
15622         */
15623         
15624     },
15625     
15626     findCell : function(dt) {
15627         dt = dt.clearTime().getTime();
15628         var ret = false;
15629         this.cells.each(function(c){
15630             //Roo.log("check " +c.dateValue + '?=' + dt);
15631             if(c.dateValue == dt){
15632                 ret = c;
15633                 return false;
15634             }
15635             return true;
15636         });
15637         
15638         return ret;
15639     },
15640     
15641     findCells : function(ev) {
15642         var s = ev.start.clone().clearTime().getTime();
15643        // Roo.log(s);
15644         var e= ev.end.clone().clearTime().getTime();
15645        // Roo.log(e);
15646         var ret = [];
15647         this.cells.each(function(c){
15648              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15649             
15650             if(c.dateValue > e){
15651                 return ;
15652             }
15653             if(c.dateValue < s){
15654                 return ;
15655             }
15656             ret.push(c);
15657         });
15658         
15659         return ret;    
15660     },
15661     
15662 //    findBestRow: function(cells)
15663 //    {
15664 //        var ret = 0;
15665 //        
15666 //        for (var i =0 ; i < cells.length;i++) {
15667 //            ret  = Math.max(cells[i].rows || 0,ret);
15668 //        }
15669 //        return ret;
15670 //        
15671 //    },
15672     
15673     
15674     addItem : function(ev)
15675     {
15676         // look for vertical location slot in
15677         var cells = this.findCells(ev);
15678         
15679 //        ev.row = this.findBestRow(cells);
15680         
15681         // work out the location.
15682         
15683         var crow = false;
15684         var rows = [];
15685         for(var i =0; i < cells.length; i++) {
15686             
15687             cells[i].row = cells[0].row;
15688             
15689             if(i == 0){
15690                 cells[i].row = cells[i].row + 1;
15691             }
15692             
15693             if (!crow) {
15694                 crow = {
15695                     start : cells[i],
15696                     end :  cells[i]
15697                 };
15698                 continue;
15699             }
15700             if (crow.start.getY() == cells[i].getY()) {
15701                 // on same row.
15702                 crow.end = cells[i];
15703                 continue;
15704             }
15705             // different row.
15706             rows.push(crow);
15707             crow = {
15708                 start: cells[i],
15709                 end : cells[i]
15710             };
15711             
15712         }
15713         
15714         rows.push(crow);
15715         ev.els = [];
15716         ev.rows = rows;
15717         ev.cells = cells;
15718         
15719         cells[0].events.push(ev);
15720         
15721         this.calevents.push(ev);
15722     },
15723     
15724     clearEvents: function() {
15725         
15726         if(!this.calevents){
15727             return;
15728         }
15729         
15730         Roo.each(this.cells.elements, function(c){
15731             c.row = 0;
15732             c.events = [];
15733             c.more = [];
15734         });
15735         
15736         Roo.each(this.calevents, function(e) {
15737             Roo.each(e.els, function(el) {
15738                 el.un('mouseenter' ,this.onEventEnter, this);
15739                 el.un('mouseleave' ,this.onEventLeave, this);
15740                 el.remove();
15741             },this);
15742         },this);
15743         
15744         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15745             e.remove();
15746         });
15747         
15748     },
15749     
15750     renderEvents: function()
15751     {   
15752         var _this = this;
15753         
15754         this.cells.each(function(c) {
15755             
15756             if(c.row < 5){
15757                 return;
15758             }
15759             
15760             var ev = c.events;
15761             
15762             var r = 4;
15763             if(c.row != c.events.length){
15764                 r = 4 - (4 - (c.row - c.events.length));
15765             }
15766             
15767             c.events = ev.slice(0, r);
15768             c.more = ev.slice(r);
15769             
15770             if(c.more.length && c.more.length == 1){
15771                 c.events.push(c.more.pop());
15772             }
15773             
15774             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15775             
15776         });
15777             
15778         this.cells.each(function(c) {
15779             
15780             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15781             
15782             
15783             for (var e = 0; e < c.events.length; e++){
15784                 var ev = c.events[e];
15785                 var rows = ev.rows;
15786                 
15787                 for(var i = 0; i < rows.length; i++) {
15788                 
15789                     // how many rows should it span..
15790
15791                     var  cfg = {
15792                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15793                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15794
15795                         unselectable : "on",
15796                         cn : [
15797                             {
15798                                 cls: 'fc-event-inner',
15799                                 cn : [
15800     //                                {
15801     //                                  tag:'span',
15802     //                                  cls: 'fc-event-time',
15803     //                                  html : cells.length > 1 ? '' : ev.time
15804     //                                },
15805                                     {
15806                                       tag:'span',
15807                                       cls: 'fc-event-title',
15808                                       html : String.format('{0}', ev.title)
15809                                     }
15810
15811
15812                                 ]
15813                             },
15814                             {
15815                                 cls: 'ui-resizable-handle ui-resizable-e',
15816                                 html : '&nbsp;&nbsp;&nbsp'
15817                             }
15818
15819                         ]
15820                     };
15821
15822                     if (i == 0) {
15823                         cfg.cls += ' fc-event-start';
15824                     }
15825                     if ((i+1) == rows.length) {
15826                         cfg.cls += ' fc-event-end';
15827                     }
15828
15829                     var ctr = _this.el.select('.fc-event-container',true).first();
15830                     var cg = ctr.createChild(cfg);
15831
15832                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15833                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15834
15835                     var r = (c.more.length) ? 1 : 0;
15836                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15837                     cg.setWidth(ebox.right - sbox.x -2);
15838
15839                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15840                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15841                     cg.on('click', _this.onEventClick, _this, ev);
15842
15843                     ev.els.push(cg);
15844                     
15845                 }
15846                 
15847             }
15848             
15849             
15850             if(c.more.length){
15851                 var  cfg = {
15852                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15853                     style : 'position: absolute',
15854                     unselectable : "on",
15855                     cn : [
15856                         {
15857                             cls: 'fc-event-inner',
15858                             cn : [
15859                                 {
15860                                   tag:'span',
15861                                   cls: 'fc-event-title',
15862                                   html : 'More'
15863                                 }
15864
15865
15866                             ]
15867                         },
15868                         {
15869                             cls: 'ui-resizable-handle ui-resizable-e',
15870                             html : '&nbsp;&nbsp;&nbsp'
15871                         }
15872
15873                     ]
15874                 };
15875
15876                 var ctr = _this.el.select('.fc-event-container',true).first();
15877                 var cg = ctr.createChild(cfg);
15878
15879                 var sbox = c.select('.fc-day-content',true).first().getBox();
15880                 var ebox = c.select('.fc-day-content',true).first().getBox();
15881                 //Roo.log(cg);
15882                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15883                 cg.setWidth(ebox.right - sbox.x -2);
15884
15885                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15886                 
15887             }
15888             
15889         });
15890         
15891         
15892         
15893     },
15894     
15895     onEventEnter: function (e, el,event,d) {
15896         this.fireEvent('evententer', this, el, event);
15897     },
15898     
15899     onEventLeave: function (e, el,event,d) {
15900         this.fireEvent('eventleave', this, el, event);
15901     },
15902     
15903     onEventClick: function (e, el,event,d) {
15904         this.fireEvent('eventclick', this, el, event);
15905     },
15906     
15907     onMonthChange: function () {
15908         this.store.load();
15909     },
15910     
15911     onMoreEventClick: function(e, el, more)
15912     {
15913         var _this = this;
15914         
15915         this.calpopover.placement = 'right';
15916         this.calpopover.setTitle('More');
15917         
15918         this.calpopover.setContent('');
15919         
15920         var ctr = this.calpopover.el.select('.popover-content', true).first();
15921         
15922         Roo.each(more, function(m){
15923             var cfg = {
15924                 cls : 'fc-event-hori fc-event-draggable',
15925                 html : m.title
15926             };
15927             var cg = ctr.createChild(cfg);
15928             
15929             cg.on('click', _this.onEventClick, _this, m);
15930         });
15931         
15932         this.calpopover.show(el);
15933         
15934         
15935     },
15936     
15937     onLoad: function () 
15938     {   
15939         this.calevents = [];
15940         var cal = this;
15941         
15942         if(this.store.getCount() > 0){
15943             this.store.data.each(function(d){
15944                cal.addItem({
15945                     id : d.data.id,
15946                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15947                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15948                     time : d.data.start_time,
15949                     title : d.data.title,
15950                     description : d.data.description,
15951                     venue : d.data.venue
15952                 });
15953             });
15954         }
15955         
15956         this.renderEvents();
15957         
15958         if(this.calevents.length && this.loadMask){
15959             this.maskEl.hide();
15960         }
15961     },
15962     
15963     onBeforeLoad: function()
15964     {
15965         this.clearEvents();
15966         if(this.loadMask){
15967             this.maskEl.show();
15968         }
15969     }
15970 });
15971
15972  
15973  /*
15974  * - LGPL
15975  *
15976  * element
15977  * 
15978  */
15979
15980 /**
15981  * @class Roo.bootstrap.Popover
15982  * @extends Roo.bootstrap.Component
15983  * Bootstrap Popover class
15984  * @cfg {String} html contents of the popover   (or false to use children..)
15985  * @cfg {String} title of popover (or false to hide)
15986  * @cfg {String} placement how it is placed
15987  * @cfg {String} trigger click || hover (or false to trigger manually)
15988  * @cfg {String} over what (parent or false to trigger manually.)
15989  * @cfg {Number} delay - delay before showing
15990  
15991  * @constructor
15992  * Create a new Popover
15993  * @param {Object} config The config object
15994  */
15995
15996 Roo.bootstrap.Popover = function(config){
15997     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15998     
15999     this.addEvents({
16000         // raw events
16001          /**
16002          * @event show
16003          * After the popover show
16004          * 
16005          * @param {Roo.bootstrap.Popover} this
16006          */
16007         "show" : true,
16008         /**
16009          * @event hide
16010          * After the popover hide
16011          * 
16012          * @param {Roo.bootstrap.Popover} this
16013          */
16014         "hide" : true
16015     });
16016 };
16017
16018 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16019     
16020     title: 'Fill in a title',
16021     html: false,
16022     
16023     placement : 'right',
16024     trigger : 'hover', // hover
16025     
16026     delay : 0,
16027     
16028     over: 'parent',
16029     
16030     can_build_overlaid : false,
16031     
16032     getChildContainer : function()
16033     {
16034         return this.el.select('.popover-content',true).first();
16035     },
16036     
16037     getAutoCreate : function(){
16038          
16039         var cfg = {
16040            cls : 'popover roo-dynamic',
16041            style: 'display:block',
16042            cn : [
16043                 {
16044                     cls : 'arrow'
16045                 },
16046                 {
16047                     cls : 'popover-inner',
16048                     cn : [
16049                         {
16050                             tag: 'h3',
16051                             cls: 'popover-title',
16052                             html : this.title
16053                         },
16054                         {
16055                             cls : 'popover-content',
16056                             html : this.html
16057                         }
16058                     ]
16059                     
16060                 }
16061            ]
16062         };
16063         
16064         return cfg;
16065     },
16066     setTitle: function(str)
16067     {
16068         this.title = str;
16069         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16070     },
16071     setContent: function(str)
16072     {
16073         this.html = str;
16074         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16075     },
16076     // as it get's added to the bottom of the page.
16077     onRender : function(ct, position)
16078     {
16079         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16080         if(!this.el){
16081             var cfg = Roo.apply({},  this.getAutoCreate());
16082             cfg.id = Roo.id();
16083             
16084             if (this.cls) {
16085                 cfg.cls += ' ' + this.cls;
16086             }
16087             if (this.style) {
16088                 cfg.style = this.style;
16089             }
16090             //Roo.log("adding to ");
16091             this.el = Roo.get(document.body).createChild(cfg, position);
16092 //            Roo.log(this.el);
16093         }
16094         this.initEvents();
16095     },
16096     
16097     initEvents : function()
16098     {
16099         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16100         this.el.enableDisplayMode('block');
16101         this.el.hide();
16102         if (this.over === false) {
16103             return; 
16104         }
16105         if (this.triggers === false) {
16106             return;
16107         }
16108         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16109         var triggers = this.trigger ? this.trigger.split(' ') : [];
16110         Roo.each(triggers, function(trigger) {
16111         
16112             if (trigger == 'click') {
16113                 on_el.on('click', this.toggle, this);
16114             } else if (trigger != 'manual') {
16115                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16116                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16117       
16118                 on_el.on(eventIn  ,this.enter, this);
16119                 on_el.on(eventOut, this.leave, this);
16120             }
16121         }, this);
16122         
16123     },
16124     
16125     
16126     // private
16127     timeout : null,
16128     hoverState : null,
16129     
16130     toggle : function () {
16131         this.hoverState == 'in' ? this.leave() : this.enter();
16132     },
16133     
16134     enter : function () {
16135         
16136         clearTimeout(this.timeout);
16137     
16138         this.hoverState = 'in';
16139     
16140         if (!this.delay || !this.delay.show) {
16141             this.show();
16142             return;
16143         }
16144         var _t = this;
16145         this.timeout = setTimeout(function () {
16146             if (_t.hoverState == 'in') {
16147                 _t.show();
16148             }
16149         }, this.delay.show)
16150     },
16151     
16152     leave : function() {
16153         clearTimeout(this.timeout);
16154     
16155         this.hoverState = 'out';
16156     
16157         if (!this.delay || !this.delay.hide) {
16158             this.hide();
16159             return;
16160         }
16161         var _t = this;
16162         this.timeout = setTimeout(function () {
16163             if (_t.hoverState == 'out') {
16164                 _t.hide();
16165             }
16166         }, this.delay.hide)
16167     },
16168     
16169     show : function (on_el)
16170     {
16171         if (!on_el) {
16172             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16173         }
16174         
16175         // set content.
16176         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16177         if (this.html !== false) {
16178             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16179         }
16180         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16181         if (!this.title.length) {
16182             this.el.select('.popover-title',true).hide();
16183         }
16184         
16185         var placement = typeof this.placement == 'function' ?
16186             this.placement.call(this, this.el, on_el) :
16187             this.placement;
16188             
16189         var autoToken = /\s?auto?\s?/i;
16190         var autoPlace = autoToken.test(placement);
16191         if (autoPlace) {
16192             placement = placement.replace(autoToken, '') || 'top';
16193         }
16194         
16195         //this.el.detach()
16196         //this.el.setXY([0,0]);
16197         this.el.show();
16198         this.el.dom.style.display='block';
16199         this.el.addClass(placement);
16200         
16201         //this.el.appendTo(on_el);
16202         
16203         var p = this.getPosition();
16204         var box = this.el.getBox();
16205         
16206         if (autoPlace) {
16207             // fixme..
16208         }
16209         var align = Roo.bootstrap.Popover.alignment[placement];
16210         this.el.alignTo(on_el, align[0],align[1]);
16211         //var arrow = this.el.select('.arrow',true).first();
16212         //arrow.set(align[2], 
16213         
16214         this.el.addClass('in');
16215         
16216         
16217         if (this.el.hasClass('fade')) {
16218             // fade it?
16219         }
16220         
16221         this.hoverState = 'in';
16222         
16223         this.fireEvent('show', this);
16224         
16225     },
16226     hide : function()
16227     {
16228         this.el.setXY([0,0]);
16229         this.el.removeClass('in');
16230         this.el.hide();
16231         this.hoverState = null;
16232         
16233         this.fireEvent('hide', this);
16234     }
16235     
16236 });
16237
16238 Roo.bootstrap.Popover.alignment = {
16239     'left' : ['r-l', [-10,0], 'right'],
16240     'right' : ['l-r', [10,0], 'left'],
16241     'bottom' : ['t-b', [0,10], 'top'],
16242     'top' : [ 'b-t', [0,-10], 'bottom']
16243 };
16244
16245  /*
16246  * - LGPL
16247  *
16248  * Progress
16249  * 
16250  */
16251
16252 /**
16253  * @class Roo.bootstrap.Progress
16254  * @extends Roo.bootstrap.Component
16255  * Bootstrap Progress class
16256  * @cfg {Boolean} striped striped of the progress bar
16257  * @cfg {Boolean} active animated of the progress bar
16258  * 
16259  * 
16260  * @constructor
16261  * Create a new Progress
16262  * @param {Object} config The config object
16263  */
16264
16265 Roo.bootstrap.Progress = function(config){
16266     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16267 };
16268
16269 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16270     
16271     striped : false,
16272     active: false,
16273     
16274     getAutoCreate : function(){
16275         var cfg = {
16276             tag: 'div',
16277             cls: 'progress'
16278         };
16279         
16280         
16281         if(this.striped){
16282             cfg.cls += ' progress-striped';
16283         }
16284       
16285         if(this.active){
16286             cfg.cls += ' active';
16287         }
16288         
16289         
16290         return cfg;
16291     }
16292    
16293 });
16294
16295  
16296
16297  /*
16298  * - LGPL
16299  *
16300  * ProgressBar
16301  * 
16302  */
16303
16304 /**
16305  * @class Roo.bootstrap.ProgressBar
16306  * @extends Roo.bootstrap.Component
16307  * Bootstrap ProgressBar class
16308  * @cfg {Number} aria_valuenow aria-value now
16309  * @cfg {Number} aria_valuemin aria-value min
16310  * @cfg {Number} aria_valuemax aria-value max
16311  * @cfg {String} label label for the progress bar
16312  * @cfg {String} panel (success | info | warning | danger )
16313  * @cfg {String} role role of the progress bar
16314  * @cfg {String} sr_only text
16315  * 
16316  * 
16317  * @constructor
16318  * Create a new ProgressBar
16319  * @param {Object} config The config object
16320  */
16321
16322 Roo.bootstrap.ProgressBar = function(config){
16323     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16324 };
16325
16326 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16327     
16328     aria_valuenow : 0,
16329     aria_valuemin : 0,
16330     aria_valuemax : 100,
16331     label : false,
16332     panel : false,
16333     role : false,
16334     sr_only: false,
16335     
16336     getAutoCreate : function()
16337     {
16338         
16339         var cfg = {
16340             tag: 'div',
16341             cls: 'progress-bar',
16342             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16343         };
16344         
16345         if(this.sr_only){
16346             cfg.cn = {
16347                 tag: 'span',
16348                 cls: 'sr-only',
16349                 html: this.sr_only
16350             }
16351         }
16352         
16353         if(this.role){
16354             cfg.role = this.role;
16355         }
16356         
16357         if(this.aria_valuenow){
16358             cfg['aria-valuenow'] = this.aria_valuenow;
16359         }
16360         
16361         if(this.aria_valuemin){
16362             cfg['aria-valuemin'] = this.aria_valuemin;
16363         }
16364         
16365         if(this.aria_valuemax){
16366             cfg['aria-valuemax'] = this.aria_valuemax;
16367         }
16368         
16369         if(this.label && !this.sr_only){
16370             cfg.html = this.label;
16371         }
16372         
16373         if(this.panel){
16374             cfg.cls += ' progress-bar-' + this.panel;
16375         }
16376         
16377         return cfg;
16378     },
16379     
16380     update : function(aria_valuenow)
16381     {
16382         this.aria_valuenow = aria_valuenow;
16383         
16384         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16385     }
16386    
16387 });
16388
16389  
16390
16391  /*
16392  * - LGPL
16393  *
16394  * column
16395  * 
16396  */
16397
16398 /**
16399  * @class Roo.bootstrap.TabGroup
16400  * @extends Roo.bootstrap.Column
16401  * Bootstrap Column class
16402  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16403  * @cfg {Boolean} carousel true to make the group behave like a carousel
16404  * @cfg {Boolean} bullets show bullets for the panels
16405  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16406  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16407  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16408  * 
16409  * @constructor
16410  * Create a new TabGroup
16411  * @param {Object} config The config object
16412  */
16413
16414 Roo.bootstrap.TabGroup = function(config){
16415     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16416     if (!this.navId) {
16417         this.navId = Roo.id();
16418     }
16419     this.tabs = [];
16420     Roo.bootstrap.TabGroup.register(this);
16421     
16422 };
16423
16424 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16425     
16426     carousel : false,
16427     transition : false,
16428     bullets : 0,
16429     timer : 0,
16430     autoslide : false,
16431     slideFn : false,
16432     slideOnTouch : false,
16433     
16434     getAutoCreate : function()
16435     {
16436         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16437         
16438         cfg.cls += ' tab-content';
16439         
16440         if (this.carousel) {
16441             cfg.cls += ' carousel slide';
16442             
16443             cfg.cn = [{
16444                cls : 'carousel-inner'
16445             }];
16446         
16447             if(this.bullets  && !Roo.isTouch){
16448                 
16449                 var bullets = {
16450                     cls : 'carousel-bullets',
16451                     cn : []
16452                 };
16453                
16454                 if(this.bullets_cls){
16455                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16456                 }
16457                  /*
16458                 for (var i = 0; i < this.bullets; i++){
16459                     bullets.cn.push({
16460                         cls : 'bullet bullet-' + i
16461                     });
16462                 }
16463                 */
16464                 bullets.cn.push({
16465                     cls : 'clear'
16466                 });
16467                 
16468                 cfg.cn[0].cn = bullets;
16469             }
16470         }
16471         
16472         return cfg;
16473     },
16474     
16475     initEvents:  function()
16476     {
16477         if(Roo.isTouch && this.slideOnTouch){
16478             this.el.on("touchstart", this.onTouchStart, this);
16479         }
16480         
16481         if(this.autoslide){
16482             var _this = this;
16483             
16484             this.slideFn = window.setInterval(function() {
16485                 _this.showPanelNext();
16486             }, this.timer);
16487         }
16488         
16489     },
16490     
16491     onTouchStart : function(e, el, o)
16492     {
16493         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16494             return;
16495         }
16496         
16497         this.showPanelNext();
16498     },
16499     
16500     getChildContainer : function()
16501     {
16502         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16503     },
16504     
16505     /**
16506     * register a Navigation item
16507     * @param {Roo.bootstrap.NavItem} the navitem to add
16508     */
16509     register : function(item)
16510     {
16511         this.tabs.push( item);
16512         item.navId = this.navId; // not really needed..
16513         this.addBullet();
16514     
16515     },
16516     
16517     getActivePanel : function()
16518     {
16519         var r = false;
16520         Roo.each(this.tabs, function(t) {
16521             if (t.active) {
16522                 r = t;
16523                 return false;
16524             }
16525             return null;
16526         });
16527         return r;
16528         
16529     },
16530     getPanelByName : function(n)
16531     {
16532         var r = false;
16533         Roo.each(this.tabs, function(t) {
16534             if (t.tabId == n) {
16535                 r = t;
16536                 return false;
16537             }
16538             return null;
16539         });
16540         return r;
16541     },
16542     indexOfPanel : function(p)
16543     {
16544         var r = false;
16545         Roo.each(this.tabs, function(t,i) {
16546             if (t.tabId == p.tabId) {
16547                 r = i;
16548                 return false;
16549             }
16550             return null;
16551         });
16552         return r;
16553     },
16554     /**
16555      * show a specific panel
16556      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16557      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16558      */
16559     showPanel : function (pan)
16560     {
16561         if(this.transition || typeof(pan) == 'undefined'){
16562             Roo.log("waiting for the transitionend");
16563             return;
16564         }
16565         
16566         if (typeof(pan) == 'number') {
16567             pan = this.tabs[pan];
16568         }
16569         
16570         if (typeof(pan) == 'string') {
16571             pan = this.getPanelByName(pan);
16572         }
16573         
16574         var cur = this.getActivePanel();
16575         
16576         if(!pan || !cur){
16577             Roo.log('pan or acitve pan is undefined');
16578             return false;
16579         }
16580         
16581         if (pan.tabId == this.getActivePanel().tabId) {
16582             return true;
16583         }
16584         
16585         if (false === cur.fireEvent('beforedeactivate')) {
16586             return false;
16587         }
16588         
16589         if(this.bullets > 0 && !Roo.isTouch){
16590             this.setActiveBullet(this.indexOfPanel(pan));
16591         }
16592         
16593         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16594             
16595             this.transition = true;
16596             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16597             var lr = dir == 'next' ? 'left' : 'right';
16598             pan.el.addClass(dir); // or prev
16599             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16600             cur.el.addClass(lr); // or right
16601             pan.el.addClass(lr);
16602             
16603             var _this = this;
16604             cur.el.on('transitionend', function() {
16605                 Roo.log("trans end?");
16606                 
16607                 pan.el.removeClass([lr,dir]);
16608                 pan.setActive(true);
16609                 
16610                 cur.el.removeClass([lr]);
16611                 cur.setActive(false);
16612                 
16613                 _this.transition = false;
16614                 
16615             }, this, { single:  true } );
16616             
16617             return true;
16618         }
16619         
16620         cur.setActive(false);
16621         pan.setActive(true);
16622         
16623         return true;
16624         
16625     },
16626     showPanelNext : function()
16627     {
16628         var i = this.indexOfPanel(this.getActivePanel());
16629         
16630         if (i >= this.tabs.length - 1 && !this.autoslide) {
16631             return;
16632         }
16633         
16634         if (i >= this.tabs.length - 1 && this.autoslide) {
16635             i = -1;
16636         }
16637         
16638         this.showPanel(this.tabs[i+1]);
16639     },
16640     
16641     showPanelPrev : function()
16642     {
16643         var i = this.indexOfPanel(this.getActivePanel());
16644         
16645         if (i  < 1 && !this.autoslide) {
16646             return;
16647         }
16648         
16649         if (i < 1 && this.autoslide) {
16650             i = this.tabs.length;
16651         }
16652         
16653         this.showPanel(this.tabs[i-1]);
16654     },
16655     
16656     
16657     addBullet: function()
16658     {
16659         if(!this.bullets || Roo.isTouch){
16660             return;
16661         }
16662         var ctr = this.el.select('.carousel-bullets',true).first();
16663         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16664         var bullet = ctr.createChild({
16665             cls : 'bullet bullet-' + i
16666         },ctr.dom.lastChild);
16667         
16668         
16669         var _this = this;
16670         
16671         bullet.on('click', (function(e, el, o, ii, t){
16672
16673             e.preventDefault();
16674
16675             this.showPanel(ii);
16676
16677             if(this.autoslide && this.slideFn){
16678                 clearInterval(this.slideFn);
16679                 this.slideFn = window.setInterval(function() {
16680                     _this.showPanelNext();
16681                 }, this.timer);
16682             }
16683
16684         }).createDelegate(this, [i, bullet], true));
16685                 
16686         
16687     },
16688      
16689     setActiveBullet : function(i)
16690     {
16691         if(Roo.isTouch){
16692             return;
16693         }
16694         
16695         Roo.each(this.el.select('.bullet', true).elements, function(el){
16696             el.removeClass('selected');
16697         });
16698
16699         var bullet = this.el.select('.bullet-' + i, true).first();
16700         
16701         if(!bullet){
16702             return;
16703         }
16704         
16705         bullet.addClass('selected');
16706     }
16707     
16708     
16709   
16710 });
16711
16712  
16713
16714  
16715  
16716 Roo.apply(Roo.bootstrap.TabGroup, {
16717     
16718     groups: {},
16719      /**
16720     * register a Navigation Group
16721     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16722     */
16723     register : function(navgrp)
16724     {
16725         this.groups[navgrp.navId] = navgrp;
16726         
16727     },
16728     /**
16729     * fetch a Navigation Group based on the navigation ID
16730     * if one does not exist , it will get created.
16731     * @param {string} the navgroup to add
16732     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16733     */
16734     get: function(navId) {
16735         if (typeof(this.groups[navId]) == 'undefined') {
16736             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16737         }
16738         return this.groups[navId] ;
16739     }
16740     
16741     
16742     
16743 });
16744
16745  /*
16746  * - LGPL
16747  *
16748  * TabPanel
16749  * 
16750  */
16751
16752 /**
16753  * @class Roo.bootstrap.TabPanel
16754  * @extends Roo.bootstrap.Component
16755  * Bootstrap TabPanel class
16756  * @cfg {Boolean} active panel active
16757  * @cfg {String} html panel content
16758  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16759  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16760  * 
16761  * 
16762  * @constructor
16763  * Create a new TabPanel
16764  * @param {Object} config The config object
16765  */
16766
16767 Roo.bootstrap.TabPanel = function(config){
16768     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16769     this.addEvents({
16770         /**
16771              * @event changed
16772              * Fires when the active status changes
16773              * @param {Roo.bootstrap.TabPanel} this
16774              * @param {Boolean} state the new state
16775             
16776          */
16777         'changed': true,
16778         /**
16779              * @event beforedeactivate
16780              * Fires before a tab is de-activated - can be used to do validation on a form.
16781              * @param {Roo.bootstrap.TabPanel} this
16782              * @return {Boolean} false if there is an error
16783             
16784          */
16785         'beforedeactivate': true
16786      });
16787     
16788     this.tabId = this.tabId || Roo.id();
16789   
16790 };
16791
16792 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16793     
16794     active: false,
16795     html: false,
16796     tabId: false,
16797     navId : false,
16798     
16799     getAutoCreate : function(){
16800         var cfg = {
16801             tag: 'div',
16802             // item is needed for carousel - not sure if it has any effect otherwise
16803             cls: 'tab-pane item',
16804             html: this.html || ''
16805         };
16806         
16807         if(this.active){
16808             cfg.cls += ' active';
16809         }
16810         
16811         if(this.tabId){
16812             cfg.tabId = this.tabId;
16813         }
16814         
16815         
16816         return cfg;
16817     },
16818     
16819     initEvents:  function()
16820     {
16821         var p = this.parent();
16822         this.navId = this.navId || p.navId;
16823         
16824         if (typeof(this.navId) != 'undefined') {
16825             // not really needed.. but just in case.. parent should be a NavGroup.
16826             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16827             
16828             tg.register(this);
16829             
16830             var i = tg.tabs.length - 1;
16831             
16832             if(this.active && tg.bullets > 0 && i < tg.bullets){
16833                 tg.setActiveBullet(i);
16834             }
16835         }
16836         
16837     },
16838     
16839     
16840     onRender : function(ct, position)
16841     {
16842        // Roo.log("Call onRender: " + this.xtype);
16843         
16844         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16845         
16846         
16847         
16848         
16849         
16850     },
16851     
16852     setActive: function(state)
16853     {
16854         Roo.log("panel - set active " + this.tabId + "=" + state);
16855         
16856         this.active = state;
16857         if (!state) {
16858             this.el.removeClass('active');
16859             
16860         } else  if (!this.el.hasClass('active')) {
16861             this.el.addClass('active');
16862         }
16863         
16864         this.fireEvent('changed', this, state);
16865     }
16866     
16867     
16868 });
16869  
16870
16871  
16872
16873  /*
16874  * - LGPL
16875  *
16876  * DateField
16877  * 
16878  */
16879
16880 /**
16881  * @class Roo.bootstrap.DateField
16882  * @extends Roo.bootstrap.Input
16883  * Bootstrap DateField class
16884  * @cfg {Number} weekStart default 0
16885  * @cfg {String} viewMode default empty, (months|years)
16886  * @cfg {String} minViewMode default empty, (months|years)
16887  * @cfg {Number} startDate default -Infinity
16888  * @cfg {Number} endDate default Infinity
16889  * @cfg {Boolean} todayHighlight default false
16890  * @cfg {Boolean} todayBtn default false
16891  * @cfg {Boolean} calendarWeeks default false
16892  * @cfg {Object} daysOfWeekDisabled default empty
16893  * @cfg {Boolean} singleMode default false (true | false)
16894  * 
16895  * @cfg {Boolean} keyboardNavigation default true
16896  * @cfg {String} language default en
16897  * 
16898  * @constructor
16899  * Create a new DateField
16900  * @param {Object} config The config object
16901  */
16902
16903 Roo.bootstrap.DateField = function(config){
16904     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16905      this.addEvents({
16906             /**
16907              * @event show
16908              * Fires when this field show.
16909              * @param {Roo.bootstrap.DateField} this
16910              * @param {Mixed} date The date value
16911              */
16912             show : true,
16913             /**
16914              * @event show
16915              * Fires when this field hide.
16916              * @param {Roo.bootstrap.DateField} this
16917              * @param {Mixed} date The date value
16918              */
16919             hide : true,
16920             /**
16921              * @event select
16922              * Fires when select a date.
16923              * @param {Roo.bootstrap.DateField} this
16924              * @param {Mixed} date The date value
16925              */
16926             select : true,
16927             /**
16928              * @event beforeselect
16929              * Fires when before select a date.
16930              * @param {Roo.bootstrap.DateField} this
16931              * @param {Mixed} date The date value
16932              */
16933             beforeselect : true
16934         });
16935 };
16936
16937 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16938     
16939     /**
16940      * @cfg {String} format
16941      * The default date format string which can be overriden for localization support.  The format must be
16942      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16943      */
16944     format : "m/d/y",
16945     /**
16946      * @cfg {String} altFormats
16947      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16948      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16949      */
16950     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16951     
16952     weekStart : 0,
16953     
16954     viewMode : '',
16955     
16956     minViewMode : '',
16957     
16958     todayHighlight : false,
16959     
16960     todayBtn: false,
16961     
16962     language: 'en',
16963     
16964     keyboardNavigation: true,
16965     
16966     calendarWeeks: false,
16967     
16968     startDate: -Infinity,
16969     
16970     endDate: Infinity,
16971     
16972     daysOfWeekDisabled: [],
16973     
16974     _events: [],
16975     
16976     singleMode : false,
16977     
16978     UTCDate: function()
16979     {
16980         return new Date(Date.UTC.apply(Date, arguments));
16981     },
16982     
16983     UTCToday: function()
16984     {
16985         var today = new Date();
16986         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16987     },
16988     
16989     getDate: function() {
16990             var d = this.getUTCDate();
16991             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16992     },
16993     
16994     getUTCDate: function() {
16995             return this.date;
16996     },
16997     
16998     setDate: function(d) {
16999             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17000     },
17001     
17002     setUTCDate: function(d) {
17003             this.date = d;
17004             this.setValue(this.formatDate(this.date));
17005     },
17006         
17007     onRender: function(ct, position)
17008     {
17009         
17010         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17011         
17012         this.language = this.language || 'en';
17013         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17014         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17015         
17016         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17017         this.format = this.format || 'm/d/y';
17018         this.isInline = false;
17019         this.isInput = true;
17020         this.component = this.el.select('.add-on', true).first() || false;
17021         this.component = (this.component && this.component.length === 0) ? false : this.component;
17022         this.hasInput = this.component && this.inputEL().length;
17023         
17024         if (typeof(this.minViewMode === 'string')) {
17025             switch (this.minViewMode) {
17026                 case 'months':
17027                     this.minViewMode = 1;
17028                     break;
17029                 case 'years':
17030                     this.minViewMode = 2;
17031                     break;
17032                 default:
17033                     this.minViewMode = 0;
17034                     break;
17035             }
17036         }
17037         
17038         if (typeof(this.viewMode === 'string')) {
17039             switch (this.viewMode) {
17040                 case 'months':
17041                     this.viewMode = 1;
17042                     break;
17043                 case 'years':
17044                     this.viewMode = 2;
17045                     break;
17046                 default:
17047                     this.viewMode = 0;
17048                     break;
17049             }
17050         }
17051                 
17052         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17053         
17054 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17055         
17056         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17057         
17058         this.picker().on('mousedown', this.onMousedown, this);
17059         this.picker().on('click', this.onClick, this);
17060         
17061         this.picker().addClass('datepicker-dropdown');
17062         
17063         this.startViewMode = this.viewMode;
17064         
17065         if(this.singleMode){
17066             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17067                 v.setVisibilityMode(Roo.Element.DISPLAY);
17068                 v.hide();
17069             });
17070             
17071             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17072                 v.setStyle('width', '189px');
17073             });
17074         }
17075         
17076         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17077             if(!this.calendarWeeks){
17078                 v.remove();
17079                 return;
17080             }
17081             
17082             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17083             v.attr('colspan', function(i, val){
17084                 return parseInt(val) + 1;
17085             });
17086         });
17087                         
17088         
17089         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17090         
17091         this.setStartDate(this.startDate);
17092         this.setEndDate(this.endDate);
17093         
17094         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17095         
17096         this.fillDow();
17097         this.fillMonths();
17098         this.update();
17099         this.showMode();
17100         
17101         if(this.isInline) {
17102             this.show();
17103         }
17104     },
17105     
17106     picker : function()
17107     {
17108         return this.pickerEl;
17109 //        return this.el.select('.datepicker', true).first();
17110     },
17111     
17112     fillDow: function()
17113     {
17114         var dowCnt = this.weekStart;
17115         
17116         var dow = {
17117             tag: 'tr',
17118             cn: [
17119                 
17120             ]
17121         };
17122         
17123         if(this.calendarWeeks){
17124             dow.cn.push({
17125                 tag: 'th',
17126                 cls: 'cw',
17127                 html: '&nbsp;'
17128             })
17129         }
17130         
17131         while (dowCnt < this.weekStart + 7) {
17132             dow.cn.push({
17133                 tag: 'th',
17134                 cls: 'dow',
17135                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17136             });
17137         }
17138         
17139         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17140     },
17141     
17142     fillMonths: function()
17143     {    
17144         var i = 0;
17145         var months = this.picker().select('>.datepicker-months td', true).first();
17146         
17147         months.dom.innerHTML = '';
17148         
17149         while (i < 12) {
17150             var month = {
17151                 tag: 'span',
17152                 cls: 'month',
17153                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17154             };
17155             
17156             months.createChild(month);
17157         }
17158         
17159     },
17160     
17161     update: function()
17162     {
17163         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;
17164         
17165         if (this.date < this.startDate) {
17166             this.viewDate = new Date(this.startDate);
17167         } else if (this.date > this.endDate) {
17168             this.viewDate = new Date(this.endDate);
17169         } else {
17170             this.viewDate = new Date(this.date);
17171         }
17172         
17173         this.fill();
17174     },
17175     
17176     fill: function() 
17177     {
17178         var d = new Date(this.viewDate),
17179                 year = d.getUTCFullYear(),
17180                 month = d.getUTCMonth(),
17181                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17182                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17183                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17184                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17185                 currentDate = this.date && this.date.valueOf(),
17186                 today = this.UTCToday();
17187         
17188         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17189         
17190 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17191         
17192 //        this.picker.select('>tfoot th.today').
17193 //                                              .text(dates[this.language].today)
17194 //                                              .toggle(this.todayBtn !== false);
17195     
17196         this.updateNavArrows();
17197         this.fillMonths();
17198                                                 
17199         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17200         
17201         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17202          
17203         prevMonth.setUTCDate(day);
17204         
17205         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17206         
17207         var nextMonth = new Date(prevMonth);
17208         
17209         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17210         
17211         nextMonth = nextMonth.valueOf();
17212         
17213         var fillMonths = false;
17214         
17215         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17216         
17217         while(prevMonth.valueOf() < nextMonth) {
17218             var clsName = '';
17219             
17220             if (prevMonth.getUTCDay() === this.weekStart) {
17221                 if(fillMonths){
17222                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17223                 }
17224                     
17225                 fillMonths = {
17226                     tag: 'tr',
17227                     cn: []
17228                 };
17229                 
17230                 if(this.calendarWeeks){
17231                     // ISO 8601: First week contains first thursday.
17232                     // ISO also states week starts on Monday, but we can be more abstract here.
17233                     var
17234                     // Start of current week: based on weekstart/current date
17235                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17236                     // Thursday of this week
17237                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17238                     // First Thursday of year, year from thursday
17239                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17240                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17241                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17242                     
17243                     fillMonths.cn.push({
17244                         tag: 'td',
17245                         cls: 'cw',
17246                         html: calWeek
17247                     });
17248                 }
17249             }
17250             
17251             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17252                 clsName += ' old';
17253             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17254                 clsName += ' new';
17255             }
17256             if (this.todayHighlight &&
17257                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17258                 prevMonth.getUTCMonth() == today.getMonth() &&
17259                 prevMonth.getUTCDate() == today.getDate()) {
17260                 clsName += ' today';
17261             }
17262             
17263             if (currentDate && prevMonth.valueOf() === currentDate) {
17264                 clsName += ' active';
17265             }
17266             
17267             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17268                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17269                     clsName += ' disabled';
17270             }
17271             
17272             fillMonths.cn.push({
17273                 tag: 'td',
17274                 cls: 'day ' + clsName,
17275                 html: prevMonth.getDate()
17276             });
17277             
17278             prevMonth.setDate(prevMonth.getDate()+1);
17279         }
17280           
17281         var currentYear = this.date && this.date.getUTCFullYear();
17282         var currentMonth = this.date && this.date.getUTCMonth();
17283         
17284         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17285         
17286         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17287             v.removeClass('active');
17288             
17289             if(currentYear === year && k === currentMonth){
17290                 v.addClass('active');
17291             }
17292             
17293             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17294                 v.addClass('disabled');
17295             }
17296             
17297         });
17298         
17299         
17300         year = parseInt(year/10, 10) * 10;
17301         
17302         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17303         
17304         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17305         
17306         year -= 1;
17307         for (var i = -1; i < 11; i++) {
17308             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17309                 tag: 'span',
17310                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17311                 html: year
17312             });
17313             
17314             year += 1;
17315         }
17316     },
17317     
17318     showMode: function(dir) 
17319     {
17320         if (dir) {
17321             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17322         }
17323         
17324         Roo.each(this.picker().select('>div',true).elements, function(v){
17325             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17326             v.hide();
17327         });
17328         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17329     },
17330     
17331     place: function()
17332     {
17333         if(this.isInline) {
17334             return;
17335         }
17336         
17337         this.picker().removeClass(['bottom', 'top']);
17338         
17339         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17340             /*
17341              * place to the top of element!
17342              *
17343              */
17344             
17345             this.picker().addClass('top');
17346             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17347             
17348             return;
17349         }
17350         
17351         this.picker().addClass('bottom');
17352         
17353         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17354     },
17355     
17356     parseDate : function(value)
17357     {
17358         if(!value || value instanceof Date){
17359             return value;
17360         }
17361         var v = Date.parseDate(value, this.format);
17362         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17363             v = Date.parseDate(value, 'Y-m-d');
17364         }
17365         if(!v && this.altFormats){
17366             if(!this.altFormatsArray){
17367                 this.altFormatsArray = this.altFormats.split("|");
17368             }
17369             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17370                 v = Date.parseDate(value, this.altFormatsArray[i]);
17371             }
17372         }
17373         return v;
17374     },
17375     
17376     formatDate : function(date, fmt)
17377     {   
17378         return (!date || !(date instanceof Date)) ?
17379         date : date.dateFormat(fmt || this.format);
17380     },
17381     
17382     onFocus : function()
17383     {
17384         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17385         this.show();
17386     },
17387     
17388     onBlur : function()
17389     {
17390         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17391         
17392         var d = this.inputEl().getValue();
17393         
17394         this.setValue(d);
17395                 
17396         this.hide();
17397     },
17398     
17399     show : function()
17400     {
17401         this.picker().show();
17402         this.update();
17403         this.place();
17404         
17405         this.fireEvent('show', this, this.date);
17406     },
17407     
17408     hide : function()
17409     {
17410         if(this.isInline) {
17411             return;
17412         }
17413         this.picker().hide();
17414         this.viewMode = this.startViewMode;
17415         this.showMode();
17416         
17417         this.fireEvent('hide', this, this.date);
17418         
17419     },
17420     
17421     onMousedown: function(e)
17422     {
17423         e.stopPropagation();
17424         e.preventDefault();
17425     },
17426     
17427     keyup: function(e)
17428     {
17429         Roo.bootstrap.DateField.superclass.keyup.call(this);
17430         this.update();
17431     },
17432
17433     setValue: function(v)
17434     {
17435         if(this.fireEvent('beforeselect', this, v) !== false){
17436             var d = new Date(this.parseDate(v) ).clearTime();
17437         
17438             if(isNaN(d.getTime())){
17439                 this.date = this.viewDate = '';
17440                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17441                 return;
17442             }
17443
17444             v = this.formatDate(d);
17445
17446             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17447
17448             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17449
17450             this.update();
17451
17452             this.fireEvent('select', this, this.date);
17453         }
17454     },
17455     
17456     getValue: function()
17457     {
17458         return this.formatDate(this.date);
17459     },
17460     
17461     fireKey: function(e)
17462     {
17463         if (!this.picker().isVisible()){
17464             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17465                 this.show();
17466             }
17467             return;
17468         }
17469         
17470         var dateChanged = false,
17471         dir, day, month,
17472         newDate, newViewDate;
17473         
17474         switch(e.keyCode){
17475             case 27: // escape
17476                 this.hide();
17477                 e.preventDefault();
17478                 break;
17479             case 37: // left
17480             case 39: // right
17481                 if (!this.keyboardNavigation) {
17482                     break;
17483                 }
17484                 dir = e.keyCode == 37 ? -1 : 1;
17485                 
17486                 if (e.ctrlKey){
17487                     newDate = this.moveYear(this.date, dir);
17488                     newViewDate = this.moveYear(this.viewDate, dir);
17489                 } else if (e.shiftKey){
17490                     newDate = this.moveMonth(this.date, dir);
17491                     newViewDate = this.moveMonth(this.viewDate, dir);
17492                 } else {
17493                     newDate = new Date(this.date);
17494                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17495                     newViewDate = new Date(this.viewDate);
17496                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17497                 }
17498                 if (this.dateWithinRange(newDate)){
17499                     this.date = newDate;
17500                     this.viewDate = newViewDate;
17501                     this.setValue(this.formatDate(this.date));
17502 //                    this.update();
17503                     e.preventDefault();
17504                     dateChanged = true;
17505                 }
17506                 break;
17507             case 38: // up
17508             case 40: // down
17509                 if (!this.keyboardNavigation) {
17510                     break;
17511                 }
17512                 dir = e.keyCode == 38 ? -1 : 1;
17513                 if (e.ctrlKey){
17514                     newDate = this.moveYear(this.date, dir);
17515                     newViewDate = this.moveYear(this.viewDate, dir);
17516                 } else if (e.shiftKey){
17517                     newDate = this.moveMonth(this.date, dir);
17518                     newViewDate = this.moveMonth(this.viewDate, dir);
17519                 } else {
17520                     newDate = new Date(this.date);
17521                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17522                     newViewDate = new Date(this.viewDate);
17523                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17524                 }
17525                 if (this.dateWithinRange(newDate)){
17526                     this.date = newDate;
17527                     this.viewDate = newViewDate;
17528                     this.setValue(this.formatDate(this.date));
17529 //                    this.update();
17530                     e.preventDefault();
17531                     dateChanged = true;
17532                 }
17533                 break;
17534             case 13: // enter
17535                 this.setValue(this.formatDate(this.date));
17536                 this.hide();
17537                 e.preventDefault();
17538                 break;
17539             case 9: // tab
17540                 this.setValue(this.formatDate(this.date));
17541                 this.hide();
17542                 break;
17543             case 16: // shift
17544             case 17: // ctrl
17545             case 18: // alt
17546                 break;
17547             default :
17548                 this.hide();
17549                 
17550         }
17551     },
17552     
17553     
17554     onClick: function(e) 
17555     {
17556         e.stopPropagation();
17557         e.preventDefault();
17558         
17559         var target = e.getTarget();
17560         
17561         if(target.nodeName.toLowerCase() === 'i'){
17562             target = Roo.get(target).dom.parentNode;
17563         }
17564         
17565         var nodeName = target.nodeName;
17566         var className = target.className;
17567         var html = target.innerHTML;
17568         //Roo.log(nodeName);
17569         
17570         switch(nodeName.toLowerCase()) {
17571             case 'th':
17572                 switch(className) {
17573                     case 'switch':
17574                         this.showMode(1);
17575                         break;
17576                     case 'prev':
17577                     case 'next':
17578                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17579                         switch(this.viewMode){
17580                                 case 0:
17581                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17582                                         break;
17583                                 case 1:
17584                                 case 2:
17585                                         this.viewDate = this.moveYear(this.viewDate, dir);
17586                                         break;
17587                         }
17588                         this.fill();
17589                         break;
17590                     case 'today':
17591                         var date = new Date();
17592                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17593 //                        this.fill()
17594                         this.setValue(this.formatDate(this.date));
17595                         
17596                         this.hide();
17597                         break;
17598                 }
17599                 break;
17600             case 'span':
17601                 if (className.indexOf('disabled') < 0) {
17602                     this.viewDate.setUTCDate(1);
17603                     if (className.indexOf('month') > -1) {
17604                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17605                     } else {
17606                         var year = parseInt(html, 10) || 0;
17607                         this.viewDate.setUTCFullYear(year);
17608                         
17609                     }
17610                     
17611                     if(this.singleMode){
17612                         this.setValue(this.formatDate(this.viewDate));
17613                         this.hide();
17614                         return;
17615                     }
17616                     
17617                     this.showMode(-1);
17618                     this.fill();
17619                 }
17620                 break;
17621                 
17622             case 'td':
17623                 //Roo.log(className);
17624                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17625                     var day = parseInt(html, 10) || 1;
17626                     var year = this.viewDate.getUTCFullYear(),
17627                         month = this.viewDate.getUTCMonth();
17628
17629                     if (className.indexOf('old') > -1) {
17630                         if(month === 0 ){
17631                             month = 11;
17632                             year -= 1;
17633                         }else{
17634                             month -= 1;
17635                         }
17636                     } else if (className.indexOf('new') > -1) {
17637                         if (month == 11) {
17638                             month = 0;
17639                             year += 1;
17640                         } else {
17641                             month += 1;
17642                         }
17643                     }
17644                     //Roo.log([year,month,day]);
17645                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17646                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17647 //                    this.fill();
17648                     //Roo.log(this.formatDate(this.date));
17649                     this.setValue(this.formatDate(this.date));
17650                     this.hide();
17651                 }
17652                 break;
17653         }
17654     },
17655     
17656     setStartDate: function(startDate)
17657     {
17658         this.startDate = startDate || -Infinity;
17659         if (this.startDate !== -Infinity) {
17660             this.startDate = this.parseDate(this.startDate);
17661         }
17662         this.update();
17663         this.updateNavArrows();
17664     },
17665
17666     setEndDate: function(endDate)
17667     {
17668         this.endDate = endDate || Infinity;
17669         if (this.endDate !== Infinity) {
17670             this.endDate = this.parseDate(this.endDate);
17671         }
17672         this.update();
17673         this.updateNavArrows();
17674     },
17675     
17676     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17677     {
17678         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17679         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17680             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17681         }
17682         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17683             return parseInt(d, 10);
17684         });
17685         this.update();
17686         this.updateNavArrows();
17687     },
17688     
17689     updateNavArrows: function() 
17690     {
17691         if(this.singleMode){
17692             return;
17693         }
17694         
17695         var d = new Date(this.viewDate),
17696         year = d.getUTCFullYear(),
17697         month = d.getUTCMonth();
17698         
17699         Roo.each(this.picker().select('.prev', true).elements, function(v){
17700             v.show();
17701             switch (this.viewMode) {
17702                 case 0:
17703
17704                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17705                         v.hide();
17706                     }
17707                     break;
17708                 case 1:
17709                 case 2:
17710                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17711                         v.hide();
17712                     }
17713                     break;
17714             }
17715         });
17716         
17717         Roo.each(this.picker().select('.next', true).elements, function(v){
17718             v.show();
17719             switch (this.viewMode) {
17720                 case 0:
17721
17722                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17723                         v.hide();
17724                     }
17725                     break;
17726                 case 1:
17727                 case 2:
17728                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17729                         v.hide();
17730                     }
17731                     break;
17732             }
17733         })
17734     },
17735     
17736     moveMonth: function(date, dir)
17737     {
17738         if (!dir) {
17739             return date;
17740         }
17741         var new_date = new Date(date.valueOf()),
17742         day = new_date.getUTCDate(),
17743         month = new_date.getUTCMonth(),
17744         mag = Math.abs(dir),
17745         new_month, test;
17746         dir = dir > 0 ? 1 : -1;
17747         if (mag == 1){
17748             test = dir == -1
17749             // If going back one month, make sure month is not current month
17750             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17751             ? function(){
17752                 return new_date.getUTCMonth() == month;
17753             }
17754             // If going forward one month, make sure month is as expected
17755             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17756             : function(){
17757                 return new_date.getUTCMonth() != new_month;
17758             };
17759             new_month = month + dir;
17760             new_date.setUTCMonth(new_month);
17761             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17762             if (new_month < 0 || new_month > 11) {
17763                 new_month = (new_month + 12) % 12;
17764             }
17765         } else {
17766             // For magnitudes >1, move one month at a time...
17767             for (var i=0; i<mag; i++) {
17768                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17769                 new_date = this.moveMonth(new_date, dir);
17770             }
17771             // ...then reset the day, keeping it in the new month
17772             new_month = new_date.getUTCMonth();
17773             new_date.setUTCDate(day);
17774             test = function(){
17775                 return new_month != new_date.getUTCMonth();
17776             };
17777         }
17778         // Common date-resetting loop -- if date is beyond end of month, make it
17779         // end of month
17780         while (test()){
17781             new_date.setUTCDate(--day);
17782             new_date.setUTCMonth(new_month);
17783         }
17784         return new_date;
17785     },
17786
17787     moveYear: function(date, dir)
17788     {
17789         return this.moveMonth(date, dir*12);
17790     },
17791
17792     dateWithinRange: function(date)
17793     {
17794         return date >= this.startDate && date <= this.endDate;
17795     },
17796
17797     
17798     remove: function() 
17799     {
17800         this.picker().remove();
17801     }
17802    
17803 });
17804
17805 Roo.apply(Roo.bootstrap.DateField,  {
17806     
17807     head : {
17808         tag: 'thead',
17809         cn: [
17810         {
17811             tag: 'tr',
17812             cn: [
17813             {
17814                 tag: 'th',
17815                 cls: 'prev',
17816                 html: '<i class="fa fa-arrow-left"/>'
17817             },
17818             {
17819                 tag: 'th',
17820                 cls: 'switch',
17821                 colspan: '5'
17822             },
17823             {
17824                 tag: 'th',
17825                 cls: 'next',
17826                 html: '<i class="fa fa-arrow-right"/>'
17827             }
17828
17829             ]
17830         }
17831         ]
17832     },
17833     
17834     content : {
17835         tag: 'tbody',
17836         cn: [
17837         {
17838             tag: 'tr',
17839             cn: [
17840             {
17841                 tag: 'td',
17842                 colspan: '7'
17843             }
17844             ]
17845         }
17846         ]
17847     },
17848     
17849     footer : {
17850         tag: 'tfoot',
17851         cn: [
17852         {
17853             tag: 'tr',
17854             cn: [
17855             {
17856                 tag: 'th',
17857                 colspan: '7',
17858                 cls: 'today'
17859             }
17860                     
17861             ]
17862         }
17863         ]
17864     },
17865     
17866     dates:{
17867         en: {
17868             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17869             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17870             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17871             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17872             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17873             today: "Today"
17874         }
17875     },
17876     
17877     modes: [
17878     {
17879         clsName: 'days',
17880         navFnc: 'Month',
17881         navStep: 1
17882     },
17883     {
17884         clsName: 'months',
17885         navFnc: 'FullYear',
17886         navStep: 1
17887     },
17888     {
17889         clsName: 'years',
17890         navFnc: 'FullYear',
17891         navStep: 10
17892     }]
17893 });
17894
17895 Roo.apply(Roo.bootstrap.DateField,  {
17896   
17897     template : {
17898         tag: 'div',
17899         cls: 'datepicker dropdown-menu roo-dynamic',
17900         cn: [
17901         {
17902             tag: 'div',
17903             cls: 'datepicker-days',
17904             cn: [
17905             {
17906                 tag: 'table',
17907                 cls: 'table-condensed',
17908                 cn:[
17909                 Roo.bootstrap.DateField.head,
17910                 {
17911                     tag: 'tbody'
17912                 },
17913                 Roo.bootstrap.DateField.footer
17914                 ]
17915             }
17916             ]
17917         },
17918         {
17919             tag: 'div',
17920             cls: 'datepicker-months',
17921             cn: [
17922             {
17923                 tag: 'table',
17924                 cls: 'table-condensed',
17925                 cn:[
17926                 Roo.bootstrap.DateField.head,
17927                 Roo.bootstrap.DateField.content,
17928                 Roo.bootstrap.DateField.footer
17929                 ]
17930             }
17931             ]
17932         },
17933         {
17934             tag: 'div',
17935             cls: 'datepicker-years',
17936             cn: [
17937             {
17938                 tag: 'table',
17939                 cls: 'table-condensed',
17940                 cn:[
17941                 Roo.bootstrap.DateField.head,
17942                 Roo.bootstrap.DateField.content,
17943                 Roo.bootstrap.DateField.footer
17944                 ]
17945             }
17946             ]
17947         }
17948         ]
17949     }
17950 });
17951
17952  
17953
17954  /*
17955  * - LGPL
17956  *
17957  * TimeField
17958  * 
17959  */
17960
17961 /**
17962  * @class Roo.bootstrap.TimeField
17963  * @extends Roo.bootstrap.Input
17964  * Bootstrap DateField class
17965  * 
17966  * 
17967  * @constructor
17968  * Create a new TimeField
17969  * @param {Object} config The config object
17970  */
17971
17972 Roo.bootstrap.TimeField = function(config){
17973     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17974     this.addEvents({
17975             /**
17976              * @event show
17977              * Fires when this field show.
17978              * @param {Roo.bootstrap.DateField} thisthis
17979              * @param {Mixed} date The date value
17980              */
17981             show : true,
17982             /**
17983              * @event show
17984              * Fires when this field hide.
17985              * @param {Roo.bootstrap.DateField} this
17986              * @param {Mixed} date The date value
17987              */
17988             hide : true,
17989             /**
17990              * @event select
17991              * Fires when select a date.
17992              * @param {Roo.bootstrap.DateField} this
17993              * @param {Mixed} date The date value
17994              */
17995             select : true
17996         });
17997 };
17998
17999 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18000     
18001     /**
18002      * @cfg {String} format
18003      * The default time format string which can be overriden for localization support.  The format must be
18004      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18005      */
18006     format : "H:i",
18007        
18008     onRender: function(ct, position)
18009     {
18010         
18011         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18012                 
18013         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18014         
18015         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18016         
18017         this.pop = this.picker().select('>.datepicker-time',true).first();
18018         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18019         
18020         this.picker().on('mousedown', this.onMousedown, this);
18021         this.picker().on('click', this.onClick, this);
18022         
18023         this.picker().addClass('datepicker-dropdown');
18024     
18025         this.fillTime();
18026         this.update();
18027             
18028         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18029         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18030         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18031         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18032         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18033         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18034
18035     },
18036     
18037     fireKey: function(e){
18038         if (!this.picker().isVisible()){
18039             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18040                 this.show();
18041             }
18042             return;
18043         }
18044
18045         e.preventDefault();
18046         
18047         switch(e.keyCode){
18048             case 27: // escape
18049                 this.hide();
18050                 break;
18051             case 37: // left
18052             case 39: // right
18053                 this.onTogglePeriod();
18054                 break;
18055             case 38: // up
18056                 this.onIncrementMinutes();
18057                 break;
18058             case 40: // down
18059                 this.onDecrementMinutes();
18060                 break;
18061             case 13: // enter
18062             case 9: // tab
18063                 this.setTime();
18064                 break;
18065         }
18066     },
18067     
18068     onClick: function(e) {
18069         e.stopPropagation();
18070         e.preventDefault();
18071     },
18072     
18073     picker : function()
18074     {
18075         return this.el.select('.datepicker', true).first();
18076     },
18077     
18078     fillTime: function()
18079     {    
18080         var time = this.pop.select('tbody', true).first();
18081         
18082         time.dom.innerHTML = '';
18083         
18084         time.createChild({
18085             tag: 'tr',
18086             cn: [
18087                 {
18088                     tag: 'td',
18089                     cn: [
18090                         {
18091                             tag: 'a',
18092                             href: '#',
18093                             cls: 'btn',
18094                             cn: [
18095                                 {
18096                                     tag: 'span',
18097                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18098                                 }
18099                             ]
18100                         } 
18101                     ]
18102                 },
18103                 {
18104                     tag: 'td',
18105                     cls: 'separator'
18106                 },
18107                 {
18108                     tag: 'td',
18109                     cn: [
18110                         {
18111                             tag: 'a',
18112                             href: '#',
18113                             cls: 'btn',
18114                             cn: [
18115                                 {
18116                                     tag: 'span',
18117                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18118                                 }
18119                             ]
18120                         }
18121                     ]
18122                 },
18123                 {
18124                     tag: 'td',
18125                     cls: 'separator'
18126                 }
18127             ]
18128         });
18129         
18130         time.createChild({
18131             tag: 'tr',
18132             cn: [
18133                 {
18134                     tag: 'td',
18135                     cn: [
18136                         {
18137                             tag: 'span',
18138                             cls: 'timepicker-hour',
18139                             html: '00'
18140                         }  
18141                     ]
18142                 },
18143                 {
18144                     tag: 'td',
18145                     cls: 'separator',
18146                     html: ':'
18147                 },
18148                 {
18149                     tag: 'td',
18150                     cn: [
18151                         {
18152                             tag: 'span',
18153                             cls: 'timepicker-minute',
18154                             html: '00'
18155                         }  
18156                     ]
18157                 },
18158                 {
18159                     tag: 'td',
18160                     cls: 'separator'
18161                 },
18162                 {
18163                     tag: 'td',
18164                     cn: [
18165                         {
18166                             tag: 'button',
18167                             type: 'button',
18168                             cls: 'btn btn-primary period',
18169                             html: 'AM'
18170                             
18171                         }
18172                     ]
18173                 }
18174             ]
18175         });
18176         
18177         time.createChild({
18178             tag: 'tr',
18179             cn: [
18180                 {
18181                     tag: 'td',
18182                     cn: [
18183                         {
18184                             tag: 'a',
18185                             href: '#',
18186                             cls: 'btn',
18187                             cn: [
18188                                 {
18189                                     tag: 'span',
18190                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18191                                 }
18192                             ]
18193                         }
18194                     ]
18195                 },
18196                 {
18197                     tag: 'td',
18198                     cls: 'separator'
18199                 },
18200                 {
18201                     tag: 'td',
18202                     cn: [
18203                         {
18204                             tag: 'a',
18205                             href: '#',
18206                             cls: 'btn',
18207                             cn: [
18208                                 {
18209                                     tag: 'span',
18210                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18211                                 }
18212                             ]
18213                         }
18214                     ]
18215                 },
18216                 {
18217                     tag: 'td',
18218                     cls: 'separator'
18219                 }
18220             ]
18221         });
18222         
18223     },
18224     
18225     update: function()
18226     {
18227         
18228         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18229         
18230         this.fill();
18231     },
18232     
18233     fill: function() 
18234     {
18235         var hours = this.time.getHours();
18236         var minutes = this.time.getMinutes();
18237         var period = 'AM';
18238         
18239         if(hours > 11){
18240             period = 'PM';
18241         }
18242         
18243         if(hours == 0){
18244             hours = 12;
18245         }
18246         
18247         
18248         if(hours > 12){
18249             hours = hours - 12;
18250         }
18251         
18252         if(hours < 10){
18253             hours = '0' + hours;
18254         }
18255         
18256         if(minutes < 10){
18257             minutes = '0' + minutes;
18258         }
18259         
18260         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18261         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18262         this.pop.select('button', true).first().dom.innerHTML = period;
18263         
18264     },
18265     
18266     place: function()
18267     {   
18268         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18269         
18270         var cls = ['bottom'];
18271         
18272         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18273             cls.pop();
18274             cls.push('top');
18275         }
18276         
18277         cls.push('right');
18278         
18279         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18280             cls.pop();
18281             cls.push('left');
18282         }
18283         
18284         this.picker().addClass(cls.join('-'));
18285         
18286         var _this = this;
18287         
18288         Roo.each(cls, function(c){
18289             if(c == 'bottom'){
18290                 _this.picker().setTop(_this.inputEl().getHeight());
18291                 return;
18292             }
18293             if(c == 'top'){
18294                 _this.picker().setTop(0 - _this.picker().getHeight());
18295                 return;
18296             }
18297             
18298             if(c == 'left'){
18299                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18300                 return;
18301             }
18302             if(c == 'right'){
18303                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18304                 return;
18305             }
18306         });
18307         
18308     },
18309   
18310     onFocus : function()
18311     {
18312         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18313         this.show();
18314     },
18315     
18316     onBlur : function()
18317     {
18318         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18319         this.hide();
18320     },
18321     
18322     show : function()
18323     {
18324         this.picker().show();
18325         this.pop.show();
18326         this.update();
18327         this.place();
18328         
18329         this.fireEvent('show', this, this.date);
18330     },
18331     
18332     hide : function()
18333     {
18334         this.picker().hide();
18335         this.pop.hide();
18336         
18337         this.fireEvent('hide', this, this.date);
18338     },
18339     
18340     setTime : function()
18341     {
18342         this.hide();
18343         this.setValue(this.time.format(this.format));
18344         
18345         this.fireEvent('select', this, this.date);
18346         
18347         
18348     },
18349     
18350     onMousedown: function(e){
18351         e.stopPropagation();
18352         e.preventDefault();
18353     },
18354     
18355     onIncrementHours: function()
18356     {
18357         Roo.log('onIncrementHours');
18358         this.time = this.time.add(Date.HOUR, 1);
18359         this.update();
18360         
18361     },
18362     
18363     onDecrementHours: function()
18364     {
18365         Roo.log('onDecrementHours');
18366         this.time = this.time.add(Date.HOUR, -1);
18367         this.update();
18368     },
18369     
18370     onIncrementMinutes: function()
18371     {
18372         Roo.log('onIncrementMinutes');
18373         this.time = this.time.add(Date.MINUTE, 1);
18374         this.update();
18375     },
18376     
18377     onDecrementMinutes: function()
18378     {
18379         Roo.log('onDecrementMinutes');
18380         this.time = this.time.add(Date.MINUTE, -1);
18381         this.update();
18382     },
18383     
18384     onTogglePeriod: function()
18385     {
18386         Roo.log('onTogglePeriod');
18387         this.time = this.time.add(Date.HOUR, 12);
18388         this.update();
18389     }
18390     
18391    
18392 });
18393
18394 Roo.apply(Roo.bootstrap.TimeField,  {
18395     
18396     content : {
18397         tag: 'tbody',
18398         cn: [
18399             {
18400                 tag: 'tr',
18401                 cn: [
18402                 {
18403                     tag: 'td',
18404                     colspan: '7'
18405                 }
18406                 ]
18407             }
18408         ]
18409     },
18410     
18411     footer : {
18412         tag: 'tfoot',
18413         cn: [
18414             {
18415                 tag: 'tr',
18416                 cn: [
18417                 {
18418                     tag: 'th',
18419                     colspan: '7',
18420                     cls: '',
18421                     cn: [
18422                         {
18423                             tag: 'button',
18424                             cls: 'btn btn-info ok',
18425                             html: 'OK'
18426                         }
18427                     ]
18428                 }
18429
18430                 ]
18431             }
18432         ]
18433     }
18434 });
18435
18436 Roo.apply(Roo.bootstrap.TimeField,  {
18437   
18438     template : {
18439         tag: 'div',
18440         cls: 'datepicker dropdown-menu',
18441         cn: [
18442             {
18443                 tag: 'div',
18444                 cls: 'datepicker-time',
18445                 cn: [
18446                 {
18447                     tag: 'table',
18448                     cls: 'table-condensed',
18449                     cn:[
18450                     Roo.bootstrap.TimeField.content,
18451                     Roo.bootstrap.TimeField.footer
18452                     ]
18453                 }
18454                 ]
18455             }
18456         ]
18457     }
18458 });
18459
18460  
18461
18462  /*
18463  * - LGPL
18464  *
18465  * MonthField
18466  * 
18467  */
18468
18469 /**
18470  * @class Roo.bootstrap.MonthField
18471  * @extends Roo.bootstrap.Input
18472  * Bootstrap MonthField class
18473  * 
18474  * @cfg {String} language default en
18475  * 
18476  * @constructor
18477  * Create a new MonthField
18478  * @param {Object} config The config object
18479  */
18480
18481 Roo.bootstrap.MonthField = function(config){
18482     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18483     
18484     this.addEvents({
18485         /**
18486          * @event show
18487          * Fires when this field show.
18488          * @param {Roo.bootstrap.MonthField} this
18489          * @param {Mixed} date The date value
18490          */
18491         show : true,
18492         /**
18493          * @event show
18494          * Fires when this field hide.
18495          * @param {Roo.bootstrap.MonthField} this
18496          * @param {Mixed} date The date value
18497          */
18498         hide : true,
18499         /**
18500          * @event select
18501          * Fires when select a date.
18502          * @param {Roo.bootstrap.MonthField} this
18503          * @param {String} oldvalue The old value
18504          * @param {String} newvalue The new value
18505          */
18506         select : true
18507     });
18508 };
18509
18510 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18511     
18512     onRender: function(ct, position)
18513     {
18514         
18515         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18516         
18517         this.language = this.language || 'en';
18518         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18519         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18520         
18521         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18522         this.isInline = false;
18523         this.isInput = true;
18524         this.component = this.el.select('.add-on', true).first() || false;
18525         this.component = (this.component && this.component.length === 0) ? false : this.component;
18526         this.hasInput = this.component && this.inputEL().length;
18527         
18528         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18529         
18530         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18531         
18532         this.picker().on('mousedown', this.onMousedown, this);
18533         this.picker().on('click', this.onClick, this);
18534         
18535         this.picker().addClass('datepicker-dropdown');
18536         
18537         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18538             v.setStyle('width', '189px');
18539         });
18540         
18541         this.fillMonths();
18542         
18543         this.update();
18544         
18545         if(this.isInline) {
18546             this.show();
18547         }
18548         
18549     },
18550     
18551     setValue: function(v, suppressEvent)
18552     {   
18553         var o = this.getValue();
18554         
18555         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18556         
18557         this.update();
18558
18559         if(suppressEvent !== true){
18560             this.fireEvent('select', this, o, v);
18561         }
18562         
18563     },
18564     
18565     getValue: function()
18566     {
18567         return this.value;
18568     },
18569     
18570     onClick: function(e) 
18571     {
18572         e.stopPropagation();
18573         e.preventDefault();
18574         
18575         var target = e.getTarget();
18576         
18577         if(target.nodeName.toLowerCase() === 'i'){
18578             target = Roo.get(target).dom.parentNode;
18579         }
18580         
18581         var nodeName = target.nodeName;
18582         var className = target.className;
18583         var html = target.innerHTML;
18584         
18585         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18586             return;
18587         }
18588         
18589         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18590         
18591         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18592         
18593         this.hide();
18594                         
18595     },
18596     
18597     picker : function()
18598     {
18599         return this.pickerEl;
18600     },
18601     
18602     fillMonths: function()
18603     {    
18604         var i = 0;
18605         var months = this.picker().select('>.datepicker-months td', true).first();
18606         
18607         months.dom.innerHTML = '';
18608         
18609         while (i < 12) {
18610             var month = {
18611                 tag: 'span',
18612                 cls: 'month',
18613                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18614             };
18615             
18616             months.createChild(month);
18617         }
18618         
18619     },
18620     
18621     update: function()
18622     {
18623         var _this = this;
18624         
18625         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18626             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18627         }
18628         
18629         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18630             e.removeClass('active');
18631             
18632             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18633                 e.addClass('active');
18634             }
18635         })
18636     },
18637     
18638     place: function()
18639     {
18640         if(this.isInline) {
18641             return;
18642         }
18643         
18644         this.picker().removeClass(['bottom', 'top']);
18645         
18646         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18647             /*
18648              * place to the top of element!
18649              *
18650              */
18651             
18652             this.picker().addClass('top');
18653             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18654             
18655             return;
18656         }
18657         
18658         this.picker().addClass('bottom');
18659         
18660         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18661     },
18662     
18663     onFocus : function()
18664     {
18665         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18666         this.show();
18667     },
18668     
18669     onBlur : function()
18670     {
18671         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18672         
18673         var d = this.inputEl().getValue();
18674         
18675         this.setValue(d);
18676                 
18677         this.hide();
18678     },
18679     
18680     show : function()
18681     {
18682         this.picker().show();
18683         this.picker().select('>.datepicker-months', true).first().show();
18684         this.update();
18685         this.place();
18686         
18687         this.fireEvent('show', this, this.date);
18688     },
18689     
18690     hide : function()
18691     {
18692         if(this.isInline) {
18693             return;
18694         }
18695         this.picker().hide();
18696         this.fireEvent('hide', this, this.date);
18697         
18698     },
18699     
18700     onMousedown: function(e)
18701     {
18702         e.stopPropagation();
18703         e.preventDefault();
18704     },
18705     
18706     keyup: function(e)
18707     {
18708         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18709         this.update();
18710     },
18711
18712     fireKey: function(e)
18713     {
18714         if (!this.picker().isVisible()){
18715             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18716                 this.show();
18717             }
18718             return;
18719         }
18720         
18721         var dir;
18722         
18723         switch(e.keyCode){
18724             case 27: // escape
18725                 this.hide();
18726                 e.preventDefault();
18727                 break;
18728             case 37: // left
18729             case 39: // right
18730                 dir = e.keyCode == 37 ? -1 : 1;
18731                 
18732                 this.vIndex = this.vIndex + dir;
18733                 
18734                 if(this.vIndex < 0){
18735                     this.vIndex = 0;
18736                 }
18737                 
18738                 if(this.vIndex > 11){
18739                     this.vIndex = 11;
18740                 }
18741                 
18742                 if(isNaN(this.vIndex)){
18743                     this.vIndex = 0;
18744                 }
18745                 
18746                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18747                 
18748                 break;
18749             case 38: // up
18750             case 40: // down
18751                 
18752                 dir = e.keyCode == 38 ? -1 : 1;
18753                 
18754                 this.vIndex = this.vIndex + dir * 4;
18755                 
18756                 if(this.vIndex < 0){
18757                     this.vIndex = 0;
18758                 }
18759                 
18760                 if(this.vIndex > 11){
18761                     this.vIndex = 11;
18762                 }
18763                 
18764                 if(isNaN(this.vIndex)){
18765                     this.vIndex = 0;
18766                 }
18767                 
18768                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18769                 break;
18770                 
18771             case 13: // enter
18772                 
18773                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18774                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18775                 }
18776                 
18777                 this.hide();
18778                 e.preventDefault();
18779                 break;
18780             case 9: // tab
18781                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18782                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18783                 }
18784                 this.hide();
18785                 break;
18786             case 16: // shift
18787             case 17: // ctrl
18788             case 18: // alt
18789                 break;
18790             default :
18791                 this.hide();
18792                 
18793         }
18794     },
18795     
18796     remove: function() 
18797     {
18798         this.picker().remove();
18799     }
18800    
18801 });
18802
18803 Roo.apply(Roo.bootstrap.MonthField,  {
18804     
18805     content : {
18806         tag: 'tbody',
18807         cn: [
18808         {
18809             tag: 'tr',
18810             cn: [
18811             {
18812                 tag: 'td',
18813                 colspan: '7'
18814             }
18815             ]
18816         }
18817         ]
18818     },
18819     
18820     dates:{
18821         en: {
18822             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18823             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18824         }
18825     }
18826 });
18827
18828 Roo.apply(Roo.bootstrap.MonthField,  {
18829   
18830     template : {
18831         tag: 'div',
18832         cls: 'datepicker dropdown-menu roo-dynamic',
18833         cn: [
18834             {
18835                 tag: 'div',
18836                 cls: 'datepicker-months',
18837                 cn: [
18838                 {
18839                     tag: 'table',
18840                     cls: 'table-condensed',
18841                     cn:[
18842                         Roo.bootstrap.DateField.content
18843                     ]
18844                 }
18845                 ]
18846             }
18847         ]
18848     }
18849 });
18850
18851  
18852
18853  
18854  /*
18855  * - LGPL
18856  *
18857  * CheckBox
18858  * 
18859  */
18860
18861 /**
18862  * @class Roo.bootstrap.CheckBox
18863  * @extends Roo.bootstrap.Input
18864  * Bootstrap CheckBox class
18865  * 
18866  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18867  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18868  * @cfg {String} boxLabel The text that appears beside the checkbox
18869  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18870  * @cfg {Boolean} checked initnal the element
18871  * @cfg {Boolean} inline inline the element (default false)
18872  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18873  * 
18874  * @constructor
18875  * Create a new CheckBox
18876  * @param {Object} config The config object
18877  */
18878
18879 Roo.bootstrap.CheckBox = function(config){
18880     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18881    
18882     this.addEvents({
18883         /**
18884         * @event check
18885         * Fires when the element is checked or unchecked.
18886         * @param {Roo.bootstrap.CheckBox} this This input
18887         * @param {Boolean} checked The new checked value
18888         */
18889        check : true
18890     });
18891     
18892 };
18893
18894 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18895   
18896     inputType: 'checkbox',
18897     inputValue: 1,
18898     valueOff: 0,
18899     boxLabel: false,
18900     checked: false,
18901     weight : false,
18902     inline: false,
18903     
18904     getAutoCreate : function()
18905     {
18906         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18907         
18908         var id = Roo.id();
18909         
18910         var cfg = {};
18911         
18912         cfg.cls = 'form-group ' + this.inputType; //input-group
18913         
18914         if(this.inline){
18915             cfg.cls += ' ' + this.inputType + '-inline';
18916         }
18917         
18918         var input =  {
18919             tag: 'input',
18920             id : id,
18921             type : this.inputType,
18922             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18923             cls : 'roo-' + this.inputType, //'form-box',
18924             placeholder : this.placeholder || ''
18925             
18926         };
18927         
18928         if (this.weight) { // Validity check?
18929             cfg.cls += " " + this.inputType + "-" + this.weight;
18930         }
18931         
18932         if (this.disabled) {
18933             input.disabled=true;
18934         }
18935         
18936         if(this.checked){
18937             input.checked = this.checked;
18938         }
18939         
18940         if (this.name) {
18941             input.name = this.name;
18942         }
18943         
18944         if (this.size) {
18945             input.cls += ' input-' + this.size;
18946         }
18947         
18948         var settings=this;
18949         
18950         ['xs','sm','md','lg'].map(function(size){
18951             if (settings[size]) {
18952                 cfg.cls += ' col-' + size + '-' + settings[size];
18953             }
18954         });
18955         
18956         var inputblock = input;
18957          
18958         if (this.before || this.after) {
18959             
18960             inputblock = {
18961                 cls : 'input-group',
18962                 cn :  [] 
18963             };
18964             
18965             if (this.before) {
18966                 inputblock.cn.push({
18967                     tag :'span',
18968                     cls : 'input-group-addon',
18969                     html : this.before
18970                 });
18971             }
18972             
18973             inputblock.cn.push(input);
18974             
18975             if (this.after) {
18976                 inputblock.cn.push({
18977                     tag :'span',
18978                     cls : 'input-group-addon',
18979                     html : this.after
18980                 });
18981             }
18982             
18983         }
18984         
18985         if (align ==='left' && this.fieldLabel.length) {
18986 //                Roo.log("left and has label");
18987                 cfg.cn = [
18988                     
18989                     {
18990                         tag: 'label',
18991                         'for' :  id,
18992                         cls : 'control-label col-md-' + this.labelWidth,
18993                         html : this.fieldLabel
18994                         
18995                     },
18996                     {
18997                         cls : "col-md-" + (12 - this.labelWidth), 
18998                         cn: [
18999                             inputblock
19000                         ]
19001                     }
19002                     
19003                 ];
19004         } else if ( this.fieldLabel.length) {
19005 //                Roo.log(" label");
19006                 cfg.cn = [
19007                    
19008                     {
19009                         tag: this.boxLabel ? 'span' : 'label',
19010                         'for': id,
19011                         cls: 'control-label box-input-label',
19012                         //cls : 'input-group-addon',
19013                         html : this.fieldLabel
19014                         
19015                     },
19016                     
19017                     inputblock
19018                     
19019                 ];
19020
19021         } else {
19022             
19023 //                Roo.log(" no label && no align");
19024                 cfg.cn = [  inputblock ] ;
19025                 
19026                 
19027         }
19028         
19029         if(this.boxLabel){
19030              var boxLabelCfg = {
19031                 tag: 'label',
19032                 //'for': id, // box label is handled by onclick - so no for...
19033                 cls: 'box-label',
19034                 html: this.boxLabel
19035             };
19036             
19037             if(this.tooltip){
19038                 boxLabelCfg.tooltip = this.tooltip;
19039             }
19040              
19041             cfg.cn.push(boxLabelCfg);
19042         }
19043         
19044         
19045        
19046         return cfg;
19047         
19048     },
19049     
19050     /**
19051      * return the real input element.
19052      */
19053     inputEl: function ()
19054     {
19055         return this.el.select('input.roo-' + this.inputType,true).first();
19056     },
19057     
19058     labelEl: function()
19059     {
19060         return this.el.select('label.control-label',true).first();
19061     },
19062     /* depricated... */
19063     
19064     label: function()
19065     {
19066         return this.labelEl();
19067     },
19068     
19069     boxLabelEl: function()
19070     {
19071         return this.el.select('label.box-label',true).first();
19072     },
19073     
19074     initEvents : function()
19075     {
19076 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19077         
19078         this.inputEl().on('click', this.onClick,  this);
19079         
19080         if (this.boxLabel) { 
19081             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19082         }
19083         
19084         this.startValue = this.getValue();
19085         
19086         if(this.groupId){
19087             Roo.bootstrap.CheckBox.register(this);
19088         }
19089     },
19090     
19091     onClick : function()
19092     {   
19093         this.setChecked(!this.checked);
19094     },
19095     
19096     setChecked : function(state,suppressEvent)
19097     {
19098         this.startValue = this.getValue();
19099         
19100         if(this.inputType == 'radio'){
19101             
19102             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19103                 e.dom.checked = false;
19104             });
19105             
19106             this.inputEl().dom.checked = true;
19107             
19108             this.inputEl().dom.value = this.inputValue;
19109             
19110             if(suppressEvent !== true){
19111                 this.fireEvent('check', this, true);
19112             }
19113             
19114             this.validate();
19115             
19116             return;
19117         }
19118         
19119         this.checked = state;
19120         
19121         this.inputEl().dom.checked = state;
19122         
19123         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19124         
19125         if(suppressEvent !== true){
19126             this.fireEvent('check', this, state);
19127         }
19128         
19129         this.validate();
19130     },
19131     
19132     getValue : function()
19133     {
19134         if(this.inputType == 'radio'){
19135             return this.getGroupValue();
19136         }
19137         
19138         return this.inputEl().getValue();
19139         
19140     },
19141     
19142     getGroupValue : function()
19143     {
19144         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19145             return '';
19146         }
19147         
19148         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19149     },
19150     
19151     setValue : function(v,suppressEvent)
19152     {
19153         if(this.inputType == 'radio'){
19154             this.setGroupValue(v, suppressEvent);
19155             return;
19156         }
19157         
19158         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19159         
19160         this.validate();
19161     },
19162     
19163     setGroupValue : function(v, suppressEvent)
19164     {
19165         this.startValue = this.getValue();
19166         
19167         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19168             e.dom.checked = false;
19169             
19170             if(e.dom.value == v){
19171                 e.dom.checked = true;
19172             }
19173         });
19174         
19175         if(suppressEvent !== true){
19176             this.fireEvent('check', this, true);
19177         }
19178
19179         this.validate();
19180         
19181         return;
19182     },
19183     
19184     validate : function()
19185     {
19186         if(
19187                 this.disabled || 
19188                 (this.inputType == 'radio' && this.validateRadio()) ||
19189                 (this.inputType == 'checkbox' && this.validateCheckbox())
19190         ){
19191             this.markValid();
19192             return true;
19193         }
19194         
19195         this.markInvalid();
19196         return false;
19197     },
19198     
19199     validateRadio : function()
19200     {
19201         var valid = false;
19202         
19203         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19204             if(!e.dom.checked){
19205                 return;
19206             }
19207             
19208             valid = true;
19209             
19210             return false;
19211         });
19212         
19213         return valid;
19214     },
19215     
19216     validateCheckbox : function()
19217     {
19218         if(!this.groupId){
19219             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19220         }
19221         
19222         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19223         
19224         if(!group){
19225             return false;
19226         }
19227         
19228         var r = false;
19229         
19230         for(var i in group){
19231             if(r){
19232                 break;
19233             }
19234             
19235             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19236         }
19237         
19238         return r;
19239     },
19240     
19241     /**
19242      * Mark this field as valid
19243      */
19244     markValid : function()
19245     {
19246         if(this.allowBlank){
19247             return;
19248         }
19249         
19250         var _this = this;
19251         
19252         this.fireEvent('valid', this);
19253         
19254         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19255         
19256         if(this.groupId){
19257             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19258         }
19259         
19260         if(label){
19261             label.markValid();
19262         }
19263         
19264         if(this.inputType == 'radio'){
19265             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19266                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19267                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19268             });
19269             
19270             return;
19271         }
19272         
19273         if(!this.groupId){
19274             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19275             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19276             return;
19277         }
19278         
19279         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19280             
19281         if(!group){
19282             return;
19283         }
19284         
19285         for(var i in group){
19286             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19287             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19288         }
19289     },
19290     
19291      /**
19292      * Mark this field as invalid
19293      * @param {String} msg The validation message
19294      */
19295     markInvalid : function(msg)
19296     {
19297         if(this.allowBlank){
19298             return;
19299         }
19300         
19301         var _this = this;
19302         
19303         this.fireEvent('invalid', this, msg);
19304         
19305         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19306         
19307         if(this.groupId){
19308             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19309         }
19310         
19311         if(label){
19312             label.markInvalid();
19313         }
19314             
19315         if(this.inputType == 'radio'){
19316             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19317                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19318                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19319             });
19320             
19321             return;
19322         }
19323         
19324         if(!this.groupId){
19325             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19326             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19327             return;
19328         }
19329         
19330         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19331         
19332         if(!group){
19333             return;
19334         }
19335         
19336         for(var i in group){
19337             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19338             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19339         }
19340         
19341     }
19342     
19343 });
19344
19345 Roo.apply(Roo.bootstrap.CheckBox, {
19346     
19347     groups: {},
19348     
19349      /**
19350     * register a CheckBox Group
19351     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19352     */
19353     register : function(checkbox)
19354     {
19355         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19356             this.groups[checkbox.groupId] = {};
19357         }
19358         
19359         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19360             return;
19361         }
19362         
19363         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19364         
19365     },
19366     /**
19367     * fetch a CheckBox Group based on the group ID
19368     * @param {string} the group ID
19369     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19370     */
19371     get: function(groupId) {
19372         if (typeof(this.groups[groupId]) == 'undefined') {
19373             return false;
19374         }
19375         
19376         return this.groups[groupId] ;
19377     }
19378     
19379     
19380 });
19381 /*
19382  * - LGPL
19383  *
19384  * Radio
19385  *
19386  *
19387  * not inline
19388  *<div class="radio">
19389   <label>
19390     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19391     Option one is this and that&mdash;be sure to include why it's great
19392   </label>
19393 </div>
19394  *
19395  *
19396  *inline
19397  *<span>
19398  *<label class="radio-inline">fieldLabel</label>
19399  *<label class="radio-inline">
19400   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19401 </label>
19402 <span>
19403  * 
19404  * 
19405  */
19406
19407 /**
19408  * @class Roo.bootstrap.Radio
19409  * @extends Roo.bootstrap.CheckBox
19410  * Bootstrap Radio class
19411
19412  * @constructor
19413  * Create a new Radio
19414  * @param {Object} config The config object
19415  */
19416
19417 Roo.bootstrap.Radio = function(config){
19418     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19419    
19420 };
19421
19422 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19423     
19424     inputType: 'radio',
19425     inputValue: '',
19426     valueOff: '',
19427     
19428     getAutoCreate : function()
19429     {
19430         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19431         align = align || 'left'; // default...
19432         
19433         
19434         
19435         var id = Roo.id();
19436         
19437         var cfg = {
19438                 tag : this.inline ? 'span' : 'div',
19439                 cls : '',
19440                 cn : []
19441         };
19442         
19443         var inline = this.inline ? ' radio-inline' : '';
19444         
19445         var lbl = {
19446                 tag: 'label' ,
19447                 // does not need for, as we wrap the input with it..
19448                 'for' : id,
19449                 cls : 'control-label box-label' + inline,
19450                 cn : []
19451         };
19452         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19453         
19454         var fieldLabel = {
19455             tag: 'label' ,
19456             //cls : 'control-label' + inline,
19457             html : this.fieldLabel,
19458             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19459         };
19460         
19461  
19462         
19463         
19464         var input =  {
19465             tag: 'input',
19466             id : id,
19467             type : this.inputType,
19468             //value : (!this.checked) ? this.valueOff : this.inputValue,
19469             value : this.inputValue,
19470             cls : 'roo-radio',
19471             placeholder : this.placeholder || '' // ?? needed????
19472             
19473         };
19474         if (this.weight) { // Validity check?
19475             input.cls += " radio-" + this.weight;
19476         }
19477         if (this.disabled) {
19478             input.disabled=true;
19479         }
19480         
19481         if(this.checked){
19482             input.checked = this.checked;
19483         }
19484         
19485         if (this.name) {
19486             input.name = this.name;
19487         }
19488         
19489         if (this.size) {
19490             input.cls += ' input-' + this.size;
19491         }
19492         
19493         //?? can span's inline have a width??
19494         
19495         var settings=this;
19496         ['xs','sm','md','lg'].map(function(size){
19497             if (settings[size]) {
19498                 cfg.cls += ' col-' + size + '-' + settings[size];
19499             }
19500         });
19501         
19502         var inputblock = input;
19503         
19504         if (this.before || this.after) {
19505             
19506             inputblock = {
19507                 cls : 'input-group',
19508                 tag : 'span',
19509                 cn :  [] 
19510             };
19511             if (this.before) {
19512                 inputblock.cn.push({
19513                     tag :'span',
19514                     cls : 'input-group-addon',
19515                     html : this.before
19516                 });
19517             }
19518             inputblock.cn.push(input);
19519             if (this.after) {
19520                 inputblock.cn.push({
19521                     tag :'span',
19522                     cls : 'input-group-addon',
19523                     html : this.after
19524                 });
19525             }
19526             
19527         };
19528         
19529         
19530         if (this.fieldLabel && this.fieldLabel.length) {
19531             cfg.cn.push(fieldLabel);
19532         }
19533        
19534         // normal bootstrap puts the input inside the label.
19535         // however with our styled version - it has to go after the input.
19536        
19537         //lbl.cn.push(inputblock);
19538         
19539         var lblwrap =  {
19540             tag: 'span',
19541             cls: 'radio' + inline,
19542             cn: [
19543                 inputblock,
19544                 lbl
19545             ]
19546         };
19547         
19548         cfg.cn.push( lblwrap);
19549         
19550         if(this.boxLabel){
19551             lbl.cn.push({
19552                 tag: 'span',
19553                 html: this.boxLabel
19554             })
19555         }
19556          
19557         
19558         return cfg;
19559         
19560     },
19561     
19562     initEvents : function()
19563     {
19564 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19565         
19566         this.inputEl().on('click', this.onClick,  this);
19567         if (this.boxLabel) {
19568             //Roo.log('find label');
19569             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19570         }
19571         
19572     },
19573     
19574     inputEl: function ()
19575     {
19576         return this.el.select('input.roo-radio',true).first();
19577     },
19578     onClick : function()
19579     {   
19580         Roo.log("click");
19581         this.setChecked(true);
19582     },
19583     
19584     setChecked : function(state,suppressEvent)
19585     {
19586         if(state){
19587             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19588                 v.dom.checked = false;
19589             });
19590         }
19591         Roo.log(this.inputEl().dom);
19592         this.checked = state;
19593         this.inputEl().dom.checked = state;
19594         
19595         if(suppressEvent !== true){
19596             this.fireEvent('check', this, state);
19597         }
19598         
19599         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19600         
19601     },
19602     
19603     getGroupValue : function()
19604     {
19605         var value = '';
19606         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19607             if(v.dom.checked == true){
19608                 value = v.dom.value;
19609             }
19610         });
19611         
19612         return value;
19613     },
19614     
19615     /**
19616      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19617      * @return {Mixed} value The field value
19618      */
19619     getValue : function(){
19620         return this.getGroupValue();
19621     }
19622     
19623 });
19624
19625  
19626 //<script type="text/javascript">
19627
19628 /*
19629  * Based  Ext JS Library 1.1.1
19630  * Copyright(c) 2006-2007, Ext JS, LLC.
19631  * LGPL
19632  *
19633  */
19634  
19635 /**
19636  * @class Roo.HtmlEditorCore
19637  * @extends Roo.Component
19638  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19639  *
19640  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19641  */
19642
19643 Roo.HtmlEditorCore = function(config){
19644     
19645     
19646     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19647     
19648     
19649     this.addEvents({
19650         /**
19651          * @event initialize
19652          * Fires when the editor is fully initialized (including the iframe)
19653          * @param {Roo.HtmlEditorCore} this
19654          */
19655         initialize: true,
19656         /**
19657          * @event activate
19658          * Fires when the editor is first receives the focus. Any insertion must wait
19659          * until after this event.
19660          * @param {Roo.HtmlEditorCore} this
19661          */
19662         activate: true,
19663          /**
19664          * @event beforesync
19665          * Fires before the textarea is updated with content from the editor iframe. Return false
19666          * to cancel the sync.
19667          * @param {Roo.HtmlEditorCore} this
19668          * @param {String} html
19669          */
19670         beforesync: true,
19671          /**
19672          * @event beforepush
19673          * Fires before the iframe editor is updated with content from the textarea. Return false
19674          * to cancel the push.
19675          * @param {Roo.HtmlEditorCore} this
19676          * @param {String} html
19677          */
19678         beforepush: true,
19679          /**
19680          * @event sync
19681          * Fires when the textarea is updated with content from the editor iframe.
19682          * @param {Roo.HtmlEditorCore} this
19683          * @param {String} html
19684          */
19685         sync: true,
19686          /**
19687          * @event push
19688          * Fires when the iframe editor is updated with content from the textarea.
19689          * @param {Roo.HtmlEditorCore} this
19690          * @param {String} html
19691          */
19692         push: true,
19693         
19694         /**
19695          * @event editorevent
19696          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19697          * @param {Roo.HtmlEditorCore} this
19698          */
19699         editorevent: true
19700         
19701     });
19702     
19703     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19704     
19705     // defaults : white / black...
19706     this.applyBlacklists();
19707     
19708     
19709     
19710 };
19711
19712
19713 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19714
19715
19716      /**
19717      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19718      */
19719     
19720     owner : false,
19721     
19722      /**
19723      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19724      *                        Roo.resizable.
19725      */
19726     resizable : false,
19727      /**
19728      * @cfg {Number} height (in pixels)
19729      */   
19730     height: 300,
19731    /**
19732      * @cfg {Number} width (in pixels)
19733      */   
19734     width: 500,
19735     
19736     /**
19737      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19738      * 
19739      */
19740     stylesheets: false,
19741     
19742     // id of frame..
19743     frameId: false,
19744     
19745     // private properties
19746     validationEvent : false,
19747     deferHeight: true,
19748     initialized : false,
19749     activated : false,
19750     sourceEditMode : false,
19751     onFocus : Roo.emptyFn,
19752     iframePad:3,
19753     hideMode:'offsets',
19754     
19755     clearUp: true,
19756     
19757     // blacklist + whitelisted elements..
19758     black: false,
19759     white: false,
19760      
19761     
19762
19763     /**
19764      * Protected method that will not generally be called directly. It
19765      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19766      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19767      */
19768     getDocMarkup : function(){
19769         // body styles..
19770         var st = '';
19771         
19772         // inherit styels from page...?? 
19773         if (this.stylesheets === false) {
19774             
19775             Roo.get(document.head).select('style').each(function(node) {
19776                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19777             });
19778             
19779             Roo.get(document.head).select('link').each(function(node) { 
19780                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19781             });
19782             
19783         } else if (!this.stylesheets.length) {
19784                 // simple..
19785                 st = '<style type="text/css">' +
19786                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19787                    '</style>';
19788         } else { 
19789             
19790         }
19791         
19792         st +=  '<style type="text/css">' +
19793             'IMG { cursor: pointer } ' +
19794         '</style>';
19795
19796         
19797         return '<html><head>' + st  +
19798             //<style type="text/css">' +
19799             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19800             //'</style>' +
19801             ' </head><body class="roo-htmleditor-body"></body></html>';
19802     },
19803
19804     // private
19805     onRender : function(ct, position)
19806     {
19807         var _t = this;
19808         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19809         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19810         
19811         
19812         this.el.dom.style.border = '0 none';
19813         this.el.dom.setAttribute('tabIndex', -1);
19814         this.el.addClass('x-hidden hide');
19815         
19816         
19817         
19818         if(Roo.isIE){ // fix IE 1px bogus margin
19819             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19820         }
19821        
19822         
19823         this.frameId = Roo.id();
19824         
19825          
19826         
19827         var iframe = this.owner.wrap.createChild({
19828             tag: 'iframe',
19829             cls: 'form-control', // bootstrap..
19830             id: this.frameId,
19831             name: this.frameId,
19832             frameBorder : 'no',
19833             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19834         }, this.el
19835         );
19836         
19837         
19838         this.iframe = iframe.dom;
19839
19840          this.assignDocWin();
19841         
19842         this.doc.designMode = 'on';
19843        
19844         this.doc.open();
19845         this.doc.write(this.getDocMarkup());
19846         this.doc.close();
19847
19848         
19849         var task = { // must defer to wait for browser to be ready
19850             run : function(){
19851                 //console.log("run task?" + this.doc.readyState);
19852                 this.assignDocWin();
19853                 if(this.doc.body || this.doc.readyState == 'complete'){
19854                     try {
19855                         this.doc.designMode="on";
19856                     } catch (e) {
19857                         return;
19858                     }
19859                     Roo.TaskMgr.stop(task);
19860                     this.initEditor.defer(10, this);
19861                 }
19862             },
19863             interval : 10,
19864             duration: 10000,
19865             scope: this
19866         };
19867         Roo.TaskMgr.start(task);
19868
19869     },
19870
19871     // private
19872     onResize : function(w, h)
19873     {
19874          Roo.log('resize: ' +w + ',' + h );
19875         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19876         if(!this.iframe){
19877             return;
19878         }
19879         if(typeof w == 'number'){
19880             
19881             this.iframe.style.width = w + 'px';
19882         }
19883         if(typeof h == 'number'){
19884             
19885             this.iframe.style.height = h + 'px';
19886             if(this.doc){
19887                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19888             }
19889         }
19890         
19891     },
19892
19893     /**
19894      * Toggles the editor between standard and source edit mode.
19895      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19896      */
19897     toggleSourceEdit : function(sourceEditMode){
19898         
19899         this.sourceEditMode = sourceEditMode === true;
19900         
19901         if(this.sourceEditMode){
19902  
19903             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19904             
19905         }else{
19906             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19907             //this.iframe.className = '';
19908             this.deferFocus();
19909         }
19910         //this.setSize(this.owner.wrap.getSize());
19911         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19912     },
19913
19914     
19915   
19916
19917     /**
19918      * Protected method that will not generally be called directly. If you need/want
19919      * custom HTML cleanup, this is the method you should override.
19920      * @param {String} html The HTML to be cleaned
19921      * return {String} The cleaned HTML
19922      */
19923     cleanHtml : function(html){
19924         html = String(html);
19925         if(html.length > 5){
19926             if(Roo.isSafari){ // strip safari nonsense
19927                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19928             }
19929         }
19930         if(html == '&nbsp;'){
19931             html = '';
19932         }
19933         return html;
19934     },
19935
19936     /**
19937      * HTML Editor -> Textarea
19938      * Protected method that will not generally be called directly. Syncs the contents
19939      * of the editor iframe with the textarea.
19940      */
19941     syncValue : function(){
19942         if(this.initialized){
19943             var bd = (this.doc.body || this.doc.documentElement);
19944             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19945             var html = bd.innerHTML;
19946             if(Roo.isSafari){
19947                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19948                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19949                 if(m && m[1]){
19950                     html = '<div style="'+m[0]+'">' + html + '</div>';
19951                 }
19952             }
19953             html = this.cleanHtml(html);
19954             // fix up the special chars.. normaly like back quotes in word...
19955             // however we do not want to do this with chinese..
19956             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19957                 var cc = b.charCodeAt();
19958                 if (
19959                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19960                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19961                     (cc >= 0xf900 && cc < 0xfb00 )
19962                 ) {
19963                         return b;
19964                 }
19965                 return "&#"+cc+";" 
19966             });
19967             if(this.owner.fireEvent('beforesync', this, html) !== false){
19968                 this.el.dom.value = html;
19969                 this.owner.fireEvent('sync', this, html);
19970             }
19971         }
19972     },
19973
19974     /**
19975      * Protected method that will not generally be called directly. Pushes the value of the textarea
19976      * into the iframe editor.
19977      */
19978     pushValue : function(){
19979         if(this.initialized){
19980             var v = this.el.dom.value.trim();
19981             
19982 //            if(v.length < 1){
19983 //                v = '&#160;';
19984 //            }
19985             
19986             if(this.owner.fireEvent('beforepush', this, v) !== false){
19987                 var d = (this.doc.body || this.doc.documentElement);
19988                 d.innerHTML = v;
19989                 this.cleanUpPaste();
19990                 this.el.dom.value = d.innerHTML;
19991                 this.owner.fireEvent('push', this, v);
19992             }
19993         }
19994     },
19995
19996     // private
19997     deferFocus : function(){
19998         this.focus.defer(10, this);
19999     },
20000
20001     // doc'ed in Field
20002     focus : function(){
20003         if(this.win && !this.sourceEditMode){
20004             this.win.focus();
20005         }else{
20006             this.el.focus();
20007         }
20008     },
20009     
20010     assignDocWin: function()
20011     {
20012         var iframe = this.iframe;
20013         
20014          if(Roo.isIE){
20015             this.doc = iframe.contentWindow.document;
20016             this.win = iframe.contentWindow;
20017         } else {
20018 //            if (!Roo.get(this.frameId)) {
20019 //                return;
20020 //            }
20021 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20022 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20023             
20024             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20025                 return;
20026             }
20027             
20028             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20029             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20030         }
20031     },
20032     
20033     // private
20034     initEditor : function(){
20035         //console.log("INIT EDITOR");
20036         this.assignDocWin();
20037         
20038         
20039         
20040         this.doc.designMode="on";
20041         this.doc.open();
20042         this.doc.write(this.getDocMarkup());
20043         this.doc.close();
20044         
20045         var dbody = (this.doc.body || this.doc.documentElement);
20046         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20047         // this copies styles from the containing element into thsi one..
20048         // not sure why we need all of this..
20049         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20050         
20051         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20052         //ss['background-attachment'] = 'fixed'; // w3c
20053         dbody.bgProperties = 'fixed'; // ie
20054         //Roo.DomHelper.applyStyles(dbody, ss);
20055         Roo.EventManager.on(this.doc, {
20056             //'mousedown': this.onEditorEvent,
20057             'mouseup': this.onEditorEvent,
20058             'dblclick': this.onEditorEvent,
20059             'click': this.onEditorEvent,
20060             'keyup': this.onEditorEvent,
20061             buffer:100,
20062             scope: this
20063         });
20064         if(Roo.isGecko){
20065             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20066         }
20067         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20068             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20069         }
20070         this.initialized = true;
20071
20072         this.owner.fireEvent('initialize', this);
20073         this.pushValue();
20074     },
20075
20076     // private
20077     onDestroy : function(){
20078         
20079         
20080         
20081         if(this.rendered){
20082             
20083             //for (var i =0; i < this.toolbars.length;i++) {
20084             //    // fixme - ask toolbars for heights?
20085             //    this.toolbars[i].onDestroy();
20086            // }
20087             
20088             //this.wrap.dom.innerHTML = '';
20089             //this.wrap.remove();
20090         }
20091     },
20092
20093     // private
20094     onFirstFocus : function(){
20095         
20096         this.assignDocWin();
20097         
20098         
20099         this.activated = true;
20100          
20101     
20102         if(Roo.isGecko){ // prevent silly gecko errors
20103             this.win.focus();
20104             var s = this.win.getSelection();
20105             if(!s.focusNode || s.focusNode.nodeType != 3){
20106                 var r = s.getRangeAt(0);
20107                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20108                 r.collapse(true);
20109                 this.deferFocus();
20110             }
20111             try{
20112                 this.execCmd('useCSS', true);
20113                 this.execCmd('styleWithCSS', false);
20114             }catch(e){}
20115         }
20116         this.owner.fireEvent('activate', this);
20117     },
20118
20119     // private
20120     adjustFont: function(btn){
20121         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20122         //if(Roo.isSafari){ // safari
20123         //    adjust *= 2;
20124        // }
20125         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20126         if(Roo.isSafari){ // safari
20127             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20128             v =  (v < 10) ? 10 : v;
20129             v =  (v > 48) ? 48 : v;
20130             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20131             
20132         }
20133         
20134         
20135         v = Math.max(1, v+adjust);
20136         
20137         this.execCmd('FontSize', v  );
20138     },
20139
20140     onEditorEvent : function(e)
20141     {
20142         this.owner.fireEvent('editorevent', this, e);
20143       //  this.updateToolbar();
20144         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20145     },
20146
20147     insertTag : function(tg)
20148     {
20149         // could be a bit smarter... -> wrap the current selected tRoo..
20150         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20151             
20152             range = this.createRange(this.getSelection());
20153             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20154             wrappingNode.appendChild(range.extractContents());
20155             range.insertNode(wrappingNode);
20156
20157             return;
20158             
20159             
20160             
20161         }
20162         this.execCmd("formatblock",   tg);
20163         
20164     },
20165     
20166     insertText : function(txt)
20167     {
20168         
20169         
20170         var range = this.createRange();
20171         range.deleteContents();
20172                //alert(Sender.getAttribute('label'));
20173                
20174         range.insertNode(this.doc.createTextNode(txt));
20175     } ,
20176     
20177      
20178
20179     /**
20180      * Executes a Midas editor command on the editor document and performs necessary focus and
20181      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20182      * @param {String} cmd The Midas command
20183      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20184      */
20185     relayCmd : function(cmd, value){
20186         this.win.focus();
20187         this.execCmd(cmd, value);
20188         this.owner.fireEvent('editorevent', this);
20189         //this.updateToolbar();
20190         this.owner.deferFocus();
20191     },
20192
20193     /**
20194      * Executes a Midas editor command directly on the editor document.
20195      * For visual commands, you should use {@link #relayCmd} instead.
20196      * <b>This should only be called after the editor is initialized.</b>
20197      * @param {String} cmd The Midas command
20198      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20199      */
20200     execCmd : function(cmd, value){
20201         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20202         this.syncValue();
20203     },
20204  
20205  
20206    
20207     /**
20208      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20209      * to insert tRoo.
20210      * @param {String} text | dom node.. 
20211      */
20212     insertAtCursor : function(text)
20213     {
20214         
20215         
20216         
20217         if(!this.activated){
20218             return;
20219         }
20220         /*
20221         if(Roo.isIE){
20222             this.win.focus();
20223             var r = this.doc.selection.createRange();
20224             if(r){
20225                 r.collapse(true);
20226                 r.pasteHTML(text);
20227                 this.syncValue();
20228                 this.deferFocus();
20229             
20230             }
20231             return;
20232         }
20233         */
20234         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20235             this.win.focus();
20236             
20237             
20238             // from jquery ui (MIT licenced)
20239             var range, node;
20240             var win = this.win;
20241             
20242             if (win.getSelection && win.getSelection().getRangeAt) {
20243                 range = win.getSelection().getRangeAt(0);
20244                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20245                 range.insertNode(node);
20246             } else if (win.document.selection && win.document.selection.createRange) {
20247                 // no firefox support
20248                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20249                 win.document.selection.createRange().pasteHTML(txt);
20250             } else {
20251                 // no firefox support
20252                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20253                 this.execCmd('InsertHTML', txt);
20254             } 
20255             
20256             this.syncValue();
20257             
20258             this.deferFocus();
20259         }
20260     },
20261  // private
20262     mozKeyPress : function(e){
20263         if(e.ctrlKey){
20264             var c = e.getCharCode(), cmd;
20265           
20266             if(c > 0){
20267                 c = String.fromCharCode(c).toLowerCase();
20268                 switch(c){
20269                     case 'b':
20270                         cmd = 'bold';
20271                         break;
20272                     case 'i':
20273                         cmd = 'italic';
20274                         break;
20275                     
20276                     case 'u':
20277                         cmd = 'underline';
20278                         break;
20279                     
20280                     case 'v':
20281                         this.cleanUpPaste.defer(100, this);
20282                         return;
20283                         
20284                 }
20285                 if(cmd){
20286                     this.win.focus();
20287                     this.execCmd(cmd);
20288                     this.deferFocus();
20289                     e.preventDefault();
20290                 }
20291                 
20292             }
20293         }
20294     },
20295
20296     // private
20297     fixKeys : function(){ // load time branching for fastest keydown performance
20298         if(Roo.isIE){
20299             return function(e){
20300                 var k = e.getKey(), r;
20301                 if(k == e.TAB){
20302                     e.stopEvent();
20303                     r = this.doc.selection.createRange();
20304                     if(r){
20305                         r.collapse(true);
20306                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20307                         this.deferFocus();
20308                     }
20309                     return;
20310                 }
20311                 
20312                 if(k == e.ENTER){
20313                     r = this.doc.selection.createRange();
20314                     if(r){
20315                         var target = r.parentElement();
20316                         if(!target || target.tagName.toLowerCase() != 'li'){
20317                             e.stopEvent();
20318                             r.pasteHTML('<br />');
20319                             r.collapse(false);
20320                             r.select();
20321                         }
20322                     }
20323                 }
20324                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20325                     this.cleanUpPaste.defer(100, this);
20326                     return;
20327                 }
20328                 
20329                 
20330             };
20331         }else if(Roo.isOpera){
20332             return function(e){
20333                 var k = e.getKey();
20334                 if(k == e.TAB){
20335                     e.stopEvent();
20336                     this.win.focus();
20337                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20338                     this.deferFocus();
20339                 }
20340                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20341                     this.cleanUpPaste.defer(100, this);
20342                     return;
20343                 }
20344                 
20345             };
20346         }else if(Roo.isSafari){
20347             return function(e){
20348                 var k = e.getKey();
20349                 
20350                 if(k == e.TAB){
20351                     e.stopEvent();
20352                     this.execCmd('InsertText','\t');
20353                     this.deferFocus();
20354                     return;
20355                 }
20356                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20357                     this.cleanUpPaste.defer(100, this);
20358                     return;
20359                 }
20360                 
20361              };
20362         }
20363     }(),
20364     
20365     getAllAncestors: function()
20366     {
20367         var p = this.getSelectedNode();
20368         var a = [];
20369         if (!p) {
20370             a.push(p); // push blank onto stack..
20371             p = this.getParentElement();
20372         }
20373         
20374         
20375         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20376             a.push(p);
20377             p = p.parentNode;
20378         }
20379         a.push(this.doc.body);
20380         return a;
20381     },
20382     lastSel : false,
20383     lastSelNode : false,
20384     
20385     
20386     getSelection : function() 
20387     {
20388         this.assignDocWin();
20389         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20390     },
20391     
20392     getSelectedNode: function() 
20393     {
20394         // this may only work on Gecko!!!
20395         
20396         // should we cache this!!!!
20397         
20398         
20399         
20400          
20401         var range = this.createRange(this.getSelection()).cloneRange();
20402         
20403         if (Roo.isIE) {
20404             var parent = range.parentElement();
20405             while (true) {
20406                 var testRange = range.duplicate();
20407                 testRange.moveToElementText(parent);
20408                 if (testRange.inRange(range)) {
20409                     break;
20410                 }
20411                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20412                     break;
20413                 }
20414                 parent = parent.parentElement;
20415             }
20416             return parent;
20417         }
20418         
20419         // is ancestor a text element.
20420         var ac =  range.commonAncestorContainer;
20421         if (ac.nodeType == 3) {
20422             ac = ac.parentNode;
20423         }
20424         
20425         var ar = ac.childNodes;
20426          
20427         var nodes = [];
20428         var other_nodes = [];
20429         var has_other_nodes = false;
20430         for (var i=0;i<ar.length;i++) {
20431             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20432                 continue;
20433             }
20434             // fullly contained node.
20435             
20436             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20437                 nodes.push(ar[i]);
20438                 continue;
20439             }
20440             
20441             // probably selected..
20442             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20443                 other_nodes.push(ar[i]);
20444                 continue;
20445             }
20446             // outer..
20447             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20448                 continue;
20449             }
20450             
20451             
20452             has_other_nodes = true;
20453         }
20454         if (!nodes.length && other_nodes.length) {
20455             nodes= other_nodes;
20456         }
20457         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20458             return false;
20459         }
20460         
20461         return nodes[0];
20462     },
20463     createRange: function(sel)
20464     {
20465         // this has strange effects when using with 
20466         // top toolbar - not sure if it's a great idea.
20467         //this.editor.contentWindow.focus();
20468         if (typeof sel != "undefined") {
20469             try {
20470                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20471             } catch(e) {
20472                 return this.doc.createRange();
20473             }
20474         } else {
20475             return this.doc.createRange();
20476         }
20477     },
20478     getParentElement: function()
20479     {
20480         
20481         this.assignDocWin();
20482         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20483         
20484         var range = this.createRange(sel);
20485          
20486         try {
20487             var p = range.commonAncestorContainer;
20488             while (p.nodeType == 3) { // text node
20489                 p = p.parentNode;
20490             }
20491             return p;
20492         } catch (e) {
20493             return null;
20494         }
20495     
20496     },
20497     /***
20498      *
20499      * Range intersection.. the hard stuff...
20500      *  '-1' = before
20501      *  '0' = hits..
20502      *  '1' = after.
20503      *         [ -- selected range --- ]
20504      *   [fail]                        [fail]
20505      *
20506      *    basically..
20507      *      if end is before start or  hits it. fail.
20508      *      if start is after end or hits it fail.
20509      *
20510      *   if either hits (but other is outside. - then it's not 
20511      *   
20512      *    
20513      **/
20514     
20515     
20516     // @see http://www.thismuchiknow.co.uk/?p=64.
20517     rangeIntersectsNode : function(range, node)
20518     {
20519         var nodeRange = node.ownerDocument.createRange();
20520         try {
20521             nodeRange.selectNode(node);
20522         } catch (e) {
20523             nodeRange.selectNodeContents(node);
20524         }
20525     
20526         var rangeStartRange = range.cloneRange();
20527         rangeStartRange.collapse(true);
20528     
20529         var rangeEndRange = range.cloneRange();
20530         rangeEndRange.collapse(false);
20531     
20532         var nodeStartRange = nodeRange.cloneRange();
20533         nodeStartRange.collapse(true);
20534     
20535         var nodeEndRange = nodeRange.cloneRange();
20536         nodeEndRange.collapse(false);
20537     
20538         return rangeStartRange.compareBoundaryPoints(
20539                  Range.START_TO_START, nodeEndRange) == -1 &&
20540                rangeEndRange.compareBoundaryPoints(
20541                  Range.START_TO_START, nodeStartRange) == 1;
20542         
20543          
20544     },
20545     rangeCompareNode : function(range, node)
20546     {
20547         var nodeRange = node.ownerDocument.createRange();
20548         try {
20549             nodeRange.selectNode(node);
20550         } catch (e) {
20551             nodeRange.selectNodeContents(node);
20552         }
20553         
20554         
20555         range.collapse(true);
20556     
20557         nodeRange.collapse(true);
20558      
20559         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20560         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20561          
20562         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20563         
20564         var nodeIsBefore   =  ss == 1;
20565         var nodeIsAfter    = ee == -1;
20566         
20567         if (nodeIsBefore && nodeIsAfter) {
20568             return 0; // outer
20569         }
20570         if (!nodeIsBefore && nodeIsAfter) {
20571             return 1; //right trailed.
20572         }
20573         
20574         if (nodeIsBefore && !nodeIsAfter) {
20575             return 2;  // left trailed.
20576         }
20577         // fully contined.
20578         return 3;
20579     },
20580
20581     // private? - in a new class?
20582     cleanUpPaste :  function()
20583     {
20584         // cleans up the whole document..
20585         Roo.log('cleanuppaste');
20586         
20587         this.cleanUpChildren(this.doc.body);
20588         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20589         if (clean != this.doc.body.innerHTML) {
20590             this.doc.body.innerHTML = clean;
20591         }
20592         
20593     },
20594     
20595     cleanWordChars : function(input) {// change the chars to hex code
20596         var he = Roo.HtmlEditorCore;
20597         
20598         var output = input;
20599         Roo.each(he.swapCodes, function(sw) { 
20600             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20601             
20602             output = output.replace(swapper, sw[1]);
20603         });
20604         
20605         return output;
20606     },
20607     
20608     
20609     cleanUpChildren : function (n)
20610     {
20611         if (!n.childNodes.length) {
20612             return;
20613         }
20614         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20615            this.cleanUpChild(n.childNodes[i]);
20616         }
20617     },
20618     
20619     
20620         
20621     
20622     cleanUpChild : function (node)
20623     {
20624         var ed = this;
20625         //console.log(node);
20626         if (node.nodeName == "#text") {
20627             // clean up silly Windows -- stuff?
20628             return; 
20629         }
20630         if (node.nodeName == "#comment") {
20631             node.parentNode.removeChild(node);
20632             // clean up silly Windows -- stuff?
20633             return; 
20634         }
20635         var lcname = node.tagName.toLowerCase();
20636         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20637         // whitelist of tags..
20638         
20639         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20640             // remove node.
20641             node.parentNode.removeChild(node);
20642             return;
20643             
20644         }
20645         
20646         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20647         
20648         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20649         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20650         
20651         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20652         //    remove_keep_children = true;
20653         //}
20654         
20655         if (remove_keep_children) {
20656             this.cleanUpChildren(node);
20657             // inserts everything just before this node...
20658             while (node.childNodes.length) {
20659                 var cn = node.childNodes[0];
20660                 node.removeChild(cn);
20661                 node.parentNode.insertBefore(cn, node);
20662             }
20663             node.parentNode.removeChild(node);
20664             return;
20665         }
20666         
20667         if (!node.attributes || !node.attributes.length) {
20668             this.cleanUpChildren(node);
20669             return;
20670         }
20671         
20672         function cleanAttr(n,v)
20673         {
20674             
20675             if (v.match(/^\./) || v.match(/^\//)) {
20676                 return;
20677             }
20678             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20679                 return;
20680             }
20681             if (v.match(/^#/)) {
20682                 return;
20683             }
20684 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20685             node.removeAttribute(n);
20686             
20687         }
20688         
20689         var cwhite = this.cwhite;
20690         var cblack = this.cblack;
20691             
20692         function cleanStyle(n,v)
20693         {
20694             if (v.match(/expression/)) { //XSS?? should we even bother..
20695                 node.removeAttribute(n);
20696                 return;
20697             }
20698             
20699             var parts = v.split(/;/);
20700             var clean = [];
20701             
20702             Roo.each(parts, function(p) {
20703                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20704                 if (!p.length) {
20705                     return true;
20706                 }
20707                 var l = p.split(':').shift().replace(/\s+/g,'');
20708                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20709                 
20710                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20711 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20712                     //node.removeAttribute(n);
20713                     return true;
20714                 }
20715                 //Roo.log()
20716                 // only allow 'c whitelisted system attributes'
20717                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20718 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20719                     //node.removeAttribute(n);
20720                     return true;
20721                 }
20722                 
20723                 
20724                  
20725                 
20726                 clean.push(p);
20727                 return true;
20728             });
20729             if (clean.length) { 
20730                 node.setAttribute(n, clean.join(';'));
20731             } else {
20732                 node.removeAttribute(n);
20733             }
20734             
20735         }
20736         
20737         
20738         for (var i = node.attributes.length-1; i > -1 ; i--) {
20739             var a = node.attributes[i];
20740             //console.log(a);
20741             
20742             if (a.name.toLowerCase().substr(0,2)=='on')  {
20743                 node.removeAttribute(a.name);
20744                 continue;
20745             }
20746             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20747                 node.removeAttribute(a.name);
20748                 continue;
20749             }
20750             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20751                 cleanAttr(a.name,a.value); // fixme..
20752                 continue;
20753             }
20754             if (a.name == 'style') {
20755                 cleanStyle(a.name,a.value);
20756                 continue;
20757             }
20758             /// clean up MS crap..
20759             // tecnically this should be a list of valid class'es..
20760             
20761             
20762             if (a.name == 'class') {
20763                 if (a.value.match(/^Mso/)) {
20764                     node.className = '';
20765                 }
20766                 
20767                 if (a.value.match(/body/)) {
20768                     node.className = '';
20769                 }
20770                 continue;
20771             }
20772             
20773             // style cleanup!?
20774             // class cleanup?
20775             
20776         }
20777         
20778         
20779         this.cleanUpChildren(node);
20780         
20781         
20782     },
20783     
20784     /**
20785      * Clean up MS wordisms...
20786      */
20787     cleanWord : function(node)
20788     {
20789         
20790         
20791         if (!node) {
20792             this.cleanWord(this.doc.body);
20793             return;
20794         }
20795         if (node.nodeName == "#text") {
20796             // clean up silly Windows -- stuff?
20797             return; 
20798         }
20799         if (node.nodeName == "#comment") {
20800             node.parentNode.removeChild(node);
20801             // clean up silly Windows -- stuff?
20802             return; 
20803         }
20804         
20805         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20806             node.parentNode.removeChild(node);
20807             return;
20808         }
20809         
20810         // remove - but keep children..
20811         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20812             while (node.childNodes.length) {
20813                 var cn = node.childNodes[0];
20814                 node.removeChild(cn);
20815                 node.parentNode.insertBefore(cn, node);
20816             }
20817             node.parentNode.removeChild(node);
20818             this.iterateChildren(node, this.cleanWord);
20819             return;
20820         }
20821         // clean styles
20822         if (node.className.length) {
20823             
20824             var cn = node.className.split(/\W+/);
20825             var cna = [];
20826             Roo.each(cn, function(cls) {
20827                 if (cls.match(/Mso[a-zA-Z]+/)) {
20828                     return;
20829                 }
20830                 cna.push(cls);
20831             });
20832             node.className = cna.length ? cna.join(' ') : '';
20833             if (!cna.length) {
20834                 node.removeAttribute("class");
20835             }
20836         }
20837         
20838         if (node.hasAttribute("lang")) {
20839             node.removeAttribute("lang");
20840         }
20841         
20842         if (node.hasAttribute("style")) {
20843             
20844             var styles = node.getAttribute("style").split(";");
20845             var nstyle = [];
20846             Roo.each(styles, function(s) {
20847                 if (!s.match(/:/)) {
20848                     return;
20849                 }
20850                 var kv = s.split(":");
20851                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20852                     return;
20853                 }
20854                 // what ever is left... we allow.
20855                 nstyle.push(s);
20856             });
20857             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20858             if (!nstyle.length) {
20859                 node.removeAttribute('style');
20860             }
20861         }
20862         this.iterateChildren(node, this.cleanWord);
20863         
20864         
20865         
20866     },
20867     /**
20868      * iterateChildren of a Node, calling fn each time, using this as the scole..
20869      * @param {DomNode} node node to iterate children of.
20870      * @param {Function} fn method of this class to call on each item.
20871      */
20872     iterateChildren : function(node, fn)
20873     {
20874         if (!node.childNodes.length) {
20875                 return;
20876         }
20877         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20878            fn.call(this, node.childNodes[i])
20879         }
20880     },
20881     
20882     
20883     /**
20884      * cleanTableWidths.
20885      *
20886      * Quite often pasting from word etc.. results in tables with column and widths.
20887      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20888      *
20889      */
20890     cleanTableWidths : function(node)
20891     {
20892          
20893          
20894         if (!node) {
20895             this.cleanTableWidths(this.doc.body);
20896             return;
20897         }
20898         
20899         // ignore list...
20900         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20901             return; 
20902         }
20903         Roo.log(node.tagName);
20904         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20905             this.iterateChildren(node, this.cleanTableWidths);
20906             return;
20907         }
20908         if (node.hasAttribute('width')) {
20909             node.removeAttribute('width');
20910         }
20911         
20912          
20913         if (node.hasAttribute("style")) {
20914             // pretty basic...
20915             
20916             var styles = node.getAttribute("style").split(";");
20917             var nstyle = [];
20918             Roo.each(styles, function(s) {
20919                 if (!s.match(/:/)) {
20920                     return;
20921                 }
20922                 var kv = s.split(":");
20923                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20924                     return;
20925                 }
20926                 // what ever is left... we allow.
20927                 nstyle.push(s);
20928             });
20929             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20930             if (!nstyle.length) {
20931                 node.removeAttribute('style');
20932             }
20933         }
20934         
20935         this.iterateChildren(node, this.cleanTableWidths);
20936         
20937         
20938     },
20939     
20940     
20941     
20942     
20943     domToHTML : function(currentElement, depth, nopadtext) {
20944         
20945         depth = depth || 0;
20946         nopadtext = nopadtext || false;
20947     
20948         if (!currentElement) {
20949             return this.domToHTML(this.doc.body);
20950         }
20951         
20952         //Roo.log(currentElement);
20953         var j;
20954         var allText = false;
20955         var nodeName = currentElement.nodeName;
20956         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20957         
20958         if  (nodeName == '#text') {
20959             
20960             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20961         }
20962         
20963         
20964         var ret = '';
20965         if (nodeName != 'BODY') {
20966              
20967             var i = 0;
20968             // Prints the node tagName, such as <A>, <IMG>, etc
20969             if (tagName) {
20970                 var attr = [];
20971                 for(i = 0; i < currentElement.attributes.length;i++) {
20972                     // quoting?
20973                     var aname = currentElement.attributes.item(i).name;
20974                     if (!currentElement.attributes.item(i).value.length) {
20975                         continue;
20976                     }
20977                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20978                 }
20979                 
20980                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20981             } 
20982             else {
20983                 
20984                 // eack
20985             }
20986         } else {
20987             tagName = false;
20988         }
20989         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20990             return ret;
20991         }
20992         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20993             nopadtext = true;
20994         }
20995         
20996         
20997         // Traverse the tree
20998         i = 0;
20999         var currentElementChild = currentElement.childNodes.item(i);
21000         var allText = true;
21001         var innerHTML  = '';
21002         lastnode = '';
21003         while (currentElementChild) {
21004             // Formatting code (indent the tree so it looks nice on the screen)
21005             var nopad = nopadtext;
21006             if (lastnode == 'SPAN') {
21007                 nopad  = true;
21008             }
21009             // text
21010             if  (currentElementChild.nodeName == '#text') {
21011                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21012                 toadd = nopadtext ? toadd : toadd.trim();
21013                 if (!nopad && toadd.length > 80) {
21014                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21015                 }
21016                 innerHTML  += toadd;
21017                 
21018                 i++;
21019                 currentElementChild = currentElement.childNodes.item(i);
21020                 lastNode = '';
21021                 continue;
21022             }
21023             allText = false;
21024             
21025             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21026                 
21027             // Recursively traverse the tree structure of the child node
21028             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21029             lastnode = currentElementChild.nodeName;
21030             i++;
21031             currentElementChild=currentElement.childNodes.item(i);
21032         }
21033         
21034         ret += innerHTML;
21035         
21036         if (!allText) {
21037                 // The remaining code is mostly for formatting the tree
21038             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21039         }
21040         
21041         
21042         if (tagName) {
21043             ret+= "</"+tagName+">";
21044         }
21045         return ret;
21046         
21047     },
21048         
21049     applyBlacklists : function()
21050     {
21051         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21052         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21053         
21054         this.white = [];
21055         this.black = [];
21056         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21057             if (b.indexOf(tag) > -1) {
21058                 return;
21059             }
21060             this.white.push(tag);
21061             
21062         }, this);
21063         
21064         Roo.each(w, function(tag) {
21065             if (b.indexOf(tag) > -1) {
21066                 return;
21067             }
21068             if (this.white.indexOf(tag) > -1) {
21069                 return;
21070             }
21071             this.white.push(tag);
21072             
21073         }, this);
21074         
21075         
21076         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21077             if (w.indexOf(tag) > -1) {
21078                 return;
21079             }
21080             this.black.push(tag);
21081             
21082         }, this);
21083         
21084         Roo.each(b, function(tag) {
21085             if (w.indexOf(tag) > -1) {
21086                 return;
21087             }
21088             if (this.black.indexOf(tag) > -1) {
21089                 return;
21090             }
21091             this.black.push(tag);
21092             
21093         }, this);
21094         
21095         
21096         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21097         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21098         
21099         this.cwhite = [];
21100         this.cblack = [];
21101         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21102             if (b.indexOf(tag) > -1) {
21103                 return;
21104             }
21105             this.cwhite.push(tag);
21106             
21107         }, this);
21108         
21109         Roo.each(w, function(tag) {
21110             if (b.indexOf(tag) > -1) {
21111                 return;
21112             }
21113             if (this.cwhite.indexOf(tag) > -1) {
21114                 return;
21115             }
21116             this.cwhite.push(tag);
21117             
21118         }, this);
21119         
21120         
21121         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21122             if (w.indexOf(tag) > -1) {
21123                 return;
21124             }
21125             this.cblack.push(tag);
21126             
21127         }, this);
21128         
21129         Roo.each(b, function(tag) {
21130             if (w.indexOf(tag) > -1) {
21131                 return;
21132             }
21133             if (this.cblack.indexOf(tag) > -1) {
21134                 return;
21135             }
21136             this.cblack.push(tag);
21137             
21138         }, this);
21139     },
21140     
21141     setStylesheets : function(stylesheets)
21142     {
21143         if(typeof(stylesheets) == 'string'){
21144             Roo.get(this.iframe.contentDocument.head).createChild({
21145                 tag : 'link',
21146                 rel : 'stylesheet',
21147                 type : 'text/css',
21148                 href : stylesheets
21149             });
21150             
21151             return;
21152         }
21153         var _this = this;
21154      
21155         Roo.each(stylesheets, function(s) {
21156             if(!s.length){
21157                 return;
21158             }
21159             
21160             Roo.get(_this.iframe.contentDocument.head).createChild({
21161                 tag : 'link',
21162                 rel : 'stylesheet',
21163                 type : 'text/css',
21164                 href : s
21165             });
21166         });
21167
21168         
21169     },
21170     
21171     removeStylesheets : function()
21172     {
21173         var _this = this;
21174         
21175         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21176             s.remove();
21177         });
21178     }
21179     
21180     // hide stuff that is not compatible
21181     /**
21182      * @event blur
21183      * @hide
21184      */
21185     /**
21186      * @event change
21187      * @hide
21188      */
21189     /**
21190      * @event focus
21191      * @hide
21192      */
21193     /**
21194      * @event specialkey
21195      * @hide
21196      */
21197     /**
21198      * @cfg {String} fieldClass @hide
21199      */
21200     /**
21201      * @cfg {String} focusClass @hide
21202      */
21203     /**
21204      * @cfg {String} autoCreate @hide
21205      */
21206     /**
21207      * @cfg {String} inputType @hide
21208      */
21209     /**
21210      * @cfg {String} invalidClass @hide
21211      */
21212     /**
21213      * @cfg {String} invalidText @hide
21214      */
21215     /**
21216      * @cfg {String} msgFx @hide
21217      */
21218     /**
21219      * @cfg {String} validateOnBlur @hide
21220      */
21221 });
21222
21223 Roo.HtmlEditorCore.white = [
21224         'area', 'br', 'img', 'input', 'hr', 'wbr',
21225         
21226        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21227        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21228        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21229        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21230        'table',   'ul',         'xmp', 
21231        
21232        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21233       'thead',   'tr', 
21234      
21235       'dir', 'menu', 'ol', 'ul', 'dl',
21236        
21237       'embed',  'object'
21238 ];
21239
21240
21241 Roo.HtmlEditorCore.black = [
21242     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21243         'applet', // 
21244         'base',   'basefont', 'bgsound', 'blink',  'body', 
21245         'frame',  'frameset', 'head',    'html',   'ilayer', 
21246         'iframe', 'layer',  'link',     'meta',    'object',   
21247         'script', 'style' ,'title',  'xml' // clean later..
21248 ];
21249 Roo.HtmlEditorCore.clean = [
21250     'script', 'style', 'title', 'xml'
21251 ];
21252 Roo.HtmlEditorCore.remove = [
21253     'font'
21254 ];
21255 // attributes..
21256
21257 Roo.HtmlEditorCore.ablack = [
21258     'on'
21259 ];
21260     
21261 Roo.HtmlEditorCore.aclean = [ 
21262     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21263 ];
21264
21265 // protocols..
21266 Roo.HtmlEditorCore.pwhite= [
21267         'http',  'https',  'mailto'
21268 ];
21269
21270 // white listed style attributes.
21271 Roo.HtmlEditorCore.cwhite= [
21272       //  'text-align', /// default is to allow most things..
21273       
21274          
21275 //        'font-size'//??
21276 ];
21277
21278 // black listed style attributes.
21279 Roo.HtmlEditorCore.cblack= [
21280       //  'font-size' -- this can be set by the project 
21281 ];
21282
21283
21284 Roo.HtmlEditorCore.swapCodes   =[ 
21285     [    8211, "--" ], 
21286     [    8212, "--" ], 
21287     [    8216,  "'" ],  
21288     [    8217, "'" ],  
21289     [    8220, '"' ],  
21290     [    8221, '"' ],  
21291     [    8226, "*" ],  
21292     [    8230, "..." ]
21293 ]; 
21294
21295     /*
21296  * - LGPL
21297  *
21298  * HtmlEditor
21299  * 
21300  */
21301
21302 /**
21303  * @class Roo.bootstrap.HtmlEditor
21304  * @extends Roo.bootstrap.TextArea
21305  * Bootstrap HtmlEditor class
21306
21307  * @constructor
21308  * Create a new HtmlEditor
21309  * @param {Object} config The config object
21310  */
21311
21312 Roo.bootstrap.HtmlEditor = function(config){
21313     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21314     if (!this.toolbars) {
21315         this.toolbars = [];
21316     }
21317     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21318     this.addEvents({
21319             /**
21320              * @event initialize
21321              * Fires when the editor is fully initialized (including the iframe)
21322              * @param {HtmlEditor} this
21323              */
21324             initialize: true,
21325             /**
21326              * @event activate
21327              * Fires when the editor is first receives the focus. Any insertion must wait
21328              * until after this event.
21329              * @param {HtmlEditor} this
21330              */
21331             activate: true,
21332              /**
21333              * @event beforesync
21334              * Fires before the textarea is updated with content from the editor iframe. Return false
21335              * to cancel the sync.
21336              * @param {HtmlEditor} this
21337              * @param {String} html
21338              */
21339             beforesync: true,
21340              /**
21341              * @event beforepush
21342              * Fires before the iframe editor is updated with content from the textarea. Return false
21343              * to cancel the push.
21344              * @param {HtmlEditor} this
21345              * @param {String} html
21346              */
21347             beforepush: true,
21348              /**
21349              * @event sync
21350              * Fires when the textarea is updated with content from the editor iframe.
21351              * @param {HtmlEditor} this
21352              * @param {String} html
21353              */
21354             sync: true,
21355              /**
21356              * @event push
21357              * Fires when the iframe editor is updated with content from the textarea.
21358              * @param {HtmlEditor} this
21359              * @param {String} html
21360              */
21361             push: true,
21362              /**
21363              * @event editmodechange
21364              * Fires when the editor switches edit modes
21365              * @param {HtmlEditor} this
21366              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21367              */
21368             editmodechange: true,
21369             /**
21370              * @event editorevent
21371              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21372              * @param {HtmlEditor} this
21373              */
21374             editorevent: true,
21375             /**
21376              * @event firstfocus
21377              * Fires when on first focus - needed by toolbars..
21378              * @param {HtmlEditor} this
21379              */
21380             firstfocus: true,
21381             /**
21382              * @event autosave
21383              * Auto save the htmlEditor value as a file into Events
21384              * @param {HtmlEditor} this
21385              */
21386             autosave: true,
21387             /**
21388              * @event savedpreview
21389              * preview the saved version of htmlEditor
21390              * @param {HtmlEditor} this
21391              */
21392             savedpreview: true
21393         });
21394 };
21395
21396
21397 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21398     
21399     
21400       /**
21401      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21402      */
21403     toolbars : false,
21404    
21405      /**
21406      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21407      *                        Roo.resizable.
21408      */
21409     resizable : false,
21410      /**
21411      * @cfg {Number} height (in pixels)
21412      */   
21413     height: 300,
21414    /**
21415      * @cfg {Number} width (in pixels)
21416      */   
21417     width: false,
21418     
21419     /**
21420      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21421      * 
21422      */
21423     stylesheets: false,
21424     
21425     // id of frame..
21426     frameId: false,
21427     
21428     // private properties
21429     validationEvent : false,
21430     deferHeight: true,
21431     initialized : false,
21432     activated : false,
21433     
21434     onFocus : Roo.emptyFn,
21435     iframePad:3,
21436     hideMode:'offsets',
21437     
21438     
21439     tbContainer : false,
21440     
21441     toolbarContainer :function() {
21442         return this.wrap.select('.x-html-editor-tb',true).first();
21443     },
21444
21445     /**
21446      * Protected method that will not generally be called directly. It
21447      * is called when the editor creates its toolbar. Override this method if you need to
21448      * add custom toolbar buttons.
21449      * @param {HtmlEditor} editor
21450      */
21451     createToolbar : function(){
21452         
21453         Roo.log("create toolbars");
21454         
21455         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21456         this.toolbars[0].render(this.toolbarContainer());
21457         
21458         return;
21459         
21460 //        if (!editor.toolbars || !editor.toolbars.length) {
21461 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21462 //        }
21463 //        
21464 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21465 //            editor.toolbars[i] = Roo.factory(
21466 //                    typeof(editor.toolbars[i]) == 'string' ?
21467 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21468 //                Roo.bootstrap.HtmlEditor);
21469 //            editor.toolbars[i].init(editor);
21470 //        }
21471     },
21472
21473      
21474     // private
21475     onRender : function(ct, position)
21476     {
21477        // Roo.log("Call onRender: " + this.xtype);
21478         var _t = this;
21479         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21480       
21481         this.wrap = this.inputEl().wrap({
21482             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21483         });
21484         
21485         this.editorcore.onRender(ct, position);
21486          
21487         if (this.resizable) {
21488             this.resizeEl = new Roo.Resizable(this.wrap, {
21489                 pinned : true,
21490                 wrap: true,
21491                 dynamic : true,
21492                 minHeight : this.height,
21493                 height: this.height,
21494                 handles : this.resizable,
21495                 width: this.width,
21496                 listeners : {
21497                     resize : function(r, w, h) {
21498                         _t.onResize(w,h); // -something
21499                     }
21500                 }
21501             });
21502             
21503         }
21504         this.createToolbar(this);
21505        
21506         
21507         if(!this.width && this.resizable){
21508             this.setSize(this.wrap.getSize());
21509         }
21510         if (this.resizeEl) {
21511             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21512             // should trigger onReize..
21513         }
21514         
21515     },
21516
21517     // private
21518     onResize : function(w, h)
21519     {
21520         Roo.log('resize: ' +w + ',' + h );
21521         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21522         var ew = false;
21523         var eh = false;
21524         
21525         if(this.inputEl() ){
21526             if(typeof w == 'number'){
21527                 var aw = w - this.wrap.getFrameWidth('lr');
21528                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21529                 ew = aw;
21530             }
21531             if(typeof h == 'number'){
21532                  var tbh = -11;  // fixme it needs to tool bar size!
21533                 for (var i =0; i < this.toolbars.length;i++) {
21534                     // fixme - ask toolbars for heights?
21535                     tbh += this.toolbars[i].el.getHeight();
21536                     //if (this.toolbars[i].footer) {
21537                     //    tbh += this.toolbars[i].footer.el.getHeight();
21538                     //}
21539                 }
21540               
21541                 
21542                 
21543                 
21544                 
21545                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21546                 ah -= 5; // knock a few pixes off for look..
21547                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21548                 var eh = ah;
21549             }
21550         }
21551         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21552         this.editorcore.onResize(ew,eh);
21553         
21554     },
21555
21556     /**
21557      * Toggles the editor between standard and source edit mode.
21558      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21559      */
21560     toggleSourceEdit : function(sourceEditMode)
21561     {
21562         this.editorcore.toggleSourceEdit(sourceEditMode);
21563         
21564         if(this.editorcore.sourceEditMode){
21565             Roo.log('editor - showing textarea');
21566             
21567 //            Roo.log('in');
21568 //            Roo.log(this.syncValue());
21569             this.syncValue();
21570             this.inputEl().removeClass(['hide', 'x-hidden']);
21571             this.inputEl().dom.removeAttribute('tabIndex');
21572             this.inputEl().focus();
21573         }else{
21574             Roo.log('editor - hiding textarea');
21575 //            Roo.log('out')
21576 //            Roo.log(this.pushValue()); 
21577             this.pushValue();
21578             
21579             this.inputEl().addClass(['hide', 'x-hidden']);
21580             this.inputEl().dom.setAttribute('tabIndex', -1);
21581             //this.deferFocus();
21582         }
21583          
21584         if(this.resizable){
21585             this.setSize(this.wrap.getSize());
21586         }
21587         
21588         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21589     },
21590  
21591     // private (for BoxComponent)
21592     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21593
21594     // private (for BoxComponent)
21595     getResizeEl : function(){
21596         return this.wrap;
21597     },
21598
21599     // private (for BoxComponent)
21600     getPositionEl : function(){
21601         return this.wrap;
21602     },
21603
21604     // private
21605     initEvents : function(){
21606         this.originalValue = this.getValue();
21607     },
21608
21609 //    /**
21610 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21611 //     * @method
21612 //     */
21613 //    markInvalid : Roo.emptyFn,
21614 //    /**
21615 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21616 //     * @method
21617 //     */
21618 //    clearInvalid : Roo.emptyFn,
21619
21620     setValue : function(v){
21621         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21622         this.editorcore.pushValue();
21623     },
21624
21625      
21626     // private
21627     deferFocus : function(){
21628         this.focus.defer(10, this);
21629     },
21630
21631     // doc'ed in Field
21632     focus : function(){
21633         this.editorcore.focus();
21634         
21635     },
21636       
21637
21638     // private
21639     onDestroy : function(){
21640         
21641         
21642         
21643         if(this.rendered){
21644             
21645             for (var i =0; i < this.toolbars.length;i++) {
21646                 // fixme - ask toolbars for heights?
21647                 this.toolbars[i].onDestroy();
21648             }
21649             
21650             this.wrap.dom.innerHTML = '';
21651             this.wrap.remove();
21652         }
21653     },
21654
21655     // private
21656     onFirstFocus : function(){
21657         //Roo.log("onFirstFocus");
21658         this.editorcore.onFirstFocus();
21659          for (var i =0; i < this.toolbars.length;i++) {
21660             this.toolbars[i].onFirstFocus();
21661         }
21662         
21663     },
21664     
21665     // private
21666     syncValue : function()
21667     {   
21668         this.editorcore.syncValue();
21669     },
21670     
21671     pushValue : function()
21672     {   
21673         this.editorcore.pushValue();
21674     }
21675      
21676     
21677     // hide stuff that is not compatible
21678     /**
21679      * @event blur
21680      * @hide
21681      */
21682     /**
21683      * @event change
21684      * @hide
21685      */
21686     /**
21687      * @event focus
21688      * @hide
21689      */
21690     /**
21691      * @event specialkey
21692      * @hide
21693      */
21694     /**
21695      * @cfg {String} fieldClass @hide
21696      */
21697     /**
21698      * @cfg {String} focusClass @hide
21699      */
21700     /**
21701      * @cfg {String} autoCreate @hide
21702      */
21703     /**
21704      * @cfg {String} inputType @hide
21705      */
21706     /**
21707      * @cfg {String} invalidClass @hide
21708      */
21709     /**
21710      * @cfg {String} invalidText @hide
21711      */
21712     /**
21713      * @cfg {String} msgFx @hide
21714      */
21715     /**
21716      * @cfg {String} validateOnBlur @hide
21717      */
21718 });
21719  
21720     
21721    
21722    
21723    
21724       
21725 Roo.namespace('Roo.bootstrap.htmleditor');
21726 /**
21727  * @class Roo.bootstrap.HtmlEditorToolbar1
21728  * Basic Toolbar
21729  * 
21730  * Usage:
21731  *
21732  new Roo.bootstrap.HtmlEditor({
21733     ....
21734     toolbars : [
21735         new Roo.bootstrap.HtmlEditorToolbar1({
21736             disable : { fonts: 1 , format: 1, ..., ... , ...],
21737             btns : [ .... ]
21738         })
21739     }
21740      
21741  * 
21742  * @cfg {Object} disable List of elements to disable..
21743  * @cfg {Array} btns List of additional buttons.
21744  * 
21745  * 
21746  * NEEDS Extra CSS? 
21747  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21748  */
21749  
21750 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21751 {
21752     
21753     Roo.apply(this, config);
21754     
21755     // default disabled, based on 'good practice'..
21756     this.disable = this.disable || {};
21757     Roo.applyIf(this.disable, {
21758         fontSize : true,
21759         colors : true,
21760         specialElements : true
21761     });
21762     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21763     
21764     this.editor = config.editor;
21765     this.editorcore = config.editor.editorcore;
21766     
21767     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21768     
21769     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21770     // dont call parent... till later.
21771 }
21772 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21773      
21774     bar : true,
21775     
21776     editor : false,
21777     editorcore : false,
21778     
21779     
21780     formats : [
21781         "p" ,  
21782         "h1","h2","h3","h4","h5","h6", 
21783         "pre", "code", 
21784         "abbr", "acronym", "address", "cite", "samp", "var",
21785         'div','span'
21786     ],
21787     
21788     onRender : function(ct, position)
21789     {
21790        // Roo.log("Call onRender: " + this.xtype);
21791         
21792        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21793        Roo.log(this.el);
21794        this.el.dom.style.marginBottom = '0';
21795        var _this = this;
21796        var editorcore = this.editorcore;
21797        var editor= this.editor;
21798        
21799        var children = [];
21800        var btn = function(id,cmd , toggle, handler){
21801        
21802             var  event = toggle ? 'toggle' : 'click';
21803        
21804             var a = {
21805                 size : 'sm',
21806                 xtype: 'Button',
21807                 xns: Roo.bootstrap,
21808                 glyphicon : id,
21809                 cmd : id || cmd,
21810                 enableToggle:toggle !== false,
21811                 //html : 'submit'
21812                 pressed : toggle ? false : null,
21813                 listeners : {}
21814             };
21815             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21816                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21817             };
21818             children.push(a);
21819             return a;
21820        }
21821         
21822         var style = {
21823                 xtype: 'Button',
21824                 size : 'sm',
21825                 xns: Roo.bootstrap,
21826                 glyphicon : 'font',
21827                 //html : 'submit'
21828                 menu : {
21829                     xtype: 'Menu',
21830                     xns: Roo.bootstrap,
21831                     items:  []
21832                 }
21833         };
21834         Roo.each(this.formats, function(f) {
21835             style.menu.items.push({
21836                 xtype :'MenuItem',
21837                 xns: Roo.bootstrap,
21838                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21839                 tagname : f,
21840                 listeners : {
21841                     click : function()
21842                     {
21843                         editorcore.insertTag(this.tagname);
21844                         editor.focus();
21845                     }
21846                 }
21847                 
21848             });
21849         });
21850          children.push(style);   
21851             
21852             
21853         btn('bold',false,true);
21854         btn('italic',false,true);
21855         btn('align-left', 'justifyleft',true);
21856         btn('align-center', 'justifycenter',true);
21857         btn('align-right' , 'justifyright',true);
21858         btn('link', false, false, function(btn) {
21859             //Roo.log("create link?");
21860             var url = prompt(this.createLinkText, this.defaultLinkValue);
21861             if(url && url != 'http:/'+'/'){
21862                 this.editorcore.relayCmd('createlink', url);
21863             }
21864         }),
21865         btn('list','insertunorderedlist',true);
21866         btn('pencil', false,true, function(btn){
21867                 Roo.log(this);
21868                 
21869                 this.toggleSourceEdit(btn.pressed);
21870         });
21871         /*
21872         var cog = {
21873                 xtype: 'Button',
21874                 size : 'sm',
21875                 xns: Roo.bootstrap,
21876                 glyphicon : 'cog',
21877                 //html : 'submit'
21878                 menu : {
21879                     xtype: 'Menu',
21880                     xns: Roo.bootstrap,
21881                     items:  []
21882                 }
21883         };
21884         
21885         cog.menu.items.push({
21886             xtype :'MenuItem',
21887             xns: Roo.bootstrap,
21888             html : Clean styles,
21889             tagname : f,
21890             listeners : {
21891                 click : function()
21892                 {
21893                     editorcore.insertTag(this.tagname);
21894                     editor.focus();
21895                 }
21896             }
21897             
21898         });
21899        */
21900         
21901          
21902        this.xtype = 'NavSimplebar';
21903         
21904         for(var i=0;i< children.length;i++) {
21905             
21906             this.buttons.add(this.addxtypeChild(children[i]));
21907             
21908         }
21909         
21910         editor.on('editorevent', this.updateToolbar, this);
21911     },
21912     onBtnClick : function(id)
21913     {
21914        this.editorcore.relayCmd(id);
21915        this.editorcore.focus();
21916     },
21917     
21918     /**
21919      * Protected method that will not generally be called directly. It triggers
21920      * a toolbar update by reading the markup state of the current selection in the editor.
21921      */
21922     updateToolbar: function(){
21923
21924         if(!this.editorcore.activated){
21925             this.editor.onFirstFocus(); // is this neeed?
21926             return;
21927         }
21928
21929         var btns = this.buttons; 
21930         var doc = this.editorcore.doc;
21931         btns.get('bold').setActive(doc.queryCommandState('bold'));
21932         btns.get('italic').setActive(doc.queryCommandState('italic'));
21933         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21934         
21935         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21936         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21937         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21938         
21939         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21940         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21941          /*
21942         
21943         var ans = this.editorcore.getAllAncestors();
21944         if (this.formatCombo) {
21945             
21946             
21947             var store = this.formatCombo.store;
21948             this.formatCombo.setValue("");
21949             for (var i =0; i < ans.length;i++) {
21950                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21951                     // select it..
21952                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21953                     break;
21954                 }
21955             }
21956         }
21957         
21958         
21959         
21960         // hides menus... - so this cant be on a menu...
21961         Roo.bootstrap.MenuMgr.hideAll();
21962         */
21963         Roo.bootstrap.MenuMgr.hideAll();
21964         //this.editorsyncValue();
21965     },
21966     onFirstFocus: function() {
21967         this.buttons.each(function(item){
21968            item.enable();
21969         });
21970     },
21971     toggleSourceEdit : function(sourceEditMode){
21972         
21973           
21974         if(sourceEditMode){
21975             Roo.log("disabling buttons");
21976            this.buttons.each( function(item){
21977                 if(item.cmd != 'pencil'){
21978                     item.disable();
21979                 }
21980             });
21981           
21982         }else{
21983             Roo.log("enabling buttons");
21984             if(this.editorcore.initialized){
21985                 this.buttons.each( function(item){
21986                     item.enable();
21987                 });
21988             }
21989             
21990         }
21991         Roo.log("calling toggole on editor");
21992         // tell the editor that it's been pressed..
21993         this.editor.toggleSourceEdit(sourceEditMode);
21994        
21995     }
21996 });
21997
21998
21999
22000
22001
22002 /**
22003  * @class Roo.bootstrap.Table.AbstractSelectionModel
22004  * @extends Roo.util.Observable
22005  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22006  * implemented by descendant classes.  This class should not be directly instantiated.
22007  * @constructor
22008  */
22009 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22010     this.locked = false;
22011     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22012 };
22013
22014
22015 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22016     /** @ignore Called by the grid automatically. Do not call directly. */
22017     init : function(grid){
22018         this.grid = grid;
22019         this.initEvents();
22020     },
22021
22022     /**
22023      * Locks the selections.
22024      */
22025     lock : function(){
22026         this.locked = true;
22027     },
22028
22029     /**
22030      * Unlocks the selections.
22031      */
22032     unlock : function(){
22033         this.locked = false;
22034     },
22035
22036     /**
22037      * Returns true if the selections are locked.
22038      * @return {Boolean}
22039      */
22040     isLocked : function(){
22041         return this.locked;
22042     }
22043 });
22044 /**
22045  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22046  * @class Roo.bootstrap.Table.RowSelectionModel
22047  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22048  * It supports multiple selections and keyboard selection/navigation. 
22049  * @constructor
22050  * @param {Object} config
22051  */
22052
22053 Roo.bootstrap.Table.RowSelectionModel = function(config){
22054     Roo.apply(this, config);
22055     this.selections = new Roo.util.MixedCollection(false, function(o){
22056         return o.id;
22057     });
22058
22059     this.last = false;
22060     this.lastActive = false;
22061
22062     this.addEvents({
22063         /**
22064              * @event selectionchange
22065              * Fires when the selection changes
22066              * @param {SelectionModel} this
22067              */
22068             "selectionchange" : true,
22069         /**
22070              * @event afterselectionchange
22071              * Fires after the selection changes (eg. by key press or clicking)
22072              * @param {SelectionModel} this
22073              */
22074             "afterselectionchange" : true,
22075         /**
22076              * @event beforerowselect
22077              * Fires when a row is selected being selected, return false to cancel.
22078              * @param {SelectionModel} this
22079              * @param {Number} rowIndex The selected index
22080              * @param {Boolean} keepExisting False if other selections will be cleared
22081              */
22082             "beforerowselect" : true,
22083         /**
22084              * @event rowselect
22085              * Fires when a row is selected.
22086              * @param {SelectionModel} this
22087              * @param {Number} rowIndex The selected index
22088              * @param {Roo.data.Record} r The record
22089              */
22090             "rowselect" : true,
22091         /**
22092              * @event rowdeselect
22093              * Fires when a row is deselected.
22094              * @param {SelectionModel} this
22095              * @param {Number} rowIndex The selected index
22096              */
22097         "rowdeselect" : true
22098     });
22099     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22100     this.locked = false;
22101 };
22102
22103 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22104     /**
22105      * @cfg {Boolean} singleSelect
22106      * True to allow selection of only one row at a time (defaults to false)
22107      */
22108     singleSelect : false,
22109
22110     // private
22111     initEvents : function(){
22112
22113         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22114             this.grid.on("mousedown", this.handleMouseDown, this);
22115         }else{ // allow click to work like normal
22116             this.grid.on("rowclick", this.handleDragableRowClick, this);
22117         }
22118
22119         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22120             "up" : function(e){
22121                 if(!e.shiftKey){
22122                     this.selectPrevious(e.shiftKey);
22123                 }else if(this.last !== false && this.lastActive !== false){
22124                     var last = this.last;
22125                     this.selectRange(this.last,  this.lastActive-1);
22126                     this.grid.getView().focusRow(this.lastActive);
22127                     if(last !== false){
22128                         this.last = last;
22129                     }
22130                 }else{
22131                     this.selectFirstRow();
22132                 }
22133                 this.fireEvent("afterselectionchange", this);
22134             },
22135             "down" : function(e){
22136                 if(!e.shiftKey){
22137                     this.selectNext(e.shiftKey);
22138                 }else if(this.last !== false && this.lastActive !== false){
22139                     var last = this.last;
22140                     this.selectRange(this.last,  this.lastActive+1);
22141                     this.grid.getView().focusRow(this.lastActive);
22142                     if(last !== false){
22143                         this.last = last;
22144                     }
22145                 }else{
22146                     this.selectFirstRow();
22147                 }
22148                 this.fireEvent("afterselectionchange", this);
22149             },
22150             scope: this
22151         });
22152
22153         var view = this.grid.view;
22154         view.on("refresh", this.onRefresh, this);
22155         view.on("rowupdated", this.onRowUpdated, this);
22156         view.on("rowremoved", this.onRemove, this);
22157     },
22158
22159     // private
22160     onRefresh : function(){
22161         var ds = this.grid.dataSource, i, v = this.grid.view;
22162         var s = this.selections;
22163         s.each(function(r){
22164             if((i = ds.indexOfId(r.id)) != -1){
22165                 v.onRowSelect(i);
22166             }else{
22167                 s.remove(r);
22168             }
22169         });
22170     },
22171
22172     // private
22173     onRemove : function(v, index, r){
22174         this.selections.remove(r);
22175     },
22176
22177     // private
22178     onRowUpdated : function(v, index, r){
22179         if(this.isSelected(r)){
22180             v.onRowSelect(index);
22181         }
22182     },
22183
22184     /**
22185      * Select records.
22186      * @param {Array} records The records to select
22187      * @param {Boolean} keepExisting (optional) True to keep existing selections
22188      */
22189     selectRecords : function(records, keepExisting){
22190         if(!keepExisting){
22191             this.clearSelections();
22192         }
22193         var ds = this.grid.dataSource;
22194         for(var i = 0, len = records.length; i < len; i++){
22195             this.selectRow(ds.indexOf(records[i]), true);
22196         }
22197     },
22198
22199     /**
22200      * Gets the number of selected rows.
22201      * @return {Number}
22202      */
22203     getCount : function(){
22204         return this.selections.length;
22205     },
22206
22207     /**
22208      * Selects the first row in the grid.
22209      */
22210     selectFirstRow : function(){
22211         this.selectRow(0);
22212     },
22213
22214     /**
22215      * Select the last row.
22216      * @param {Boolean} keepExisting (optional) True to keep existing selections
22217      */
22218     selectLastRow : function(keepExisting){
22219         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22220     },
22221
22222     /**
22223      * Selects the row immediately following the last selected row.
22224      * @param {Boolean} keepExisting (optional) True to keep existing selections
22225      */
22226     selectNext : function(keepExisting){
22227         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22228             this.selectRow(this.last+1, keepExisting);
22229             this.grid.getView().focusRow(this.last);
22230         }
22231     },
22232
22233     /**
22234      * Selects the row that precedes the last selected row.
22235      * @param {Boolean} keepExisting (optional) True to keep existing selections
22236      */
22237     selectPrevious : function(keepExisting){
22238         if(this.last){
22239             this.selectRow(this.last-1, keepExisting);
22240             this.grid.getView().focusRow(this.last);
22241         }
22242     },
22243
22244     /**
22245      * Returns the selected records
22246      * @return {Array} Array of selected records
22247      */
22248     getSelections : function(){
22249         return [].concat(this.selections.items);
22250     },
22251
22252     /**
22253      * Returns the first selected record.
22254      * @return {Record}
22255      */
22256     getSelected : function(){
22257         return this.selections.itemAt(0);
22258     },
22259
22260
22261     /**
22262      * Clears all selections.
22263      */
22264     clearSelections : function(fast){
22265         if(this.locked) {
22266             return;
22267         }
22268         if(fast !== true){
22269             var ds = this.grid.dataSource;
22270             var s = this.selections;
22271             s.each(function(r){
22272                 this.deselectRow(ds.indexOfId(r.id));
22273             }, this);
22274             s.clear();
22275         }else{
22276             this.selections.clear();
22277         }
22278         this.last = false;
22279     },
22280
22281
22282     /**
22283      * Selects all rows.
22284      */
22285     selectAll : function(){
22286         if(this.locked) {
22287             return;
22288         }
22289         this.selections.clear();
22290         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22291             this.selectRow(i, true);
22292         }
22293     },
22294
22295     /**
22296      * Returns True if there is a selection.
22297      * @return {Boolean}
22298      */
22299     hasSelection : function(){
22300         return this.selections.length > 0;
22301     },
22302
22303     /**
22304      * Returns True if the specified row is selected.
22305      * @param {Number/Record} record The record or index of the record to check
22306      * @return {Boolean}
22307      */
22308     isSelected : function(index){
22309         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22310         return (r && this.selections.key(r.id) ? true : false);
22311     },
22312
22313     /**
22314      * Returns True if the specified record id is selected.
22315      * @param {String} id The id of record to check
22316      * @return {Boolean}
22317      */
22318     isIdSelected : function(id){
22319         return (this.selections.key(id) ? true : false);
22320     },
22321
22322     // private
22323     handleMouseDown : function(e, t){
22324         var view = this.grid.getView(), rowIndex;
22325         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22326             return;
22327         };
22328         if(e.shiftKey && this.last !== false){
22329             var last = this.last;
22330             this.selectRange(last, rowIndex, e.ctrlKey);
22331             this.last = last; // reset the last
22332             view.focusRow(rowIndex);
22333         }else{
22334             var isSelected = this.isSelected(rowIndex);
22335             if(e.button !== 0 && isSelected){
22336                 view.focusRow(rowIndex);
22337             }else if(e.ctrlKey && isSelected){
22338                 this.deselectRow(rowIndex);
22339             }else if(!isSelected){
22340                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22341                 view.focusRow(rowIndex);
22342             }
22343         }
22344         this.fireEvent("afterselectionchange", this);
22345     },
22346     // private
22347     handleDragableRowClick :  function(grid, rowIndex, e) 
22348     {
22349         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22350             this.selectRow(rowIndex, false);
22351             grid.view.focusRow(rowIndex);
22352              this.fireEvent("afterselectionchange", this);
22353         }
22354     },
22355     
22356     /**
22357      * Selects multiple rows.
22358      * @param {Array} rows Array of the indexes of the row to select
22359      * @param {Boolean} keepExisting (optional) True to keep existing selections
22360      */
22361     selectRows : function(rows, keepExisting){
22362         if(!keepExisting){
22363             this.clearSelections();
22364         }
22365         for(var i = 0, len = rows.length; i < len; i++){
22366             this.selectRow(rows[i], true);
22367         }
22368     },
22369
22370     /**
22371      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22372      * @param {Number} startRow The index of the first row in the range
22373      * @param {Number} endRow The index of the last row in the range
22374      * @param {Boolean} keepExisting (optional) True to retain existing selections
22375      */
22376     selectRange : function(startRow, endRow, keepExisting){
22377         if(this.locked) {
22378             return;
22379         }
22380         if(!keepExisting){
22381             this.clearSelections();
22382         }
22383         if(startRow <= endRow){
22384             for(var i = startRow; i <= endRow; i++){
22385                 this.selectRow(i, true);
22386             }
22387         }else{
22388             for(var i = startRow; i >= endRow; i--){
22389                 this.selectRow(i, true);
22390             }
22391         }
22392     },
22393
22394     /**
22395      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22396      * @param {Number} startRow The index of the first row in the range
22397      * @param {Number} endRow The index of the last row in the range
22398      */
22399     deselectRange : function(startRow, endRow, preventViewNotify){
22400         if(this.locked) {
22401             return;
22402         }
22403         for(var i = startRow; i <= endRow; i++){
22404             this.deselectRow(i, preventViewNotify);
22405         }
22406     },
22407
22408     /**
22409      * Selects a row.
22410      * @param {Number} row The index of the row to select
22411      * @param {Boolean} keepExisting (optional) True to keep existing selections
22412      */
22413     selectRow : function(index, keepExisting, preventViewNotify){
22414         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22415             return;
22416         }
22417         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22418             if(!keepExisting || this.singleSelect){
22419                 this.clearSelections();
22420             }
22421             var r = this.grid.dataSource.getAt(index);
22422             this.selections.add(r);
22423             this.last = this.lastActive = index;
22424             if(!preventViewNotify){
22425                 this.grid.getView().onRowSelect(index);
22426             }
22427             this.fireEvent("rowselect", this, index, r);
22428             this.fireEvent("selectionchange", this);
22429         }
22430     },
22431
22432     /**
22433      * Deselects a row.
22434      * @param {Number} row The index of the row to deselect
22435      */
22436     deselectRow : function(index, preventViewNotify){
22437         if(this.locked) {
22438             return;
22439         }
22440         if(this.last == index){
22441             this.last = false;
22442         }
22443         if(this.lastActive == index){
22444             this.lastActive = false;
22445         }
22446         var r = this.grid.dataSource.getAt(index);
22447         this.selections.remove(r);
22448         if(!preventViewNotify){
22449             this.grid.getView().onRowDeselect(index);
22450         }
22451         this.fireEvent("rowdeselect", this, index);
22452         this.fireEvent("selectionchange", this);
22453     },
22454
22455     // private
22456     restoreLast : function(){
22457         if(this._last){
22458             this.last = this._last;
22459         }
22460     },
22461
22462     // private
22463     acceptsNav : function(row, col, cm){
22464         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22465     },
22466
22467     // private
22468     onEditorKey : function(field, e){
22469         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22470         if(k == e.TAB){
22471             e.stopEvent();
22472             ed.completeEdit();
22473             if(e.shiftKey){
22474                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22475             }else{
22476                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22477             }
22478         }else if(k == e.ENTER && !e.ctrlKey){
22479             e.stopEvent();
22480             ed.completeEdit();
22481             if(e.shiftKey){
22482                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22483             }else{
22484                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22485             }
22486         }else if(k == e.ESC){
22487             ed.cancelEdit();
22488         }
22489         if(newCell){
22490             g.startEditing(newCell[0], newCell[1]);
22491         }
22492     }
22493 });/*
22494  * Based on:
22495  * Ext JS Library 1.1.1
22496  * Copyright(c) 2006-2007, Ext JS, LLC.
22497  *
22498  * Originally Released Under LGPL - original licence link has changed is not relivant.
22499  *
22500  * Fork - LGPL
22501  * <script type="text/javascript">
22502  */
22503  
22504 /**
22505  * @class Roo.bootstrap.PagingToolbar
22506  * @extends Roo.bootstrap.NavSimplebar
22507  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22508  * @constructor
22509  * Create a new PagingToolbar
22510  * @param {Object} config The config object
22511  * @param {Roo.data.Store} store
22512  */
22513 Roo.bootstrap.PagingToolbar = function(config)
22514 {
22515     // old args format still supported... - xtype is prefered..
22516         // created from xtype...
22517     
22518     this.ds = config.dataSource;
22519     
22520     if (config.store && !this.ds) {
22521         this.store= Roo.factory(config.store, Roo.data);
22522         this.ds = this.store;
22523         this.ds.xmodule = this.xmodule || false;
22524     }
22525     
22526     this.toolbarItems = [];
22527     if (config.items) {
22528         this.toolbarItems = config.items;
22529     }
22530     
22531     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22532     
22533     this.cursor = 0;
22534     
22535     if (this.ds) { 
22536         this.bind(this.ds);
22537     }
22538     
22539     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22540     
22541 };
22542
22543 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22544     /**
22545      * @cfg {Roo.data.Store} dataSource
22546      * The underlying data store providing the paged data
22547      */
22548     /**
22549      * @cfg {String/HTMLElement/Element} container
22550      * container The id or element that will contain the toolbar
22551      */
22552     /**
22553      * @cfg {Boolean} displayInfo
22554      * True to display the displayMsg (defaults to false)
22555      */
22556     /**
22557      * @cfg {Number} pageSize
22558      * The number of records to display per page (defaults to 20)
22559      */
22560     pageSize: 20,
22561     /**
22562      * @cfg {String} displayMsg
22563      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22564      */
22565     displayMsg : 'Displaying {0} - {1} of {2}',
22566     /**
22567      * @cfg {String} emptyMsg
22568      * The message to display when no records are found (defaults to "No data to display")
22569      */
22570     emptyMsg : 'No data to display',
22571     /**
22572      * Customizable piece of the default paging text (defaults to "Page")
22573      * @type String
22574      */
22575     beforePageText : "Page",
22576     /**
22577      * Customizable piece of the default paging text (defaults to "of %0")
22578      * @type String
22579      */
22580     afterPageText : "of {0}",
22581     /**
22582      * Customizable piece of the default paging text (defaults to "First Page")
22583      * @type String
22584      */
22585     firstText : "First Page",
22586     /**
22587      * Customizable piece of the default paging text (defaults to "Previous Page")
22588      * @type String
22589      */
22590     prevText : "Previous Page",
22591     /**
22592      * Customizable piece of the default paging text (defaults to "Next Page")
22593      * @type String
22594      */
22595     nextText : "Next Page",
22596     /**
22597      * Customizable piece of the default paging text (defaults to "Last Page")
22598      * @type String
22599      */
22600     lastText : "Last Page",
22601     /**
22602      * Customizable piece of the default paging text (defaults to "Refresh")
22603      * @type String
22604      */
22605     refreshText : "Refresh",
22606
22607     buttons : false,
22608     // private
22609     onRender : function(ct, position) 
22610     {
22611         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22612         this.navgroup.parentId = this.id;
22613         this.navgroup.onRender(this.el, null);
22614         // add the buttons to the navgroup
22615         
22616         if(this.displayInfo){
22617             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22618             this.displayEl = this.el.select('.x-paging-info', true).first();
22619 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22620 //            this.displayEl = navel.el.select('span',true).first();
22621         }
22622         
22623         var _this = this;
22624         
22625         if(this.buttons){
22626             Roo.each(_this.buttons, function(e){ // this might need to use render????
22627                Roo.factory(e).onRender(_this.el, null);
22628             });
22629         }
22630             
22631         Roo.each(_this.toolbarItems, function(e) {
22632             _this.navgroup.addItem(e);
22633         });
22634         
22635         
22636         this.first = this.navgroup.addItem({
22637             tooltip: this.firstText,
22638             cls: "prev",
22639             icon : 'fa fa-backward',
22640             disabled: true,
22641             preventDefault: true,
22642             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22643         });
22644         
22645         this.prev =  this.navgroup.addItem({
22646             tooltip: this.prevText,
22647             cls: "prev",
22648             icon : 'fa fa-step-backward',
22649             disabled: true,
22650             preventDefault: true,
22651             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22652         });
22653     //this.addSeparator();
22654         
22655         
22656         var field = this.navgroup.addItem( {
22657             tagtype : 'span',
22658             cls : 'x-paging-position',
22659             
22660             html : this.beforePageText  +
22661                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22662                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22663          } ); //?? escaped?
22664         
22665         this.field = field.el.select('input', true).first();
22666         this.field.on("keydown", this.onPagingKeydown, this);
22667         this.field.on("focus", function(){this.dom.select();});
22668     
22669     
22670         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22671         //this.field.setHeight(18);
22672         //this.addSeparator();
22673         this.next = this.navgroup.addItem({
22674             tooltip: this.nextText,
22675             cls: "next",
22676             html : ' <i class="fa fa-step-forward">',
22677             disabled: true,
22678             preventDefault: true,
22679             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22680         });
22681         this.last = this.navgroup.addItem({
22682             tooltip: this.lastText,
22683             icon : 'fa fa-forward',
22684             cls: "next",
22685             disabled: true,
22686             preventDefault: true,
22687             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22688         });
22689     //this.addSeparator();
22690         this.loading = this.navgroup.addItem({
22691             tooltip: this.refreshText,
22692             icon: 'fa fa-refresh',
22693             preventDefault: true,
22694             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22695         });
22696         
22697     },
22698
22699     // private
22700     updateInfo : function(){
22701         if(this.displayEl){
22702             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22703             var msg = count == 0 ?
22704                 this.emptyMsg :
22705                 String.format(
22706                     this.displayMsg,
22707                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22708                 );
22709             this.displayEl.update(msg);
22710         }
22711     },
22712
22713     // private
22714     onLoad : function(ds, r, o){
22715        this.cursor = o.params ? o.params.start : 0;
22716        var d = this.getPageData(),
22717             ap = d.activePage,
22718             ps = d.pages;
22719         
22720        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22721        this.field.dom.value = ap;
22722        this.first.setDisabled(ap == 1);
22723        this.prev.setDisabled(ap == 1);
22724        this.next.setDisabled(ap == ps);
22725        this.last.setDisabled(ap == ps);
22726        this.loading.enable();
22727        this.updateInfo();
22728     },
22729
22730     // private
22731     getPageData : function(){
22732         var total = this.ds.getTotalCount();
22733         return {
22734             total : total,
22735             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22736             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22737         };
22738     },
22739
22740     // private
22741     onLoadError : function(){
22742         this.loading.enable();
22743     },
22744
22745     // private
22746     onPagingKeydown : function(e){
22747         var k = e.getKey();
22748         var d = this.getPageData();
22749         if(k == e.RETURN){
22750             var v = this.field.dom.value, pageNum;
22751             if(!v || isNaN(pageNum = parseInt(v, 10))){
22752                 this.field.dom.value = d.activePage;
22753                 return;
22754             }
22755             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22756             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22757             e.stopEvent();
22758         }
22759         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))
22760         {
22761           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22762           this.field.dom.value = pageNum;
22763           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22764           e.stopEvent();
22765         }
22766         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22767         {
22768           var v = this.field.dom.value, pageNum; 
22769           var increment = (e.shiftKey) ? 10 : 1;
22770           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22771                 increment *= -1;
22772           }
22773           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22774             this.field.dom.value = d.activePage;
22775             return;
22776           }
22777           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22778           {
22779             this.field.dom.value = parseInt(v, 10) + increment;
22780             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22781             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22782           }
22783           e.stopEvent();
22784         }
22785     },
22786
22787     // private
22788     beforeLoad : function(){
22789         if(this.loading){
22790             this.loading.disable();
22791         }
22792     },
22793
22794     // private
22795     onClick : function(which){
22796         
22797         var ds = this.ds;
22798         if (!ds) {
22799             return;
22800         }
22801         
22802         switch(which){
22803             case "first":
22804                 ds.load({params:{start: 0, limit: this.pageSize}});
22805             break;
22806             case "prev":
22807                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22808             break;
22809             case "next":
22810                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22811             break;
22812             case "last":
22813                 var total = ds.getTotalCount();
22814                 var extra = total % this.pageSize;
22815                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22816                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22817             break;
22818             case "refresh":
22819                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22820             break;
22821         }
22822     },
22823
22824     /**
22825      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22826      * @param {Roo.data.Store} store The data store to unbind
22827      */
22828     unbind : function(ds){
22829         ds.un("beforeload", this.beforeLoad, this);
22830         ds.un("load", this.onLoad, this);
22831         ds.un("loadexception", this.onLoadError, this);
22832         ds.un("remove", this.updateInfo, this);
22833         ds.un("add", this.updateInfo, this);
22834         this.ds = undefined;
22835     },
22836
22837     /**
22838      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22839      * @param {Roo.data.Store} store The data store to bind
22840      */
22841     bind : function(ds){
22842         ds.on("beforeload", this.beforeLoad, this);
22843         ds.on("load", this.onLoad, this);
22844         ds.on("loadexception", this.onLoadError, this);
22845         ds.on("remove", this.updateInfo, this);
22846         ds.on("add", this.updateInfo, this);
22847         this.ds = ds;
22848     }
22849 });/*
22850  * - LGPL
22851  *
22852  * element
22853  * 
22854  */
22855
22856 /**
22857  * @class Roo.bootstrap.MessageBar
22858  * @extends Roo.bootstrap.Component
22859  * Bootstrap MessageBar class
22860  * @cfg {String} html contents of the MessageBar
22861  * @cfg {String} weight (info | success | warning | danger) default info
22862  * @cfg {String} beforeClass insert the bar before the given class
22863  * @cfg {Boolean} closable (true | false) default false
22864  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22865  * 
22866  * @constructor
22867  * Create a new Element
22868  * @param {Object} config The config object
22869  */
22870
22871 Roo.bootstrap.MessageBar = function(config){
22872     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22873 };
22874
22875 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22876     
22877     html: '',
22878     weight: 'info',
22879     closable: false,
22880     fixed: false,
22881     beforeClass: 'bootstrap-sticky-wrap',
22882     
22883     getAutoCreate : function(){
22884         
22885         var cfg = {
22886             tag: 'div',
22887             cls: 'alert alert-dismissable alert-' + this.weight,
22888             cn: [
22889                 {
22890                     tag: 'span',
22891                     cls: 'message',
22892                     html: this.html || ''
22893                 }
22894             ]
22895         };
22896         
22897         if(this.fixed){
22898             cfg.cls += ' alert-messages-fixed';
22899         }
22900         
22901         if(this.closable){
22902             cfg.cn.push({
22903                 tag: 'button',
22904                 cls: 'close',
22905                 html: 'x'
22906             });
22907         }
22908         
22909         return cfg;
22910     },
22911     
22912     onRender : function(ct, position)
22913     {
22914         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22915         
22916         if(!this.el){
22917             var cfg = Roo.apply({},  this.getAutoCreate());
22918             cfg.id = Roo.id();
22919             
22920             if (this.cls) {
22921                 cfg.cls += ' ' + this.cls;
22922             }
22923             if (this.style) {
22924                 cfg.style = this.style;
22925             }
22926             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22927             
22928             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22929         }
22930         
22931         this.el.select('>button.close').on('click', this.hide, this);
22932         
22933     },
22934     
22935     show : function()
22936     {
22937         if (!this.rendered) {
22938             this.render();
22939         }
22940         
22941         this.el.show();
22942         
22943         this.fireEvent('show', this);
22944         
22945     },
22946     
22947     hide : function()
22948     {
22949         if (!this.rendered) {
22950             this.render();
22951         }
22952         
22953         this.el.hide();
22954         
22955         this.fireEvent('hide', this);
22956     },
22957     
22958     update : function()
22959     {
22960 //        var e = this.el.dom.firstChild;
22961 //        
22962 //        if(this.closable){
22963 //            e = e.nextSibling;
22964 //        }
22965 //        
22966 //        e.data = this.html || '';
22967
22968         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22969     }
22970    
22971 });
22972
22973  
22974
22975      /*
22976  * - LGPL
22977  *
22978  * Graph
22979  * 
22980  */
22981
22982
22983 /**
22984  * @class Roo.bootstrap.Graph
22985  * @extends Roo.bootstrap.Component
22986  * Bootstrap Graph class
22987 > Prameters
22988  -sm {number} sm 4
22989  -md {number} md 5
22990  @cfg {String} graphtype  bar | vbar | pie
22991  @cfg {number} g_x coodinator | centre x (pie)
22992  @cfg {number} g_y coodinator | centre y (pie)
22993  @cfg {number} g_r radius (pie)
22994  @cfg {number} g_height height of the chart (respected by all elements in the set)
22995  @cfg {number} g_width width of the chart (respected by all elements in the set)
22996  @cfg {Object} title The title of the chart
22997     
22998  -{Array}  values
22999  -opts (object) options for the chart 
23000      o {
23001      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23002      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23003      o vgutter (number)
23004      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.
23005      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23006      o to
23007      o stretch (boolean)
23008      o }
23009  -opts (object) options for the pie
23010      o{
23011      o cut
23012      o startAngle (number)
23013      o endAngle (number)
23014      } 
23015  *
23016  * @constructor
23017  * Create a new Input
23018  * @param {Object} config The config object
23019  */
23020
23021 Roo.bootstrap.Graph = function(config){
23022     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23023     
23024     this.addEvents({
23025         // img events
23026         /**
23027          * @event click
23028          * The img click event for the img.
23029          * @param {Roo.EventObject} e
23030          */
23031         "click" : true
23032     });
23033 };
23034
23035 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23036     
23037     sm: 4,
23038     md: 5,
23039     graphtype: 'bar',
23040     g_height: 250,
23041     g_width: 400,
23042     g_x: 50,
23043     g_y: 50,
23044     g_r: 30,
23045     opts:{
23046         //g_colors: this.colors,
23047         g_type: 'soft',
23048         g_gutter: '20%'
23049
23050     },
23051     title : false,
23052
23053     getAutoCreate : function(){
23054         
23055         var cfg = {
23056             tag: 'div',
23057             html : null
23058         };
23059         
23060         
23061         return  cfg;
23062     },
23063
23064     onRender : function(ct,position){
23065         
23066         
23067         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23068         
23069         if (typeof(Raphael) == 'undefined') {
23070             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23071             return;
23072         }
23073         
23074         this.raphael = Raphael(this.el.dom);
23075         
23076                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23077                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23078                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23079                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23080                 /*
23081                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23082                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23083                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23084                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23085                 
23086                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23087                 r.barchart(330, 10, 300, 220, data1);
23088                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23089                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23090                 */
23091                 
23092                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23093                 // r.barchart(30, 30, 560, 250,  xdata, {
23094                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23095                 //     axis : "0 0 1 1",
23096                 //     axisxlabels :  xdata
23097                 //     //yvalues : cols,
23098                    
23099                 // });
23100 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23101 //        
23102 //        this.load(null,xdata,{
23103 //                axis : "0 0 1 1",
23104 //                axisxlabels :  xdata
23105 //                });
23106
23107     },
23108
23109     load : function(graphtype,xdata,opts)
23110     {
23111         this.raphael.clear();
23112         if(!graphtype) {
23113             graphtype = this.graphtype;
23114         }
23115         if(!opts){
23116             opts = this.opts;
23117         }
23118         var r = this.raphael,
23119             fin = function () {
23120                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23121             },
23122             fout = function () {
23123                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23124             },
23125             pfin = function() {
23126                 this.sector.stop();
23127                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23128
23129                 if (this.label) {
23130                     this.label[0].stop();
23131                     this.label[0].attr({ r: 7.5 });
23132                     this.label[1].attr({ "font-weight": 800 });
23133                 }
23134             },
23135             pfout = function() {
23136                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23137
23138                 if (this.label) {
23139                     this.label[0].animate({ r: 5 }, 500, "bounce");
23140                     this.label[1].attr({ "font-weight": 400 });
23141                 }
23142             };
23143
23144         switch(graphtype){
23145             case 'bar':
23146                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23147                 break;
23148             case 'hbar':
23149                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23150                 break;
23151             case 'pie':
23152 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23153 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23154 //            
23155                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23156                 
23157                 break;
23158
23159         }
23160         
23161         if(this.title){
23162             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23163         }
23164         
23165     },
23166     
23167     setTitle: function(o)
23168     {
23169         this.title = o;
23170     },
23171     
23172     initEvents: function() {
23173         
23174         if(!this.href){
23175             this.el.on('click', this.onClick, this);
23176         }
23177     },
23178     
23179     onClick : function(e)
23180     {
23181         Roo.log('img onclick');
23182         this.fireEvent('click', this, e);
23183     }
23184    
23185 });
23186
23187  
23188 /*
23189  * - LGPL
23190  *
23191  * numberBox
23192  * 
23193  */
23194 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23195
23196 /**
23197  * @class Roo.bootstrap.dash.NumberBox
23198  * @extends Roo.bootstrap.Component
23199  * Bootstrap NumberBox class
23200  * @cfg {String} headline Box headline
23201  * @cfg {String} content Box content
23202  * @cfg {String} icon Box icon
23203  * @cfg {String} footer Footer text
23204  * @cfg {String} fhref Footer href
23205  * 
23206  * @constructor
23207  * Create a new NumberBox
23208  * @param {Object} config The config object
23209  */
23210
23211
23212 Roo.bootstrap.dash.NumberBox = function(config){
23213     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23214     
23215 };
23216
23217 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23218     
23219     headline : '',
23220     content : '',
23221     icon : '',
23222     footer : '',
23223     fhref : '',
23224     ficon : '',
23225     
23226     getAutoCreate : function(){
23227         
23228         var cfg = {
23229             tag : 'div',
23230             cls : 'small-box ',
23231             cn : [
23232                 {
23233                     tag : 'div',
23234                     cls : 'inner',
23235                     cn :[
23236                         {
23237                             tag : 'h3',
23238                             cls : 'roo-headline',
23239                             html : this.headline
23240                         },
23241                         {
23242                             tag : 'p',
23243                             cls : 'roo-content',
23244                             html : this.content
23245                         }
23246                     ]
23247                 }
23248             ]
23249         };
23250         
23251         if(this.icon){
23252             cfg.cn.push({
23253                 tag : 'div',
23254                 cls : 'icon',
23255                 cn :[
23256                     {
23257                         tag : 'i',
23258                         cls : 'ion ' + this.icon
23259                     }
23260                 ]
23261             });
23262         }
23263         
23264         if(this.footer){
23265             var footer = {
23266                 tag : 'a',
23267                 cls : 'small-box-footer',
23268                 href : this.fhref || '#',
23269                 html : this.footer
23270             };
23271             
23272             cfg.cn.push(footer);
23273             
23274         }
23275         
23276         return  cfg;
23277     },
23278
23279     onRender : function(ct,position){
23280         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23281
23282
23283        
23284                 
23285     },
23286
23287     setHeadline: function (value)
23288     {
23289         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23290     },
23291     
23292     setFooter: function (value, href)
23293     {
23294         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23295         
23296         if(href){
23297             this.el.select('a.small-box-footer',true).first().attr('href', href);
23298         }
23299         
23300     },
23301
23302     setContent: function (value)
23303     {
23304         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23305     },
23306
23307     initEvents: function() 
23308     {   
23309         
23310     }
23311     
23312 });
23313
23314  
23315 /*
23316  * - LGPL
23317  *
23318  * TabBox
23319  * 
23320  */
23321 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23322
23323 /**
23324  * @class Roo.bootstrap.dash.TabBox
23325  * @extends Roo.bootstrap.Component
23326  * Bootstrap TabBox class
23327  * @cfg {String} title Title of the TabBox
23328  * @cfg {String} icon Icon of the TabBox
23329  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23330  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23331  * 
23332  * @constructor
23333  * Create a new TabBox
23334  * @param {Object} config The config object
23335  */
23336
23337
23338 Roo.bootstrap.dash.TabBox = function(config){
23339     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23340     this.addEvents({
23341         // raw events
23342         /**
23343          * @event addpane
23344          * When a pane is added
23345          * @param {Roo.bootstrap.dash.TabPane} pane
23346          */
23347         "addpane" : true,
23348         /**
23349          * @event activatepane
23350          * When a pane is activated
23351          * @param {Roo.bootstrap.dash.TabPane} pane
23352          */
23353         "activatepane" : true
23354         
23355          
23356     });
23357     
23358     this.panes = [];
23359 };
23360
23361 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23362
23363     title : '',
23364     icon : false,
23365     showtabs : true,
23366     tabScrollable : false,
23367     
23368     getChildContainer : function()
23369     {
23370         return this.el.select('.tab-content', true).first();
23371     },
23372     
23373     getAutoCreate : function(){
23374         
23375         var header = {
23376             tag: 'li',
23377             cls: 'pull-left header',
23378             html: this.title,
23379             cn : []
23380         };
23381         
23382         if(this.icon){
23383             header.cn.push({
23384                 tag: 'i',
23385                 cls: 'fa ' + this.icon
23386             });
23387         }
23388         
23389         var h = {
23390             tag: 'ul',
23391             cls: 'nav nav-tabs pull-right',
23392             cn: [
23393                 header
23394             ]
23395         };
23396         
23397         if(this.tabScrollable){
23398             h = {
23399                 tag: 'div',
23400                 cls: 'tab-header',
23401                 cn: [
23402                     {
23403                         tag: 'ul',
23404                         cls: 'nav nav-tabs pull-right',
23405                         cn: [
23406                             header
23407                         ]
23408                     }
23409                 ]
23410             };
23411         }
23412         
23413         var cfg = {
23414             tag: 'div',
23415             cls: 'nav-tabs-custom',
23416             cn: [
23417                 h,
23418                 {
23419                     tag: 'div',
23420                     cls: 'tab-content no-padding',
23421                     cn: []
23422                 }
23423             ]
23424         };
23425
23426         return  cfg;
23427     },
23428     initEvents : function()
23429     {
23430         //Roo.log('add add pane handler');
23431         this.on('addpane', this.onAddPane, this);
23432     },
23433      /**
23434      * Updates the box title
23435      * @param {String} html to set the title to.
23436      */
23437     setTitle : function(value)
23438     {
23439         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23440     },
23441     onAddPane : function(pane)
23442     {
23443         this.panes.push(pane);
23444         //Roo.log('addpane');
23445         //Roo.log(pane);
23446         // tabs are rendere left to right..
23447         if(!this.showtabs){
23448             return;
23449         }
23450         
23451         var ctr = this.el.select('.nav-tabs', true).first();
23452          
23453          
23454         var existing = ctr.select('.nav-tab',true);
23455         var qty = existing.getCount();;
23456         
23457         
23458         var tab = ctr.createChild({
23459             tag : 'li',
23460             cls : 'nav-tab' + (qty ? '' : ' active'),
23461             cn : [
23462                 {
23463                     tag : 'a',
23464                     href:'#',
23465                     html : pane.title
23466                 }
23467             ]
23468         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23469         pane.tab = tab;
23470         
23471         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23472         if (!qty) {
23473             pane.el.addClass('active');
23474         }
23475         
23476                 
23477     },
23478     onTabClick : function(ev,un,ob,pane)
23479     {
23480         //Roo.log('tab - prev default');
23481         ev.preventDefault();
23482         
23483         
23484         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23485         pane.tab.addClass('active');
23486         //Roo.log(pane.title);
23487         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23488         // technically we should have a deactivate event.. but maybe add later.
23489         // and it should not de-activate the selected tab...
23490         this.fireEvent('activatepane', pane);
23491         pane.el.addClass('active');
23492         pane.fireEvent('activate');
23493         
23494         
23495     },
23496     
23497     getActivePane : function()
23498     {
23499         var r = false;
23500         Roo.each(this.panes, function(p) {
23501             if(p.el.hasClass('active')){
23502                 r = p;
23503                 return false;
23504             }
23505             
23506             return;
23507         });
23508         
23509         return r;
23510     }
23511     
23512     
23513 });
23514
23515  
23516 /*
23517  * - LGPL
23518  *
23519  * Tab pane
23520  * 
23521  */
23522 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23523 /**
23524  * @class Roo.bootstrap.TabPane
23525  * @extends Roo.bootstrap.Component
23526  * Bootstrap TabPane class
23527  * @cfg {Boolean} active (false | true) Default false
23528  * @cfg {String} title title of panel
23529
23530  * 
23531  * @constructor
23532  * Create a new TabPane
23533  * @param {Object} config The config object
23534  */
23535
23536 Roo.bootstrap.dash.TabPane = function(config){
23537     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23538     
23539     this.addEvents({
23540         // raw events
23541         /**
23542          * @event activate
23543          * When a pane is activated
23544          * @param {Roo.bootstrap.dash.TabPane} pane
23545          */
23546         "activate" : true
23547          
23548     });
23549 };
23550
23551 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23552     
23553     active : false,
23554     title : '',
23555     
23556     // the tabBox that this is attached to.
23557     tab : false,
23558      
23559     getAutoCreate : function() 
23560     {
23561         var cfg = {
23562             tag: 'div',
23563             cls: 'tab-pane'
23564         };
23565         
23566         if(this.active){
23567             cfg.cls += ' active';
23568         }
23569         
23570         return cfg;
23571     },
23572     initEvents  : function()
23573     {
23574         //Roo.log('trigger add pane handler');
23575         this.parent().fireEvent('addpane', this)
23576     },
23577     
23578      /**
23579      * Updates the tab title 
23580      * @param {String} html to set the title to.
23581      */
23582     setTitle: function(str)
23583     {
23584         if (!this.tab) {
23585             return;
23586         }
23587         this.title = str;
23588         this.tab.select('a', true).first().dom.innerHTML = str;
23589         
23590     }
23591     
23592     
23593     
23594 });
23595
23596  
23597
23598
23599  /*
23600  * - LGPL
23601  *
23602  * menu
23603  * 
23604  */
23605 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23606
23607 /**
23608  * @class Roo.bootstrap.menu.Menu
23609  * @extends Roo.bootstrap.Component
23610  * Bootstrap Menu class - container for Menu
23611  * @cfg {String} html Text of the menu
23612  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23613  * @cfg {String} icon Font awesome icon
23614  * @cfg {String} pos Menu align to (top | bottom) default bottom
23615  * 
23616  * 
23617  * @constructor
23618  * Create a new Menu
23619  * @param {Object} config The config object
23620  */
23621
23622
23623 Roo.bootstrap.menu.Menu = function(config){
23624     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23625     
23626     this.addEvents({
23627         /**
23628          * @event beforeshow
23629          * Fires before this menu is displayed
23630          * @param {Roo.bootstrap.menu.Menu} this
23631          */
23632         beforeshow : true,
23633         /**
23634          * @event beforehide
23635          * Fires before this menu is hidden
23636          * @param {Roo.bootstrap.menu.Menu} this
23637          */
23638         beforehide : true,
23639         /**
23640          * @event show
23641          * Fires after this menu is displayed
23642          * @param {Roo.bootstrap.menu.Menu} this
23643          */
23644         show : true,
23645         /**
23646          * @event hide
23647          * Fires after this menu is hidden
23648          * @param {Roo.bootstrap.menu.Menu} this
23649          */
23650         hide : true,
23651         /**
23652          * @event click
23653          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23654          * @param {Roo.bootstrap.menu.Menu} this
23655          * @param {Roo.EventObject} e
23656          */
23657         click : true
23658     });
23659     
23660 };
23661
23662 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23663     
23664     submenu : false,
23665     html : '',
23666     weight : 'default',
23667     icon : false,
23668     pos : 'bottom',
23669     
23670     
23671     getChildContainer : function() {
23672         if(this.isSubMenu){
23673             return this.el;
23674         }
23675         
23676         return this.el.select('ul.dropdown-menu', true).first();  
23677     },
23678     
23679     getAutoCreate : function()
23680     {
23681         var text = [
23682             {
23683                 tag : 'span',
23684                 cls : 'roo-menu-text',
23685                 html : this.html
23686             }
23687         ];
23688         
23689         if(this.icon){
23690             text.unshift({
23691                 tag : 'i',
23692                 cls : 'fa ' + this.icon
23693             })
23694         }
23695         
23696         
23697         var cfg = {
23698             tag : 'div',
23699             cls : 'btn-group',
23700             cn : [
23701                 {
23702                     tag : 'button',
23703                     cls : 'dropdown-button btn btn-' + this.weight,
23704                     cn : text
23705                 },
23706                 {
23707                     tag : 'button',
23708                     cls : 'dropdown-toggle btn btn-' + this.weight,
23709                     cn : [
23710                         {
23711                             tag : 'span',
23712                             cls : 'caret'
23713                         }
23714                     ]
23715                 },
23716                 {
23717                     tag : 'ul',
23718                     cls : 'dropdown-menu'
23719                 }
23720             ]
23721             
23722         };
23723         
23724         if(this.pos == 'top'){
23725             cfg.cls += ' dropup';
23726         }
23727         
23728         if(this.isSubMenu){
23729             cfg = {
23730                 tag : 'ul',
23731                 cls : 'dropdown-menu'
23732             }
23733         }
23734         
23735         return cfg;
23736     },
23737     
23738     onRender : function(ct, position)
23739     {
23740         this.isSubMenu = ct.hasClass('dropdown-submenu');
23741         
23742         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23743     },
23744     
23745     initEvents : function() 
23746     {
23747         if(this.isSubMenu){
23748             return;
23749         }
23750         
23751         this.hidden = true;
23752         
23753         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23754         this.triggerEl.on('click', this.onTriggerPress, this);
23755         
23756         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23757         this.buttonEl.on('click', this.onClick, this);
23758         
23759     },
23760     
23761     list : function()
23762     {
23763         if(this.isSubMenu){
23764             return this.el;
23765         }
23766         
23767         return this.el.select('ul.dropdown-menu', true).first();
23768     },
23769     
23770     onClick : function(e)
23771     {
23772         this.fireEvent("click", this, e);
23773     },
23774     
23775     onTriggerPress  : function(e)
23776     {   
23777         if (this.isVisible()) {
23778             this.hide();
23779         } else {
23780             this.show();
23781         }
23782     },
23783     
23784     isVisible : function(){
23785         return !this.hidden;
23786     },
23787     
23788     show : function()
23789     {
23790         this.fireEvent("beforeshow", this);
23791         
23792         this.hidden = false;
23793         this.el.addClass('open');
23794         
23795         Roo.get(document).on("mouseup", this.onMouseUp, this);
23796         
23797         this.fireEvent("show", this);
23798         
23799         
23800     },
23801     
23802     hide : function()
23803     {
23804         this.fireEvent("beforehide", this);
23805         
23806         this.hidden = true;
23807         this.el.removeClass('open');
23808         
23809         Roo.get(document).un("mouseup", this.onMouseUp);
23810         
23811         this.fireEvent("hide", this);
23812     },
23813     
23814     onMouseUp : function()
23815     {
23816         this.hide();
23817     }
23818     
23819 });
23820
23821  
23822  /*
23823  * - LGPL
23824  *
23825  * menu item
23826  * 
23827  */
23828 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23829
23830 /**
23831  * @class Roo.bootstrap.menu.Item
23832  * @extends Roo.bootstrap.Component
23833  * Bootstrap MenuItem class
23834  * @cfg {Boolean} submenu (true | false) default false
23835  * @cfg {String} html text of the item
23836  * @cfg {String} href the link
23837  * @cfg {Boolean} disable (true | false) default false
23838  * @cfg {Boolean} preventDefault (true | false) default true
23839  * @cfg {String} icon Font awesome icon
23840  * @cfg {String} pos Submenu align to (left | right) default right 
23841  * 
23842  * 
23843  * @constructor
23844  * Create a new Item
23845  * @param {Object} config The config object
23846  */
23847
23848
23849 Roo.bootstrap.menu.Item = function(config){
23850     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23851     this.addEvents({
23852         /**
23853          * @event mouseover
23854          * Fires when the mouse is hovering over this menu
23855          * @param {Roo.bootstrap.menu.Item} this
23856          * @param {Roo.EventObject} e
23857          */
23858         mouseover : true,
23859         /**
23860          * @event mouseout
23861          * Fires when the mouse exits this menu
23862          * @param {Roo.bootstrap.menu.Item} this
23863          * @param {Roo.EventObject} e
23864          */
23865         mouseout : true,
23866         // raw events
23867         /**
23868          * @event click
23869          * The raw click event for the entire grid.
23870          * @param {Roo.EventObject} e
23871          */
23872         click : true
23873     });
23874 };
23875
23876 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23877     
23878     submenu : false,
23879     href : '',
23880     html : '',
23881     preventDefault: true,
23882     disable : false,
23883     icon : false,
23884     pos : 'right',
23885     
23886     getAutoCreate : function()
23887     {
23888         var text = [
23889             {
23890                 tag : 'span',
23891                 cls : 'roo-menu-item-text',
23892                 html : this.html
23893             }
23894         ];
23895         
23896         if(this.icon){
23897             text.unshift({
23898                 tag : 'i',
23899                 cls : 'fa ' + this.icon
23900             })
23901         }
23902         
23903         var cfg = {
23904             tag : 'li',
23905             cn : [
23906                 {
23907                     tag : 'a',
23908                     href : this.href || '#',
23909                     cn : text
23910                 }
23911             ]
23912         };
23913         
23914         if(this.disable){
23915             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23916         }
23917         
23918         if(this.submenu){
23919             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23920             
23921             if(this.pos == 'left'){
23922                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23923             }
23924         }
23925         
23926         return cfg;
23927     },
23928     
23929     initEvents : function() 
23930     {
23931         this.el.on('mouseover', this.onMouseOver, this);
23932         this.el.on('mouseout', this.onMouseOut, this);
23933         
23934         this.el.select('a', true).first().on('click', this.onClick, this);
23935         
23936     },
23937     
23938     onClick : function(e)
23939     {
23940         if(this.preventDefault){
23941             e.preventDefault();
23942         }
23943         
23944         this.fireEvent("click", this, e);
23945     },
23946     
23947     onMouseOver : function(e)
23948     {
23949         if(this.submenu && this.pos == 'left'){
23950             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23951         }
23952         
23953         this.fireEvent("mouseover", this, e);
23954     },
23955     
23956     onMouseOut : function(e)
23957     {
23958         this.fireEvent("mouseout", this, e);
23959     }
23960 });
23961
23962  
23963
23964  /*
23965  * - LGPL
23966  *
23967  * menu separator
23968  * 
23969  */
23970 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23971
23972 /**
23973  * @class Roo.bootstrap.menu.Separator
23974  * @extends Roo.bootstrap.Component
23975  * Bootstrap Separator class
23976  * 
23977  * @constructor
23978  * Create a new Separator
23979  * @param {Object} config The config object
23980  */
23981
23982
23983 Roo.bootstrap.menu.Separator = function(config){
23984     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23985 };
23986
23987 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23988     
23989     getAutoCreate : function(){
23990         var cfg = {
23991             tag : 'li',
23992             cls: 'divider'
23993         };
23994         
23995         return cfg;
23996     }
23997    
23998 });
23999
24000  
24001
24002  /*
24003  * - LGPL
24004  *
24005  * Tooltip
24006  * 
24007  */
24008
24009 /**
24010  * @class Roo.bootstrap.Tooltip
24011  * Bootstrap Tooltip class
24012  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24013  * to determine which dom element triggers the tooltip.
24014  * 
24015  * It needs to add support for additional attributes like tooltip-position
24016  * 
24017  * @constructor
24018  * Create a new Toolti
24019  * @param {Object} config The config object
24020  */
24021
24022 Roo.bootstrap.Tooltip = function(config){
24023     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24024 };
24025
24026 Roo.apply(Roo.bootstrap.Tooltip, {
24027     /**
24028      * @function init initialize tooltip monitoring.
24029      * @static
24030      */
24031     currentEl : false,
24032     currentTip : false,
24033     currentRegion : false,
24034     
24035     //  init : delay?
24036     
24037     init : function()
24038     {
24039         Roo.get(document).on('mouseover', this.enter ,this);
24040         Roo.get(document).on('mouseout', this.leave, this);
24041          
24042         
24043         this.currentTip = new Roo.bootstrap.Tooltip();
24044     },
24045     
24046     enter : function(ev)
24047     {
24048         var dom = ev.getTarget();
24049         
24050         //Roo.log(['enter',dom]);
24051         var el = Roo.fly(dom);
24052         if (this.currentEl) {
24053             //Roo.log(dom);
24054             //Roo.log(this.currentEl);
24055             //Roo.log(this.currentEl.contains(dom));
24056             if (this.currentEl == el) {
24057                 return;
24058             }
24059             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24060                 return;
24061             }
24062
24063         }
24064         
24065         if (this.currentTip.el) {
24066             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24067         }    
24068         //Roo.log(ev);
24069         var bindEl = el;
24070         
24071         // you can not look for children, as if el is the body.. then everythign is the child..
24072         if (!el.attr('tooltip')) { //
24073             if (!el.select("[tooltip]").elements.length) {
24074                 return;
24075             }
24076             // is the mouse over this child...?
24077             bindEl = el.select("[tooltip]").first();
24078             var xy = ev.getXY();
24079             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24080                 //Roo.log("not in region.");
24081                 return;
24082             }
24083             //Roo.log("child element over..");
24084             
24085         }
24086         this.currentEl = bindEl;
24087         this.currentTip.bind(bindEl);
24088         this.currentRegion = Roo.lib.Region.getRegion(dom);
24089         this.currentTip.enter();
24090         
24091     },
24092     leave : function(ev)
24093     {
24094         var dom = ev.getTarget();
24095         //Roo.log(['leave',dom]);
24096         if (!this.currentEl) {
24097             return;
24098         }
24099         
24100         
24101         if (dom != this.currentEl.dom) {
24102             return;
24103         }
24104         var xy = ev.getXY();
24105         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24106             return;
24107         }
24108         // only activate leave if mouse cursor is outside... bounding box..
24109         
24110         
24111         
24112         
24113         if (this.currentTip) {
24114             this.currentTip.leave();
24115         }
24116         //Roo.log('clear currentEl');
24117         this.currentEl = false;
24118         
24119         
24120     },
24121     alignment : {
24122         'left' : ['r-l', [-2,0], 'right'],
24123         'right' : ['l-r', [2,0], 'left'],
24124         'bottom' : ['t-b', [0,2], 'top'],
24125         'top' : [ 'b-t', [0,-2], 'bottom']
24126     }
24127     
24128 });
24129
24130
24131 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24132     
24133     
24134     bindEl : false,
24135     
24136     delay : null, // can be { show : 300 , hide: 500}
24137     
24138     timeout : null,
24139     
24140     hoverState : null, //???
24141     
24142     placement : 'bottom', 
24143     
24144     getAutoCreate : function(){
24145     
24146         var cfg = {
24147            cls : 'tooltip',
24148            role : 'tooltip',
24149            cn : [
24150                 {
24151                     cls : 'tooltip-arrow'
24152                 },
24153                 {
24154                     cls : 'tooltip-inner'
24155                 }
24156            ]
24157         };
24158         
24159         return cfg;
24160     },
24161     bind : function(el)
24162     {
24163         this.bindEl = el;
24164     },
24165       
24166     
24167     enter : function () {
24168        
24169         if (this.timeout != null) {
24170             clearTimeout(this.timeout);
24171         }
24172         
24173         this.hoverState = 'in';
24174          //Roo.log("enter - show");
24175         if (!this.delay || !this.delay.show) {
24176             this.show();
24177             return;
24178         }
24179         var _t = this;
24180         this.timeout = setTimeout(function () {
24181             if (_t.hoverState == 'in') {
24182                 _t.show();
24183             }
24184         }, this.delay.show);
24185     },
24186     leave : function()
24187     {
24188         clearTimeout(this.timeout);
24189     
24190         this.hoverState = 'out';
24191          if (!this.delay || !this.delay.hide) {
24192             this.hide();
24193             return;
24194         }
24195        
24196         var _t = this;
24197         this.timeout = setTimeout(function () {
24198             //Roo.log("leave - timeout");
24199             
24200             if (_t.hoverState == 'out') {
24201                 _t.hide();
24202                 Roo.bootstrap.Tooltip.currentEl = false;
24203             }
24204         }, delay);
24205     },
24206     
24207     show : function ()
24208     {
24209         if (!this.el) {
24210             this.render(document.body);
24211         }
24212         // set content.
24213         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24214         
24215         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24216         
24217         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24218         
24219         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24220         
24221         var placement = typeof this.placement == 'function' ?
24222             this.placement.call(this, this.el, on_el) :
24223             this.placement;
24224             
24225         var autoToken = /\s?auto?\s?/i;
24226         var autoPlace = autoToken.test(placement);
24227         if (autoPlace) {
24228             placement = placement.replace(autoToken, '') || 'top';
24229         }
24230         
24231         //this.el.detach()
24232         //this.el.setXY([0,0]);
24233         this.el.show();
24234         //this.el.dom.style.display='block';
24235         
24236         //this.el.appendTo(on_el);
24237         
24238         var p = this.getPosition();
24239         var box = this.el.getBox();
24240         
24241         if (autoPlace) {
24242             // fixme..
24243         }
24244         
24245         var align = Roo.bootstrap.Tooltip.alignment[placement];
24246         
24247         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24248         
24249         if(placement == 'top' || placement == 'bottom'){
24250             if(xy[0] < 0){
24251                 placement = 'right';
24252             }
24253             
24254             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24255                 placement = 'left';
24256             }
24257         }
24258         
24259         align = Roo.bootstrap.Tooltip.alignment[placement];
24260         
24261         this.el.alignTo(this.bindEl, align[0],align[1]);
24262         //var arrow = this.el.select('.arrow',true).first();
24263         //arrow.set(align[2], 
24264         
24265         this.el.addClass(placement);
24266         
24267         this.el.addClass('in fade');
24268         
24269         this.hoverState = null;
24270         
24271         if (this.el.hasClass('fade')) {
24272             // fade it?
24273         }
24274         
24275     },
24276     hide : function()
24277     {
24278          
24279         if (!this.el) {
24280             return;
24281         }
24282         //this.el.setXY([0,0]);
24283         this.el.removeClass('in');
24284         //this.el.hide();
24285         
24286     }
24287     
24288 });
24289  
24290
24291  /*
24292  * - LGPL
24293  *
24294  * Location Picker
24295  * 
24296  */
24297
24298 /**
24299  * @class Roo.bootstrap.LocationPicker
24300  * @extends Roo.bootstrap.Component
24301  * Bootstrap LocationPicker class
24302  * @cfg {Number} latitude Position when init default 0
24303  * @cfg {Number} longitude Position when init default 0
24304  * @cfg {Number} zoom default 15
24305  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24306  * @cfg {Boolean} mapTypeControl default false
24307  * @cfg {Boolean} disableDoubleClickZoom default false
24308  * @cfg {Boolean} scrollwheel default true
24309  * @cfg {Boolean} streetViewControl default false
24310  * @cfg {Number} radius default 0
24311  * @cfg {String} locationName
24312  * @cfg {Boolean} draggable default true
24313  * @cfg {Boolean} enableAutocomplete default false
24314  * @cfg {Boolean} enableReverseGeocode default true
24315  * @cfg {String} markerTitle
24316  * 
24317  * @constructor
24318  * Create a new LocationPicker
24319  * @param {Object} config The config object
24320  */
24321
24322
24323 Roo.bootstrap.LocationPicker = function(config){
24324     
24325     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24326     
24327     this.addEvents({
24328         /**
24329          * @event initial
24330          * Fires when the picker initialized.
24331          * @param {Roo.bootstrap.LocationPicker} this
24332          * @param {Google Location} location
24333          */
24334         initial : true,
24335         /**
24336          * @event positionchanged
24337          * Fires when the picker position changed.
24338          * @param {Roo.bootstrap.LocationPicker} this
24339          * @param {Google Location} location
24340          */
24341         positionchanged : true,
24342         /**
24343          * @event resize
24344          * Fires when the map resize.
24345          * @param {Roo.bootstrap.LocationPicker} this
24346          */
24347         resize : true,
24348         /**
24349          * @event show
24350          * Fires when the map show.
24351          * @param {Roo.bootstrap.LocationPicker} this
24352          */
24353         show : true,
24354         /**
24355          * @event hide
24356          * Fires when the map hide.
24357          * @param {Roo.bootstrap.LocationPicker} this
24358          */
24359         hide : true,
24360         /**
24361          * @event mapClick
24362          * Fires when click the map.
24363          * @param {Roo.bootstrap.LocationPicker} this
24364          * @param {Map event} e
24365          */
24366         mapClick : true,
24367         /**
24368          * @event mapRightClick
24369          * Fires when right click the map.
24370          * @param {Roo.bootstrap.LocationPicker} this
24371          * @param {Map event} e
24372          */
24373         mapRightClick : true,
24374         /**
24375          * @event markerClick
24376          * Fires when click the marker.
24377          * @param {Roo.bootstrap.LocationPicker} this
24378          * @param {Map event} e
24379          */
24380         markerClick : true,
24381         /**
24382          * @event markerRightClick
24383          * Fires when right click the marker.
24384          * @param {Roo.bootstrap.LocationPicker} this
24385          * @param {Map event} e
24386          */
24387         markerRightClick : true,
24388         /**
24389          * @event OverlayViewDraw
24390          * Fires when OverlayView Draw
24391          * @param {Roo.bootstrap.LocationPicker} this
24392          */
24393         OverlayViewDraw : true,
24394         /**
24395          * @event OverlayViewOnAdd
24396          * Fires when OverlayView Draw
24397          * @param {Roo.bootstrap.LocationPicker} this
24398          */
24399         OverlayViewOnAdd : true,
24400         /**
24401          * @event OverlayViewOnRemove
24402          * Fires when OverlayView Draw
24403          * @param {Roo.bootstrap.LocationPicker} this
24404          */
24405         OverlayViewOnRemove : true,
24406         /**
24407          * @event OverlayViewShow
24408          * Fires when OverlayView Draw
24409          * @param {Roo.bootstrap.LocationPicker} this
24410          * @param {Pixel} cpx
24411          */
24412         OverlayViewShow : true,
24413         /**
24414          * @event OverlayViewHide
24415          * Fires when OverlayView Draw
24416          * @param {Roo.bootstrap.LocationPicker} this
24417          */
24418         OverlayViewHide : true,
24419         /**
24420          * @event loadexception
24421          * Fires when load google lib failed.
24422          * @param {Roo.bootstrap.LocationPicker} this
24423          */
24424         loadexception : true
24425     });
24426         
24427 };
24428
24429 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24430     
24431     gMapContext: false,
24432     
24433     latitude: 0,
24434     longitude: 0,
24435     zoom: 15,
24436     mapTypeId: false,
24437     mapTypeControl: false,
24438     disableDoubleClickZoom: false,
24439     scrollwheel: true,
24440     streetViewControl: false,
24441     radius: 0,
24442     locationName: '',
24443     draggable: true,
24444     enableAutocomplete: false,
24445     enableReverseGeocode: true,
24446     markerTitle: '',
24447     
24448     getAutoCreate: function()
24449     {
24450
24451         var cfg = {
24452             tag: 'div',
24453             cls: 'roo-location-picker'
24454         };
24455         
24456         return cfg
24457     },
24458     
24459     initEvents: function(ct, position)
24460     {       
24461         if(!this.el.getWidth() || this.isApplied()){
24462             return;
24463         }
24464         
24465         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24466         
24467         this.initial();
24468     },
24469     
24470     initial: function()
24471     {
24472         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24473             this.fireEvent('loadexception', this);
24474             return;
24475         }
24476         
24477         if(!this.mapTypeId){
24478             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24479         }
24480         
24481         this.gMapContext = this.GMapContext();
24482         
24483         this.initOverlayView();
24484         
24485         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24486         
24487         var _this = this;
24488                 
24489         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24490             _this.setPosition(_this.gMapContext.marker.position);
24491         });
24492         
24493         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24494             _this.fireEvent('mapClick', this, event);
24495             
24496         });
24497
24498         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24499             _this.fireEvent('mapRightClick', this, event);
24500             
24501         });
24502         
24503         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24504             _this.fireEvent('markerClick', this, event);
24505             
24506         });
24507
24508         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24509             _this.fireEvent('markerRightClick', this, event);
24510             
24511         });
24512         
24513         this.setPosition(this.gMapContext.location);
24514         
24515         this.fireEvent('initial', this, this.gMapContext.location);
24516     },
24517     
24518     initOverlayView: function()
24519     {
24520         var _this = this;
24521         
24522         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24523             
24524             draw: function()
24525             {
24526                 _this.fireEvent('OverlayViewDraw', _this);
24527             },
24528             
24529             onAdd: function()
24530             {
24531                 _this.fireEvent('OverlayViewOnAdd', _this);
24532             },
24533             
24534             onRemove: function()
24535             {
24536                 _this.fireEvent('OverlayViewOnRemove', _this);
24537             },
24538             
24539             show: function(cpx)
24540             {
24541                 _this.fireEvent('OverlayViewShow', _this, cpx);
24542             },
24543             
24544             hide: function()
24545             {
24546                 _this.fireEvent('OverlayViewHide', _this);
24547             }
24548             
24549         });
24550     },
24551     
24552     fromLatLngToContainerPixel: function(event)
24553     {
24554         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24555     },
24556     
24557     isApplied: function() 
24558     {
24559         return this.getGmapContext() == false ? false : true;
24560     },
24561     
24562     getGmapContext: function() 
24563     {
24564         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24565     },
24566     
24567     GMapContext: function() 
24568     {
24569         var position = new google.maps.LatLng(this.latitude, this.longitude);
24570         
24571         var _map = new google.maps.Map(this.el.dom, {
24572             center: position,
24573             zoom: this.zoom,
24574             mapTypeId: this.mapTypeId,
24575             mapTypeControl: this.mapTypeControl,
24576             disableDoubleClickZoom: this.disableDoubleClickZoom,
24577             scrollwheel: this.scrollwheel,
24578             streetViewControl: this.streetViewControl,
24579             locationName: this.locationName,
24580             draggable: this.draggable,
24581             enableAutocomplete: this.enableAutocomplete,
24582             enableReverseGeocode: this.enableReverseGeocode
24583         });
24584         
24585         var _marker = new google.maps.Marker({
24586             position: position,
24587             map: _map,
24588             title: this.markerTitle,
24589             draggable: this.draggable
24590         });
24591         
24592         return {
24593             map: _map,
24594             marker: _marker,
24595             circle: null,
24596             location: position,
24597             radius: this.radius,
24598             locationName: this.locationName,
24599             addressComponents: {
24600                 formatted_address: null,
24601                 addressLine1: null,
24602                 addressLine2: null,
24603                 streetName: null,
24604                 streetNumber: null,
24605                 city: null,
24606                 district: null,
24607                 state: null,
24608                 stateOrProvince: null
24609             },
24610             settings: this,
24611             domContainer: this.el.dom,
24612             geodecoder: new google.maps.Geocoder()
24613         };
24614     },
24615     
24616     drawCircle: function(center, radius, options) 
24617     {
24618         if (this.gMapContext.circle != null) {
24619             this.gMapContext.circle.setMap(null);
24620         }
24621         if (radius > 0) {
24622             radius *= 1;
24623             options = Roo.apply({}, options, {
24624                 strokeColor: "#0000FF",
24625                 strokeOpacity: .35,
24626                 strokeWeight: 2,
24627                 fillColor: "#0000FF",
24628                 fillOpacity: .2
24629             });
24630             
24631             options.map = this.gMapContext.map;
24632             options.radius = radius;
24633             options.center = center;
24634             this.gMapContext.circle = new google.maps.Circle(options);
24635             return this.gMapContext.circle;
24636         }
24637         
24638         return null;
24639     },
24640     
24641     setPosition: function(location) 
24642     {
24643         this.gMapContext.location = location;
24644         this.gMapContext.marker.setPosition(location);
24645         this.gMapContext.map.panTo(location);
24646         this.drawCircle(location, this.gMapContext.radius, {});
24647         
24648         var _this = this;
24649         
24650         if (this.gMapContext.settings.enableReverseGeocode) {
24651             this.gMapContext.geodecoder.geocode({
24652                 latLng: this.gMapContext.location
24653             }, function(results, status) {
24654                 
24655                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24656                     _this.gMapContext.locationName = results[0].formatted_address;
24657                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24658                     
24659                     _this.fireEvent('positionchanged', this, location);
24660                 }
24661             });
24662             
24663             return;
24664         }
24665         
24666         this.fireEvent('positionchanged', this, location);
24667     },
24668     
24669     resize: function()
24670     {
24671         google.maps.event.trigger(this.gMapContext.map, "resize");
24672         
24673         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24674         
24675         this.fireEvent('resize', this);
24676     },
24677     
24678     setPositionByLatLng: function(latitude, longitude)
24679     {
24680         this.setPosition(new google.maps.LatLng(latitude, longitude));
24681     },
24682     
24683     getCurrentPosition: function() 
24684     {
24685         return {
24686             latitude: this.gMapContext.location.lat(),
24687             longitude: this.gMapContext.location.lng()
24688         };
24689     },
24690     
24691     getAddressName: function() 
24692     {
24693         return this.gMapContext.locationName;
24694     },
24695     
24696     getAddressComponents: function() 
24697     {
24698         return this.gMapContext.addressComponents;
24699     },
24700     
24701     address_component_from_google_geocode: function(address_components) 
24702     {
24703         var result = {};
24704         
24705         for (var i = 0; i < address_components.length; i++) {
24706             var component = address_components[i];
24707             if (component.types.indexOf("postal_code") >= 0) {
24708                 result.postalCode = component.short_name;
24709             } else if (component.types.indexOf("street_number") >= 0) {
24710                 result.streetNumber = component.short_name;
24711             } else if (component.types.indexOf("route") >= 0) {
24712                 result.streetName = component.short_name;
24713             } else if (component.types.indexOf("neighborhood") >= 0) {
24714                 result.city = component.short_name;
24715             } else if (component.types.indexOf("locality") >= 0) {
24716                 result.city = component.short_name;
24717             } else if (component.types.indexOf("sublocality") >= 0) {
24718                 result.district = component.short_name;
24719             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24720                 result.stateOrProvince = component.short_name;
24721             } else if (component.types.indexOf("country") >= 0) {
24722                 result.country = component.short_name;
24723             }
24724         }
24725         
24726         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24727         result.addressLine2 = "";
24728         return result;
24729     },
24730     
24731     setZoomLevel: function(zoom)
24732     {
24733         this.gMapContext.map.setZoom(zoom);
24734     },
24735     
24736     show: function()
24737     {
24738         if(!this.el){
24739             return;
24740         }
24741         
24742         this.el.show();
24743         
24744         this.resize();
24745         
24746         this.fireEvent('show', this);
24747     },
24748     
24749     hide: function()
24750     {
24751         if(!this.el){
24752             return;
24753         }
24754         
24755         this.el.hide();
24756         
24757         this.fireEvent('hide', this);
24758     }
24759     
24760 });
24761
24762 Roo.apply(Roo.bootstrap.LocationPicker, {
24763     
24764     OverlayView : function(map, options)
24765     {
24766         options = options || {};
24767         
24768         this.setMap(map);
24769     }
24770     
24771     
24772 });/*
24773  * - LGPL
24774  *
24775  * Alert
24776  * 
24777  */
24778
24779 /**
24780  * @class Roo.bootstrap.Alert
24781  * @extends Roo.bootstrap.Component
24782  * Bootstrap Alert class
24783  * @cfg {String} title The title of alert
24784  * @cfg {String} html The content of alert
24785  * @cfg {String} weight (  success | info | warning | danger )
24786  * @cfg {String} faicon font-awesomeicon
24787  * 
24788  * @constructor
24789  * Create a new alert
24790  * @param {Object} config The config object
24791  */
24792
24793
24794 Roo.bootstrap.Alert = function(config){
24795     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24796     
24797 };
24798
24799 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24800     
24801     title: '',
24802     html: '',
24803     weight: false,
24804     faicon: false,
24805     
24806     getAutoCreate : function()
24807     {
24808         
24809         var cfg = {
24810             tag : 'div',
24811             cls : 'alert',
24812             cn : [
24813                 {
24814                     tag : 'i',
24815                     cls : 'roo-alert-icon'
24816                     
24817                 },
24818                 {
24819                     tag : 'b',
24820                     cls : 'roo-alert-title',
24821                     html : this.title
24822                 },
24823                 {
24824                     tag : 'span',
24825                     cls : 'roo-alert-text',
24826                     html : this.html
24827                 }
24828             ]
24829         };
24830         
24831         if(this.faicon){
24832             cfg.cn[0].cls += ' fa ' + this.faicon;
24833         }
24834         
24835         if(this.weight){
24836             cfg.cls += ' alert-' + this.weight;
24837         }
24838         
24839         return cfg;
24840     },
24841     
24842     initEvents: function() 
24843     {
24844         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24845     },
24846     
24847     setTitle : function(str)
24848     {
24849         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24850     },
24851     
24852     setText : function(str)
24853     {
24854         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24855     },
24856     
24857     setWeight : function(weight)
24858     {
24859         if(this.weight){
24860             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24861         }
24862         
24863         this.weight = weight;
24864         
24865         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24866     },
24867     
24868     setIcon : function(icon)
24869     {
24870         if(this.faicon){
24871             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24872         }
24873         
24874         this.faicon = icon;
24875         
24876         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24877     },
24878     
24879     hide: function() 
24880     {
24881         this.el.hide();   
24882     },
24883     
24884     show: function() 
24885     {  
24886         this.el.show();   
24887     }
24888     
24889 });
24890
24891  
24892 /*
24893 * Licence: LGPL
24894 */
24895
24896 /**
24897  * @class Roo.bootstrap.UploadCropbox
24898  * @extends Roo.bootstrap.Component
24899  * Bootstrap UploadCropbox class
24900  * @cfg {String} emptyText show when image has been loaded
24901  * @cfg {String} rotateNotify show when image too small to rotate
24902  * @cfg {Number} errorTimeout default 3000
24903  * @cfg {Number} minWidth default 300
24904  * @cfg {Number} minHeight default 300
24905  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24906  * @cfg {Boolean} isDocument (true|false) default false
24907  * @cfg {String} url action url
24908  * @cfg {String} paramName default 'imageUpload'
24909  * @cfg {String} method default POST
24910  * @cfg {Boolean} loadMask (true|false) default true
24911  * @cfg {Boolean} loadingText default 'Loading...'
24912  * 
24913  * @constructor
24914  * Create a new UploadCropbox
24915  * @param {Object} config The config object
24916  */
24917
24918 Roo.bootstrap.UploadCropbox = function(config){
24919     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24920     
24921     this.addEvents({
24922         /**
24923          * @event beforeselectfile
24924          * Fire before select file
24925          * @param {Roo.bootstrap.UploadCropbox} this
24926          */
24927         "beforeselectfile" : true,
24928         /**
24929          * @event initial
24930          * Fire after initEvent
24931          * @param {Roo.bootstrap.UploadCropbox} this
24932          */
24933         "initial" : true,
24934         /**
24935          * @event crop
24936          * Fire after initEvent
24937          * @param {Roo.bootstrap.UploadCropbox} this
24938          * @param {String} data
24939          */
24940         "crop" : true,
24941         /**
24942          * @event prepare
24943          * Fire when preparing the file data
24944          * @param {Roo.bootstrap.UploadCropbox} this
24945          * @param {Object} file
24946          */
24947         "prepare" : true,
24948         /**
24949          * @event exception
24950          * Fire when get exception
24951          * @param {Roo.bootstrap.UploadCropbox} this
24952          * @param {XMLHttpRequest} xhr
24953          */
24954         "exception" : true,
24955         /**
24956          * @event beforeloadcanvas
24957          * Fire before load the canvas
24958          * @param {Roo.bootstrap.UploadCropbox} this
24959          * @param {String} src
24960          */
24961         "beforeloadcanvas" : true,
24962         /**
24963          * @event trash
24964          * Fire when trash image
24965          * @param {Roo.bootstrap.UploadCropbox} this
24966          */
24967         "trash" : true,
24968         /**
24969          * @event download
24970          * Fire when download the image
24971          * @param {Roo.bootstrap.UploadCropbox} this
24972          */
24973         "download" : true,
24974         /**
24975          * @event footerbuttonclick
24976          * Fire when footerbuttonclick
24977          * @param {Roo.bootstrap.UploadCropbox} this
24978          * @param {String} type
24979          */
24980         "footerbuttonclick" : true,
24981         /**
24982          * @event resize
24983          * Fire when resize
24984          * @param {Roo.bootstrap.UploadCropbox} this
24985          */
24986         "resize" : true,
24987         /**
24988          * @event rotate
24989          * Fire when rotate the image
24990          * @param {Roo.bootstrap.UploadCropbox} this
24991          * @param {String} pos
24992          */
24993         "rotate" : true,
24994         /**
24995          * @event inspect
24996          * Fire when inspect the file
24997          * @param {Roo.bootstrap.UploadCropbox} this
24998          * @param {Object} file
24999          */
25000         "inspect" : true,
25001         /**
25002          * @event upload
25003          * Fire when xhr upload the file
25004          * @param {Roo.bootstrap.UploadCropbox} this
25005          * @param {Object} data
25006          */
25007         "upload" : true,
25008         /**
25009          * @event arrange
25010          * Fire when arrange the file data
25011          * @param {Roo.bootstrap.UploadCropbox} this
25012          * @param {Object} formData
25013          */
25014         "arrange" : true
25015     });
25016     
25017     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25018 };
25019
25020 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25021     
25022     emptyText : 'Click to upload image',
25023     rotateNotify : 'Image is too small to rotate',
25024     errorTimeout : 3000,
25025     scale : 0,
25026     baseScale : 1,
25027     rotate : 0,
25028     dragable : false,
25029     pinching : false,
25030     mouseX : 0,
25031     mouseY : 0,
25032     cropData : false,
25033     minWidth : 300,
25034     minHeight : 300,
25035     file : false,
25036     exif : {},
25037     baseRotate : 1,
25038     cropType : 'image/jpeg',
25039     buttons : false,
25040     canvasLoaded : false,
25041     isDocument : false,
25042     method : 'POST',
25043     paramName : 'imageUpload',
25044     loadMask : true,
25045     loadingText : 'Loading...',
25046     maskEl : false,
25047     
25048     getAutoCreate : function()
25049     {
25050         var cfg = {
25051             tag : 'div',
25052             cls : 'roo-upload-cropbox',
25053             cn : [
25054                 {
25055                     tag : 'input',
25056                     cls : 'roo-upload-cropbox-selector',
25057                     type : 'file'
25058                 },
25059                 {
25060                     tag : 'div',
25061                     cls : 'roo-upload-cropbox-body',
25062                     style : 'cursor:pointer',
25063                     cn : [
25064                         {
25065                             tag : 'div',
25066                             cls : 'roo-upload-cropbox-preview'
25067                         },
25068                         {
25069                             tag : 'div',
25070                             cls : 'roo-upload-cropbox-thumb'
25071                         },
25072                         {
25073                             tag : 'div',
25074                             cls : 'roo-upload-cropbox-empty-notify',
25075                             html : this.emptyText
25076                         },
25077                         {
25078                             tag : 'div',
25079                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25080                             html : this.rotateNotify
25081                         }
25082                     ]
25083                 },
25084                 {
25085                     tag : 'div',
25086                     cls : 'roo-upload-cropbox-footer',
25087                     cn : {
25088                         tag : 'div',
25089                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25090                         cn : []
25091                     }
25092                 }
25093             ]
25094         };
25095         
25096         return cfg;
25097     },
25098     
25099     onRender : function(ct, position)
25100     {
25101         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25102         
25103         if (this.buttons.length) {
25104             
25105             Roo.each(this.buttons, function(bb) {
25106                 
25107                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25108                 
25109                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25110                 
25111             }, this);
25112         }
25113         
25114         if(this.loadMask){
25115             this.maskEl = this.el;
25116         }
25117     },
25118     
25119     initEvents : function()
25120     {
25121         this.urlAPI = (window.createObjectURL && window) || 
25122                                 (window.URL && URL.revokeObjectURL && URL) || 
25123                                 (window.webkitURL && webkitURL);
25124                         
25125         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25126         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25127         
25128         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25129         this.selectorEl.hide();
25130         
25131         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25132         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25133         
25134         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25135         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25136         this.thumbEl.hide();
25137         
25138         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25139         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25140         
25141         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25142         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25143         this.errorEl.hide();
25144         
25145         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25146         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25147         this.footerEl.hide();
25148         
25149         this.setThumbBoxSize();
25150         
25151         this.bind();
25152         
25153         this.resize();
25154         
25155         this.fireEvent('initial', this);
25156     },
25157
25158     bind : function()
25159     {
25160         var _this = this;
25161         
25162         window.addEventListener("resize", function() { _this.resize(); } );
25163         
25164         this.bodyEl.on('click', this.beforeSelectFile, this);
25165         
25166         if(Roo.isTouch){
25167             this.bodyEl.on('touchstart', this.onTouchStart, this);
25168             this.bodyEl.on('touchmove', this.onTouchMove, this);
25169             this.bodyEl.on('touchend', this.onTouchEnd, this);
25170         }
25171         
25172         if(!Roo.isTouch){
25173             this.bodyEl.on('mousedown', this.onMouseDown, this);
25174             this.bodyEl.on('mousemove', this.onMouseMove, this);
25175             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25176             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25177             Roo.get(document).on('mouseup', this.onMouseUp, this);
25178         }
25179         
25180         this.selectorEl.on('change', this.onFileSelected, this);
25181     },
25182     
25183     reset : function()
25184     {    
25185         this.scale = 0;
25186         this.baseScale = 1;
25187         this.rotate = 0;
25188         this.baseRotate = 1;
25189         this.dragable = false;
25190         this.pinching = false;
25191         this.mouseX = 0;
25192         this.mouseY = 0;
25193         this.cropData = false;
25194         this.notifyEl.dom.innerHTML = this.emptyText;
25195         
25196         this.selectorEl.dom.value = '';
25197         
25198     },
25199     
25200     resize : function()
25201     {
25202         if(this.fireEvent('resize', this) != false){
25203             this.setThumbBoxPosition();
25204             this.setCanvasPosition();
25205         }
25206     },
25207     
25208     onFooterButtonClick : function(e, el, o, type)
25209     {
25210         switch (type) {
25211             case 'rotate-left' :
25212                 this.onRotateLeft(e);
25213                 break;
25214             case 'rotate-right' :
25215                 this.onRotateRight(e);
25216                 break;
25217             case 'picture' :
25218                 this.beforeSelectFile(e);
25219                 break;
25220             case 'trash' :
25221                 this.trash(e);
25222                 break;
25223             case 'crop' :
25224                 this.crop(e);
25225                 break;
25226             case 'download' :
25227                 this.download(e);
25228                 break;
25229             default :
25230                 break;
25231         }
25232         
25233         this.fireEvent('footerbuttonclick', this, type);
25234     },
25235     
25236     beforeSelectFile : function(e)
25237     {
25238         e.preventDefault();
25239         
25240         if(this.fireEvent('beforeselectfile', this) != false){
25241             this.selectorEl.dom.click();
25242         }
25243     },
25244     
25245     onFileSelected : function(e)
25246     {
25247         e.preventDefault();
25248         
25249         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25250             return;
25251         }
25252         
25253         var file = this.selectorEl.dom.files[0];
25254         
25255         if(this.fireEvent('inspect', this, file) != false){
25256             this.prepare(file);
25257         }
25258         
25259     },
25260     
25261     trash : function(e)
25262     {
25263         this.fireEvent('trash', this);
25264     },
25265     
25266     download : function(e)
25267     {
25268         this.fireEvent('download', this);
25269     },
25270     
25271     loadCanvas : function(src)
25272     {   
25273         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25274             
25275             this.reset();
25276             
25277             this.imageEl = document.createElement('img');
25278             
25279             var _this = this;
25280             
25281             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25282             
25283             this.imageEl.src = src;
25284         }
25285     },
25286     
25287     onLoadCanvas : function()
25288     {   
25289         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25290         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25291         
25292         this.bodyEl.un('click', this.beforeSelectFile, this);
25293         
25294         this.notifyEl.hide();
25295         this.thumbEl.show();
25296         this.footerEl.show();
25297         
25298         this.baseRotateLevel();
25299         
25300         if(this.isDocument){
25301             this.setThumbBoxSize();
25302         }
25303         
25304         this.setThumbBoxPosition();
25305         
25306         this.baseScaleLevel();
25307         
25308         this.draw();
25309         
25310         this.resize();
25311         
25312         this.canvasLoaded = true;
25313         
25314         if(this.loadMask){
25315             this.maskEl.unmask();
25316         }
25317         
25318     },
25319     
25320     setCanvasPosition : function()
25321     {   
25322         if(!this.canvasEl){
25323             return;
25324         }
25325         
25326         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25327         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25328         
25329         this.previewEl.setLeft(pw);
25330         this.previewEl.setTop(ph);
25331         
25332     },
25333     
25334     onMouseDown : function(e)
25335     {   
25336         e.stopEvent();
25337         
25338         this.dragable = true;
25339         this.pinching = false;
25340         
25341         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25342             this.dragable = false;
25343             return;
25344         }
25345         
25346         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25347         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25348         
25349     },
25350     
25351     onMouseMove : function(e)
25352     {   
25353         e.stopEvent();
25354         
25355         if(!this.canvasLoaded){
25356             return;
25357         }
25358         
25359         if (!this.dragable){
25360             return;
25361         }
25362         
25363         var minX = Math.ceil(this.thumbEl.getLeft(true));
25364         var minY = Math.ceil(this.thumbEl.getTop(true));
25365         
25366         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25367         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25368         
25369         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25370         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25371         
25372         x = x - this.mouseX;
25373         y = y - this.mouseY;
25374         
25375         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25376         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25377         
25378         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25379         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25380         
25381         this.previewEl.setLeft(bgX);
25382         this.previewEl.setTop(bgY);
25383         
25384         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25385         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25386     },
25387     
25388     onMouseUp : function(e)
25389     {   
25390         e.stopEvent();
25391         
25392         this.dragable = false;
25393     },
25394     
25395     onMouseWheel : function(e)
25396     {   
25397         e.stopEvent();
25398         
25399         this.startScale = this.scale;
25400         
25401         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25402         
25403         if(!this.zoomable()){
25404             this.scale = this.startScale;
25405             return;
25406         }
25407         
25408         this.draw();
25409         
25410         return;
25411     },
25412     
25413     zoomable : function()
25414     {
25415         var minScale = this.thumbEl.getWidth() / this.minWidth;
25416         
25417         if(this.minWidth < this.minHeight){
25418             minScale = this.thumbEl.getHeight() / this.minHeight;
25419         }
25420         
25421         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25422         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25423         
25424         if(
25425                 this.isDocument &&
25426                 (this.rotate == 0 || this.rotate == 180) && 
25427                 (
25428                     width > this.imageEl.OriginWidth || 
25429                     height > this.imageEl.OriginHeight ||
25430                     (width < this.minWidth && height < this.minHeight)
25431                 )
25432         ){
25433             return false;
25434         }
25435         
25436         if(
25437                 this.isDocument &&
25438                 (this.rotate == 90 || this.rotate == 270) && 
25439                 (
25440                     width > this.imageEl.OriginWidth || 
25441                     height > this.imageEl.OriginHeight ||
25442                     (width < this.minHeight && height < this.minWidth)
25443                 )
25444         ){
25445             return false;
25446         }
25447         
25448         if(
25449                 !this.isDocument &&
25450                 (this.rotate == 0 || this.rotate == 180) && 
25451                 (
25452                     width < this.minWidth || 
25453                     width > this.imageEl.OriginWidth || 
25454                     height < this.minHeight || 
25455                     height > this.imageEl.OriginHeight
25456                 )
25457         ){
25458             return false;
25459         }
25460         
25461         if(
25462                 !this.isDocument &&
25463                 (this.rotate == 90 || this.rotate == 270) && 
25464                 (
25465                     width < this.minHeight || 
25466                     width > this.imageEl.OriginWidth || 
25467                     height < this.minWidth || 
25468                     height > this.imageEl.OriginHeight
25469                 )
25470         ){
25471             return false;
25472         }
25473         
25474         return true;
25475         
25476     },
25477     
25478     onRotateLeft : function(e)
25479     {   
25480         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25481             
25482             var minScale = this.thumbEl.getWidth() / this.minWidth;
25483             
25484             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25485             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25486             
25487             this.startScale = this.scale;
25488             
25489             while (this.getScaleLevel() < minScale){
25490             
25491                 this.scale = this.scale + 1;
25492                 
25493                 if(!this.zoomable()){
25494                     break;
25495                 }
25496                 
25497                 if(
25498                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25499                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25500                 ){
25501                     continue;
25502                 }
25503                 
25504                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25505
25506                 this.draw();
25507                 
25508                 return;
25509             }
25510             
25511             this.scale = this.startScale;
25512             
25513             this.onRotateFail();
25514             
25515             return false;
25516         }
25517         
25518         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25519
25520         if(this.isDocument){
25521             this.setThumbBoxSize();
25522             this.setThumbBoxPosition();
25523             this.setCanvasPosition();
25524         }
25525         
25526         this.draw();
25527         
25528         this.fireEvent('rotate', this, 'left');
25529         
25530     },
25531     
25532     onRotateRight : function(e)
25533     {
25534         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25535             
25536             var minScale = this.thumbEl.getWidth() / this.minWidth;
25537         
25538             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25539             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25540             
25541             this.startScale = this.scale;
25542             
25543             while (this.getScaleLevel() < minScale){
25544             
25545                 this.scale = this.scale + 1;
25546                 
25547                 if(!this.zoomable()){
25548                     break;
25549                 }
25550                 
25551                 if(
25552                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25553                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25554                 ){
25555                     continue;
25556                 }
25557                 
25558                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25559
25560                 this.draw();
25561                 
25562                 return;
25563             }
25564             
25565             this.scale = this.startScale;
25566             
25567             this.onRotateFail();
25568             
25569             return false;
25570         }
25571         
25572         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25573
25574         if(this.isDocument){
25575             this.setThumbBoxSize();
25576             this.setThumbBoxPosition();
25577             this.setCanvasPosition();
25578         }
25579         
25580         this.draw();
25581         
25582         this.fireEvent('rotate', this, 'right');
25583     },
25584     
25585     onRotateFail : function()
25586     {
25587         this.errorEl.show(true);
25588         
25589         var _this = this;
25590         
25591         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25592     },
25593     
25594     draw : function()
25595     {
25596         this.previewEl.dom.innerHTML = '';
25597         
25598         var canvasEl = document.createElement("canvas");
25599         
25600         var contextEl = canvasEl.getContext("2d");
25601         
25602         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25603         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25604         var center = this.imageEl.OriginWidth / 2;
25605         
25606         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25607             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25608             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25609             center = this.imageEl.OriginHeight / 2;
25610         }
25611         
25612         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25613         
25614         contextEl.translate(center, center);
25615         contextEl.rotate(this.rotate * Math.PI / 180);
25616
25617         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25618         
25619         this.canvasEl = document.createElement("canvas");
25620         
25621         this.contextEl = this.canvasEl.getContext("2d");
25622         
25623         switch (this.rotate) {
25624             case 0 :
25625                 
25626                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25627                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25628                 
25629                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25630                 
25631                 break;
25632             case 90 : 
25633                 
25634                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25635                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25636                 
25637                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25638                     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);
25639                     break;
25640                 }
25641                 
25642                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25643                 
25644                 break;
25645             case 180 :
25646                 
25647                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25648                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25649                 
25650                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25651                     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);
25652                     break;
25653                 }
25654                 
25655                 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);
25656                 
25657                 break;
25658             case 270 :
25659                 
25660                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25661                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25662         
25663                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25664                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25665                     break;
25666                 }
25667                 
25668                 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);
25669                 
25670                 break;
25671             default : 
25672                 break;
25673         }
25674         
25675         this.previewEl.appendChild(this.canvasEl);
25676         
25677         this.setCanvasPosition();
25678     },
25679     
25680     crop : function()
25681     {
25682         if(!this.canvasLoaded){
25683             return;
25684         }
25685         
25686         var imageCanvas = document.createElement("canvas");
25687         
25688         var imageContext = imageCanvas.getContext("2d");
25689         
25690         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25691         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25692         
25693         var center = imageCanvas.width / 2;
25694         
25695         imageContext.translate(center, center);
25696         
25697         imageContext.rotate(this.rotate * Math.PI / 180);
25698         
25699         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25700         
25701         var canvas = document.createElement("canvas");
25702         
25703         var context = canvas.getContext("2d");
25704                 
25705         canvas.width = this.minWidth;
25706         canvas.height = this.minHeight;
25707
25708         switch (this.rotate) {
25709             case 0 :
25710                 
25711                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25712                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25713                 
25714                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25715                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25716                 
25717                 var targetWidth = this.minWidth - 2 * x;
25718                 var targetHeight = this.minHeight - 2 * y;
25719                 
25720                 var scale = 1;
25721                 
25722                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25723                     scale = targetWidth / width;
25724                 }
25725                 
25726                 if(x > 0 && y == 0){
25727                     scale = targetHeight / height;
25728                 }
25729                 
25730                 if(x > 0 && y > 0){
25731                     scale = targetWidth / width;
25732                     
25733                     if(width < height){
25734                         scale = targetHeight / height;
25735                     }
25736                 }
25737                 
25738                 context.scale(scale, scale);
25739                 
25740                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25741                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25742
25743                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25744                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25745
25746                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25747                 
25748                 break;
25749             case 90 : 
25750                 
25751                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25752                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25753                 
25754                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25755                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25756                 
25757                 var targetWidth = this.minWidth - 2 * x;
25758                 var targetHeight = this.minHeight - 2 * y;
25759                 
25760                 var scale = 1;
25761                 
25762                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25763                     scale = targetWidth / width;
25764                 }
25765                 
25766                 if(x > 0 && y == 0){
25767                     scale = targetHeight / height;
25768                 }
25769                 
25770                 if(x > 0 && y > 0){
25771                     scale = targetWidth / width;
25772                     
25773                     if(width < height){
25774                         scale = targetHeight / height;
25775                     }
25776                 }
25777                 
25778                 context.scale(scale, scale);
25779                 
25780                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25781                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25782
25783                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25784                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25785                 
25786                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25787                 
25788                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25789                 
25790                 break;
25791             case 180 :
25792                 
25793                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25794                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25795                 
25796                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25797                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25798                 
25799                 var targetWidth = this.minWidth - 2 * x;
25800                 var targetHeight = this.minHeight - 2 * y;
25801                 
25802                 var scale = 1;
25803                 
25804                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25805                     scale = targetWidth / width;
25806                 }
25807                 
25808                 if(x > 0 && y == 0){
25809                     scale = targetHeight / height;
25810                 }
25811                 
25812                 if(x > 0 && y > 0){
25813                     scale = targetWidth / width;
25814                     
25815                     if(width < height){
25816                         scale = targetHeight / height;
25817                     }
25818                 }
25819                 
25820                 context.scale(scale, scale);
25821                 
25822                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25823                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25824
25825                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25826                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25827
25828                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25829                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25830                 
25831                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25832                 
25833                 break;
25834             case 270 :
25835                 
25836                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25837                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25838                 
25839                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25840                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25841                 
25842                 var targetWidth = this.minWidth - 2 * x;
25843                 var targetHeight = this.minHeight - 2 * y;
25844                 
25845                 var scale = 1;
25846                 
25847                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25848                     scale = targetWidth / width;
25849                 }
25850                 
25851                 if(x > 0 && y == 0){
25852                     scale = targetHeight / height;
25853                 }
25854                 
25855                 if(x > 0 && y > 0){
25856                     scale = targetWidth / width;
25857                     
25858                     if(width < height){
25859                         scale = targetHeight / height;
25860                     }
25861                 }
25862                 
25863                 context.scale(scale, scale);
25864                 
25865                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25866                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25867
25868                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25869                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25870                 
25871                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25872                 
25873                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25874                 
25875                 break;
25876             default : 
25877                 break;
25878         }
25879         
25880         this.cropData = canvas.toDataURL(this.cropType);
25881         
25882         if(this.fireEvent('crop', this, this.cropData) !== false){
25883             this.process(this.file, this.cropData);
25884         }
25885         
25886         return;
25887         
25888     },
25889     
25890     setThumbBoxSize : function()
25891     {
25892         var width, height;
25893         
25894         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25895             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25896             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25897             
25898             this.minWidth = width;
25899             this.minHeight = height;
25900             
25901             if(this.rotate == 90 || this.rotate == 270){
25902                 this.minWidth = height;
25903                 this.minHeight = width;
25904             }
25905         }
25906         
25907         height = 300;
25908         width = Math.ceil(this.minWidth * height / this.minHeight);
25909         
25910         if(this.minWidth > this.minHeight){
25911             width = 300;
25912             height = Math.ceil(this.minHeight * width / this.minWidth);
25913         }
25914         
25915         this.thumbEl.setStyle({
25916             width : width + 'px',
25917             height : height + 'px'
25918         });
25919
25920         return;
25921             
25922     },
25923     
25924     setThumbBoxPosition : function()
25925     {
25926         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25927         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25928         
25929         this.thumbEl.setLeft(x);
25930         this.thumbEl.setTop(y);
25931         
25932     },
25933     
25934     baseRotateLevel : function()
25935     {
25936         this.baseRotate = 1;
25937         
25938         if(
25939                 typeof(this.exif) != 'undefined' &&
25940                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25941                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25942         ){
25943             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25944         }
25945         
25946         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25947         
25948     },
25949     
25950     baseScaleLevel : function()
25951     {
25952         var width, height;
25953         
25954         if(this.isDocument){
25955             
25956             if(this.baseRotate == 6 || this.baseRotate == 8){
25957             
25958                 height = this.thumbEl.getHeight();
25959                 this.baseScale = height / this.imageEl.OriginWidth;
25960
25961                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25962                     width = this.thumbEl.getWidth();
25963                     this.baseScale = width / this.imageEl.OriginHeight;
25964                 }
25965
25966                 return;
25967             }
25968
25969             height = this.thumbEl.getHeight();
25970             this.baseScale = height / this.imageEl.OriginHeight;
25971
25972             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25973                 width = this.thumbEl.getWidth();
25974                 this.baseScale = width / this.imageEl.OriginWidth;
25975             }
25976
25977             return;
25978         }
25979         
25980         if(this.baseRotate == 6 || this.baseRotate == 8){
25981             
25982             width = this.thumbEl.getHeight();
25983             this.baseScale = width / this.imageEl.OriginHeight;
25984             
25985             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25986                 height = this.thumbEl.getWidth();
25987                 this.baseScale = height / this.imageEl.OriginHeight;
25988             }
25989             
25990             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25991                 height = this.thumbEl.getWidth();
25992                 this.baseScale = height / this.imageEl.OriginHeight;
25993                 
25994                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25995                     width = this.thumbEl.getHeight();
25996                     this.baseScale = width / this.imageEl.OriginWidth;
25997                 }
25998             }
25999             
26000             return;
26001         }
26002         
26003         width = this.thumbEl.getWidth();
26004         this.baseScale = width / this.imageEl.OriginWidth;
26005         
26006         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26007             height = this.thumbEl.getHeight();
26008             this.baseScale = height / this.imageEl.OriginHeight;
26009         }
26010         
26011         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26012             
26013             height = this.thumbEl.getHeight();
26014             this.baseScale = height / this.imageEl.OriginHeight;
26015             
26016             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26017                 width = this.thumbEl.getWidth();
26018                 this.baseScale = width / this.imageEl.OriginWidth;
26019             }
26020             
26021         }
26022         
26023         return;
26024     },
26025     
26026     getScaleLevel : function()
26027     {
26028         return this.baseScale * Math.pow(1.1, this.scale);
26029     },
26030     
26031     onTouchStart : function(e)
26032     {
26033         if(!this.canvasLoaded){
26034             this.beforeSelectFile(e);
26035             return;
26036         }
26037         
26038         var touches = e.browserEvent.touches;
26039         
26040         if(!touches){
26041             return;
26042         }
26043         
26044         if(touches.length == 1){
26045             this.onMouseDown(e);
26046             return;
26047         }
26048         
26049         if(touches.length != 2){
26050             return;
26051         }
26052         
26053         var coords = [];
26054         
26055         for(var i = 0, finger; finger = touches[i]; i++){
26056             coords.push(finger.pageX, finger.pageY);
26057         }
26058         
26059         var x = Math.pow(coords[0] - coords[2], 2);
26060         var y = Math.pow(coords[1] - coords[3], 2);
26061         
26062         this.startDistance = Math.sqrt(x + y);
26063         
26064         this.startScale = this.scale;
26065         
26066         this.pinching = true;
26067         this.dragable = false;
26068         
26069     },
26070     
26071     onTouchMove : function(e)
26072     {
26073         if(!this.pinching && !this.dragable){
26074             return;
26075         }
26076         
26077         var touches = e.browserEvent.touches;
26078         
26079         if(!touches){
26080             return;
26081         }
26082         
26083         if(this.dragable){
26084             this.onMouseMove(e);
26085             return;
26086         }
26087         
26088         var coords = [];
26089         
26090         for(var i = 0, finger; finger = touches[i]; i++){
26091             coords.push(finger.pageX, finger.pageY);
26092         }
26093         
26094         var x = Math.pow(coords[0] - coords[2], 2);
26095         var y = Math.pow(coords[1] - coords[3], 2);
26096         
26097         this.endDistance = Math.sqrt(x + y);
26098         
26099         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26100         
26101         if(!this.zoomable()){
26102             this.scale = this.startScale;
26103             return;
26104         }
26105         
26106         this.draw();
26107         
26108     },
26109     
26110     onTouchEnd : function(e)
26111     {
26112         this.pinching = false;
26113         this.dragable = false;
26114         
26115     },
26116     
26117     process : function(file, crop)
26118     {
26119         if(this.loadMask){
26120             this.maskEl.mask(this.loadingText);
26121         }
26122         
26123         this.xhr = new XMLHttpRequest();
26124         
26125         file.xhr = this.xhr;
26126
26127         this.xhr.open(this.method, this.url, true);
26128         
26129         var headers = {
26130             "Accept": "application/json",
26131             "Cache-Control": "no-cache",
26132             "X-Requested-With": "XMLHttpRequest"
26133         };
26134         
26135         for (var headerName in headers) {
26136             var headerValue = headers[headerName];
26137             if (headerValue) {
26138                 this.xhr.setRequestHeader(headerName, headerValue);
26139             }
26140         }
26141         
26142         var _this = this;
26143         
26144         this.xhr.onload = function()
26145         {
26146             _this.xhrOnLoad(_this.xhr);
26147         }
26148         
26149         this.xhr.onerror = function()
26150         {
26151             _this.xhrOnError(_this.xhr);
26152         }
26153         
26154         var formData = new FormData();
26155
26156         formData.append('returnHTML', 'NO');
26157         
26158         if(crop){
26159             formData.append('crop', crop);
26160         }
26161         
26162         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26163             formData.append(this.paramName, file, file.name);
26164         }
26165         
26166         if(typeof(file.filename) != 'undefined'){
26167             formData.append('filename', file.filename);
26168         }
26169         
26170         if(typeof(file.mimetype) != 'undefined'){
26171             formData.append('mimetype', file.mimetype);
26172         }
26173         
26174         if(this.fireEvent('arrange', this, formData) != false){
26175             this.xhr.send(formData);
26176         };
26177     },
26178     
26179     xhrOnLoad : function(xhr)
26180     {
26181         if(this.loadMask){
26182             this.maskEl.unmask();
26183         }
26184         
26185         if (xhr.readyState !== 4) {
26186             this.fireEvent('exception', this, xhr);
26187             return;
26188         }
26189
26190         var response = Roo.decode(xhr.responseText);
26191         
26192         if(!response.success){
26193             this.fireEvent('exception', this, xhr);
26194             return;
26195         }
26196         
26197         var response = Roo.decode(xhr.responseText);
26198         
26199         this.fireEvent('upload', this, response);
26200         
26201     },
26202     
26203     xhrOnError : function()
26204     {
26205         if(this.loadMask){
26206             this.maskEl.unmask();
26207         }
26208         
26209         Roo.log('xhr on error');
26210         
26211         var response = Roo.decode(xhr.responseText);
26212           
26213         Roo.log(response);
26214         
26215     },
26216     
26217     prepare : function(file)
26218     {   
26219         if(this.loadMask){
26220             this.maskEl.mask(this.loadingText);
26221         }
26222         
26223         this.file = false;
26224         this.exif = {};
26225         
26226         if(typeof(file) === 'string'){
26227             this.loadCanvas(file);
26228             return;
26229         }
26230         
26231         if(!file || !this.urlAPI){
26232             return;
26233         }
26234         
26235         this.file = file;
26236         this.cropType = file.type;
26237         
26238         var _this = this;
26239         
26240         if(this.fireEvent('prepare', this, this.file) != false){
26241             
26242             var reader = new FileReader();
26243             
26244             reader.onload = function (e) {
26245                 if (e.target.error) {
26246                     Roo.log(e.target.error);
26247                     return;
26248                 }
26249                 
26250                 var buffer = e.target.result,
26251                     dataView = new DataView(buffer),
26252                     offset = 2,
26253                     maxOffset = dataView.byteLength - 4,
26254                     markerBytes,
26255                     markerLength;
26256                 
26257                 if (dataView.getUint16(0) === 0xffd8) {
26258                     while (offset < maxOffset) {
26259                         markerBytes = dataView.getUint16(offset);
26260                         
26261                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26262                             markerLength = dataView.getUint16(offset + 2) + 2;
26263                             if (offset + markerLength > dataView.byteLength) {
26264                                 Roo.log('Invalid meta data: Invalid segment size.');
26265                                 break;
26266                             }
26267                             
26268                             if(markerBytes == 0xffe1){
26269                                 _this.parseExifData(
26270                                     dataView,
26271                                     offset,
26272                                     markerLength
26273                                 );
26274                             }
26275                             
26276                             offset += markerLength;
26277                             
26278                             continue;
26279                         }
26280                         
26281                         break;
26282                     }
26283                     
26284                 }
26285                 
26286                 var url = _this.urlAPI.createObjectURL(_this.file);
26287                 
26288                 _this.loadCanvas(url);
26289                 
26290                 return;
26291             }
26292             
26293             reader.readAsArrayBuffer(this.file);
26294             
26295         }
26296         
26297     },
26298     
26299     parseExifData : function(dataView, offset, length)
26300     {
26301         var tiffOffset = offset + 10,
26302             littleEndian,
26303             dirOffset;
26304     
26305         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26306             // No Exif data, might be XMP data instead
26307             return;
26308         }
26309         
26310         // Check for the ASCII code for "Exif" (0x45786966):
26311         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26312             // No Exif data, might be XMP data instead
26313             return;
26314         }
26315         if (tiffOffset + 8 > dataView.byteLength) {
26316             Roo.log('Invalid Exif data: Invalid segment size.');
26317             return;
26318         }
26319         // Check for the two null bytes:
26320         if (dataView.getUint16(offset + 8) !== 0x0000) {
26321             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26322             return;
26323         }
26324         // Check the byte alignment:
26325         switch (dataView.getUint16(tiffOffset)) {
26326         case 0x4949:
26327             littleEndian = true;
26328             break;
26329         case 0x4D4D:
26330             littleEndian = false;
26331             break;
26332         default:
26333             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26334             return;
26335         }
26336         // Check for the TIFF tag marker (0x002A):
26337         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26338             Roo.log('Invalid Exif data: Missing TIFF marker.');
26339             return;
26340         }
26341         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26342         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26343         
26344         this.parseExifTags(
26345             dataView,
26346             tiffOffset,
26347             tiffOffset + dirOffset,
26348             littleEndian
26349         );
26350     },
26351     
26352     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26353     {
26354         var tagsNumber,
26355             dirEndOffset,
26356             i;
26357         if (dirOffset + 6 > dataView.byteLength) {
26358             Roo.log('Invalid Exif data: Invalid directory offset.');
26359             return;
26360         }
26361         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26362         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26363         if (dirEndOffset + 4 > dataView.byteLength) {
26364             Roo.log('Invalid Exif data: Invalid directory size.');
26365             return;
26366         }
26367         for (i = 0; i < tagsNumber; i += 1) {
26368             this.parseExifTag(
26369                 dataView,
26370                 tiffOffset,
26371                 dirOffset + 2 + 12 * i, // tag offset
26372                 littleEndian
26373             );
26374         }
26375         // Return the offset to the next directory:
26376         return dataView.getUint32(dirEndOffset, littleEndian);
26377     },
26378     
26379     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26380     {
26381         var tag = dataView.getUint16(offset, littleEndian);
26382         
26383         this.exif[tag] = this.getExifValue(
26384             dataView,
26385             tiffOffset,
26386             offset,
26387             dataView.getUint16(offset + 2, littleEndian), // tag type
26388             dataView.getUint32(offset + 4, littleEndian), // tag length
26389             littleEndian
26390         );
26391     },
26392     
26393     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26394     {
26395         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26396             tagSize,
26397             dataOffset,
26398             values,
26399             i,
26400             str,
26401             c;
26402     
26403         if (!tagType) {
26404             Roo.log('Invalid Exif data: Invalid tag type.');
26405             return;
26406         }
26407         
26408         tagSize = tagType.size * length;
26409         // Determine if the value is contained in the dataOffset bytes,
26410         // or if the value at the dataOffset is a pointer to the actual data:
26411         dataOffset = tagSize > 4 ?
26412                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26413         if (dataOffset + tagSize > dataView.byteLength) {
26414             Roo.log('Invalid Exif data: Invalid data offset.');
26415             return;
26416         }
26417         if (length === 1) {
26418             return tagType.getValue(dataView, dataOffset, littleEndian);
26419         }
26420         values = [];
26421         for (i = 0; i < length; i += 1) {
26422             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26423         }
26424         
26425         if (tagType.ascii) {
26426             str = '';
26427             // Concatenate the chars:
26428             for (i = 0; i < values.length; i += 1) {
26429                 c = values[i];
26430                 // Ignore the terminating NULL byte(s):
26431                 if (c === '\u0000') {
26432                     break;
26433                 }
26434                 str += c;
26435             }
26436             return str;
26437         }
26438         return values;
26439     }
26440     
26441 });
26442
26443 Roo.apply(Roo.bootstrap.UploadCropbox, {
26444     tags : {
26445         'Orientation': 0x0112
26446     },
26447     
26448     Orientation: {
26449             1: 0, //'top-left',
26450 //            2: 'top-right',
26451             3: 180, //'bottom-right',
26452 //            4: 'bottom-left',
26453 //            5: 'left-top',
26454             6: 90, //'right-top',
26455 //            7: 'right-bottom',
26456             8: 270 //'left-bottom'
26457     },
26458     
26459     exifTagTypes : {
26460         // byte, 8-bit unsigned int:
26461         1: {
26462             getValue: function (dataView, dataOffset) {
26463                 return dataView.getUint8(dataOffset);
26464             },
26465             size: 1
26466         },
26467         // ascii, 8-bit byte:
26468         2: {
26469             getValue: function (dataView, dataOffset) {
26470                 return String.fromCharCode(dataView.getUint8(dataOffset));
26471             },
26472             size: 1,
26473             ascii: true
26474         },
26475         // short, 16 bit int:
26476         3: {
26477             getValue: function (dataView, dataOffset, littleEndian) {
26478                 return dataView.getUint16(dataOffset, littleEndian);
26479             },
26480             size: 2
26481         },
26482         // long, 32 bit int:
26483         4: {
26484             getValue: function (dataView, dataOffset, littleEndian) {
26485                 return dataView.getUint32(dataOffset, littleEndian);
26486             },
26487             size: 4
26488         },
26489         // rational = two long values, first is numerator, second is denominator:
26490         5: {
26491             getValue: function (dataView, dataOffset, littleEndian) {
26492                 return dataView.getUint32(dataOffset, littleEndian) /
26493                     dataView.getUint32(dataOffset + 4, littleEndian);
26494             },
26495             size: 8
26496         },
26497         // slong, 32 bit signed int:
26498         9: {
26499             getValue: function (dataView, dataOffset, littleEndian) {
26500                 return dataView.getInt32(dataOffset, littleEndian);
26501             },
26502             size: 4
26503         },
26504         // srational, two slongs, first is numerator, second is denominator:
26505         10: {
26506             getValue: function (dataView, dataOffset, littleEndian) {
26507                 return dataView.getInt32(dataOffset, littleEndian) /
26508                     dataView.getInt32(dataOffset + 4, littleEndian);
26509             },
26510             size: 8
26511         }
26512     },
26513     
26514     footer : {
26515         STANDARD : [
26516             {
26517                 tag : 'div',
26518                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26519                 action : 'rotate-left',
26520                 cn : [
26521                     {
26522                         tag : 'button',
26523                         cls : 'btn btn-default',
26524                         html : '<i class="fa fa-undo"></i>'
26525                     }
26526                 ]
26527             },
26528             {
26529                 tag : 'div',
26530                 cls : 'btn-group roo-upload-cropbox-picture',
26531                 action : 'picture',
26532                 cn : [
26533                     {
26534                         tag : 'button',
26535                         cls : 'btn btn-default',
26536                         html : '<i class="fa fa-picture-o"></i>'
26537                     }
26538                 ]
26539             },
26540             {
26541                 tag : 'div',
26542                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26543                 action : 'rotate-right',
26544                 cn : [
26545                     {
26546                         tag : 'button',
26547                         cls : 'btn btn-default',
26548                         html : '<i class="fa fa-repeat"></i>'
26549                     }
26550                 ]
26551             }
26552         ],
26553         DOCUMENT : [
26554             {
26555                 tag : 'div',
26556                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26557                 action : 'rotate-left',
26558                 cn : [
26559                     {
26560                         tag : 'button',
26561                         cls : 'btn btn-default',
26562                         html : '<i class="fa fa-undo"></i>'
26563                     }
26564                 ]
26565             },
26566             {
26567                 tag : 'div',
26568                 cls : 'btn-group roo-upload-cropbox-download',
26569                 action : 'download',
26570                 cn : [
26571                     {
26572                         tag : 'button',
26573                         cls : 'btn btn-default',
26574                         html : '<i class="fa fa-download"></i>'
26575                     }
26576                 ]
26577             },
26578             {
26579                 tag : 'div',
26580                 cls : 'btn-group roo-upload-cropbox-crop',
26581                 action : 'crop',
26582                 cn : [
26583                     {
26584                         tag : 'button',
26585                         cls : 'btn btn-default',
26586                         html : '<i class="fa fa-crop"></i>'
26587                     }
26588                 ]
26589             },
26590             {
26591                 tag : 'div',
26592                 cls : 'btn-group roo-upload-cropbox-trash',
26593                 action : 'trash',
26594                 cn : [
26595                     {
26596                         tag : 'button',
26597                         cls : 'btn btn-default',
26598                         html : '<i class="fa fa-trash"></i>'
26599                     }
26600                 ]
26601             },
26602             {
26603                 tag : 'div',
26604                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26605                 action : 'rotate-right',
26606                 cn : [
26607                     {
26608                         tag : 'button',
26609                         cls : 'btn btn-default',
26610                         html : '<i class="fa fa-repeat"></i>'
26611                     }
26612                 ]
26613             }
26614         ],
26615         ROTATOR : [
26616             {
26617                 tag : 'div',
26618                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26619                 action : 'rotate-left',
26620                 cn : [
26621                     {
26622                         tag : 'button',
26623                         cls : 'btn btn-default',
26624                         html : '<i class="fa fa-undo"></i>'
26625                     }
26626                 ]
26627             },
26628             {
26629                 tag : 'div',
26630                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26631                 action : 'rotate-right',
26632                 cn : [
26633                     {
26634                         tag : 'button',
26635                         cls : 'btn btn-default',
26636                         html : '<i class="fa fa-repeat"></i>'
26637                     }
26638                 ]
26639             }
26640         ]
26641     }
26642 });
26643
26644 /*
26645 * Licence: LGPL
26646 */
26647
26648 /**
26649  * @class Roo.bootstrap.DocumentManager
26650  * @extends Roo.bootstrap.Component
26651  * Bootstrap DocumentManager class
26652  * @cfg {String} paramName default 'imageUpload'
26653  * @cfg {String} method default POST
26654  * @cfg {String} url action url
26655  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26656  * @cfg {Boolean} multiple multiple upload default true
26657  * @cfg {Number} thumbSize default 300
26658  * @cfg {String} fieldLabel
26659  * @cfg {Number} labelWidth default 4
26660  * @cfg {String} labelAlign (left|top) default left
26661  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26662  * 
26663  * @constructor
26664  * Create a new DocumentManager
26665  * @param {Object} config The config object
26666  */
26667
26668 Roo.bootstrap.DocumentManager = function(config){
26669     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26670     
26671     this.addEvents({
26672         /**
26673          * @event initial
26674          * Fire when initial the DocumentManager
26675          * @param {Roo.bootstrap.DocumentManager} this
26676          */
26677         "initial" : true,
26678         /**
26679          * @event inspect
26680          * inspect selected file
26681          * @param {Roo.bootstrap.DocumentManager} this
26682          * @param {File} file
26683          */
26684         "inspect" : true,
26685         /**
26686          * @event exception
26687          * Fire when xhr load exception
26688          * @param {Roo.bootstrap.DocumentManager} this
26689          * @param {XMLHttpRequest} xhr
26690          */
26691         "exception" : true,
26692         /**
26693          * @event prepare
26694          * prepare the form data
26695          * @param {Roo.bootstrap.DocumentManager} this
26696          * @param {Object} formData
26697          */
26698         "prepare" : true,
26699         /**
26700          * @event remove
26701          * Fire when remove the file
26702          * @param {Roo.bootstrap.DocumentManager} this
26703          * @param {Object} file
26704          */
26705         "remove" : true,
26706         /**
26707          * @event refresh
26708          * Fire after refresh the file
26709          * @param {Roo.bootstrap.DocumentManager} this
26710          */
26711         "refresh" : true,
26712         /**
26713          * @event click
26714          * Fire after click the image
26715          * @param {Roo.bootstrap.DocumentManager} this
26716          * @param {Object} file
26717          */
26718         "click" : true,
26719         /**
26720          * @event edit
26721          * Fire when upload a image and editable set to true
26722          * @param {Roo.bootstrap.DocumentManager} this
26723          * @param {Object} file
26724          */
26725         "edit" : true,
26726         /**
26727          * @event beforeselectfile
26728          * Fire before select file
26729          * @param {Roo.bootstrap.DocumentManager} this
26730          */
26731         "beforeselectfile" : true,
26732         /**
26733          * @event process
26734          * Fire before process file
26735          * @param {Roo.bootstrap.DocumentManager} this
26736          * @param {Object} file
26737          */
26738         "process" : true
26739         
26740     });
26741 };
26742
26743 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26744     
26745     boxes : 0,
26746     inputName : '',
26747     thumbSize : 300,
26748     multiple : true,
26749     files : [],
26750     method : 'POST',
26751     url : '',
26752     paramName : 'imageUpload',
26753     fieldLabel : '',
26754     labelWidth : 4,
26755     labelAlign : 'left',
26756     editable : true,
26757     delegates : [],
26758     
26759     
26760     xhr : false, 
26761     
26762     getAutoCreate : function()
26763     {   
26764         var managerWidget = {
26765             tag : 'div',
26766             cls : 'roo-document-manager',
26767             cn : [
26768                 {
26769                     tag : 'input',
26770                     cls : 'roo-document-manager-selector',
26771                     type : 'file'
26772                 },
26773                 {
26774                     tag : 'div',
26775                     cls : 'roo-document-manager-uploader',
26776                     cn : [
26777                         {
26778                             tag : 'div',
26779                             cls : 'roo-document-manager-upload-btn',
26780                             html : '<i class="fa fa-plus"></i>'
26781                         }
26782                     ]
26783                     
26784                 }
26785             ]
26786         };
26787         
26788         var content = [
26789             {
26790                 tag : 'div',
26791                 cls : 'column col-md-12',
26792                 cn : managerWidget
26793             }
26794         ];
26795         
26796         if(this.fieldLabel.length){
26797             
26798             content = [
26799                 {
26800                     tag : 'div',
26801                     cls : 'column col-md-12',
26802                     html : this.fieldLabel
26803                 },
26804                 {
26805                     tag : 'div',
26806                     cls : 'column col-md-12',
26807                     cn : managerWidget
26808                 }
26809             ];
26810
26811             if(this.labelAlign == 'left'){
26812                 content = [
26813                     {
26814                         tag : 'div',
26815                         cls : 'column col-md-' + this.labelWidth,
26816                         html : this.fieldLabel
26817                     },
26818                     {
26819                         tag : 'div',
26820                         cls : 'column col-md-' + (12 - this.labelWidth),
26821                         cn : managerWidget
26822                     }
26823                 ];
26824                 
26825             }
26826         }
26827         
26828         var cfg = {
26829             tag : 'div',
26830             cls : 'row clearfix',
26831             cn : content
26832         };
26833         
26834         return cfg;
26835         
26836     },
26837     
26838     initEvents : function()
26839     {
26840         this.managerEl = this.el.select('.roo-document-manager', true).first();
26841         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26842         
26843         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26844         this.selectorEl.hide();
26845         
26846         if(this.multiple){
26847             this.selectorEl.attr('multiple', 'multiple');
26848         }
26849         
26850         this.selectorEl.on('change', this.onFileSelected, this);
26851         
26852         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26853         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26854         
26855         this.uploader.on('click', this.onUploaderClick, this);
26856         
26857         this.renderProgressDialog();
26858         
26859         var _this = this;
26860         
26861         window.addEventListener("resize", function() { _this.refresh(); } );
26862         
26863         this.fireEvent('initial', this);
26864     },
26865     
26866     renderProgressDialog : function()
26867     {
26868         var _this = this;
26869         
26870         this.progressDialog = new Roo.bootstrap.Modal({
26871             cls : 'roo-document-manager-progress-dialog',
26872             allow_close : false,
26873             title : '',
26874             buttons : [
26875                 {
26876                     name  :'cancel',
26877                     weight : 'danger',
26878                     html : 'Cancel'
26879                 }
26880             ], 
26881             listeners : { 
26882                 btnclick : function() {
26883                     _this.uploadCancel();
26884                     this.hide();
26885                 }
26886             }
26887         });
26888          
26889         this.progressDialog.render(Roo.get(document.body));
26890          
26891         this.progress = new Roo.bootstrap.Progress({
26892             cls : 'roo-document-manager-progress',
26893             active : true,
26894             striped : true
26895         });
26896         
26897         this.progress.render(this.progressDialog.getChildContainer());
26898         
26899         this.progressBar = new Roo.bootstrap.ProgressBar({
26900             cls : 'roo-document-manager-progress-bar',
26901             aria_valuenow : 0,
26902             aria_valuemin : 0,
26903             aria_valuemax : 12,
26904             panel : 'success'
26905         });
26906         
26907         this.progressBar.render(this.progress.getChildContainer());
26908     },
26909     
26910     onUploaderClick : function(e)
26911     {
26912         e.preventDefault();
26913      
26914         if(this.fireEvent('beforeselectfile', this) != false){
26915             this.selectorEl.dom.click();
26916         }
26917         
26918     },
26919     
26920     onFileSelected : function(e)
26921     {
26922         e.preventDefault();
26923         
26924         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26925             return;
26926         }
26927         
26928         Roo.each(this.selectorEl.dom.files, function(file){
26929             if(this.fireEvent('inspect', this, file) != false){
26930                 this.files.push(file);
26931             }
26932         }, this);
26933         
26934         this.queue();
26935         
26936     },
26937     
26938     queue : function()
26939     {
26940         this.selectorEl.dom.value = '';
26941         
26942         if(!this.files.length){
26943             return;
26944         }
26945         
26946         if(this.boxes > 0 && this.files.length > this.boxes){
26947             this.files = this.files.slice(0, this.boxes);
26948         }
26949         
26950         this.uploader.show();
26951         
26952         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26953             this.uploader.hide();
26954         }
26955         
26956         var _this = this;
26957         
26958         var files = [];
26959         
26960         var docs = [];
26961         
26962         Roo.each(this.files, function(file){
26963             
26964             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26965                 var f = this.renderPreview(file);
26966                 files.push(f);
26967                 return;
26968             }
26969             
26970             if(file.type.indexOf('image') != -1){
26971                 this.delegates.push(
26972                     (function(){
26973                         _this.process(file);
26974                     }).createDelegate(this)
26975                 );
26976         
26977                 return;
26978             }
26979             
26980             docs.push(
26981                 (function(){
26982                     _this.process(file);
26983                 }).createDelegate(this)
26984             );
26985             
26986         }, this);
26987         
26988         this.files = files;
26989         
26990         this.delegates = this.delegates.concat(docs);
26991         
26992         if(!this.delegates.length){
26993             this.refresh();
26994             return;
26995         }
26996         
26997         this.progressBar.aria_valuemax = this.delegates.length;
26998         
26999         this.arrange();
27000         
27001         return;
27002     },
27003     
27004     arrange : function()
27005     {
27006         if(!this.delegates.length){
27007             this.progressDialog.hide();
27008             this.refresh();
27009             return;
27010         }
27011         
27012         var delegate = this.delegates.shift();
27013         
27014         this.progressDialog.show();
27015         
27016         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27017         
27018         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27019         
27020         delegate();
27021     },
27022     
27023     refresh : function()
27024     {
27025         this.uploader.show();
27026         
27027         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27028             this.uploader.hide();
27029         }
27030         
27031         Roo.isTouch ? this.closable(false) : this.closable(true);
27032         
27033         this.fireEvent('refresh', this);
27034     },
27035     
27036     onRemove : function(e, el, o)
27037     {
27038         e.preventDefault();
27039         
27040         this.fireEvent('remove', this, o);
27041         
27042     },
27043     
27044     remove : function(o)
27045     {
27046         var files = [];
27047         
27048         Roo.each(this.files, function(file){
27049             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27050                 files.push(file);
27051                 return;
27052             }
27053
27054             o.target.remove();
27055
27056         }, this);
27057         
27058         this.files = files;
27059         
27060         this.refresh();
27061     },
27062     
27063     clear : function()
27064     {
27065         Roo.each(this.files, function(file){
27066             if(!file.target){
27067                 return;
27068             }
27069             
27070             file.target.remove();
27071
27072         }, this);
27073         
27074         this.files = [];
27075         
27076         this.refresh();
27077     },
27078     
27079     onClick : function(e, el, o)
27080     {
27081         e.preventDefault();
27082         
27083         this.fireEvent('click', this, o);
27084         
27085     },
27086     
27087     closable : function(closable)
27088     {
27089         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27090             
27091             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27092             
27093             if(closable){
27094                 el.show();
27095                 return;
27096             }
27097             
27098             el.hide();
27099             
27100         }, this);
27101     },
27102     
27103     xhrOnLoad : function(xhr)
27104     {
27105         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27106             el.remove();
27107         }, this);
27108         
27109         if (xhr.readyState !== 4) {
27110             this.arrange();
27111             this.fireEvent('exception', this, xhr);
27112             return;
27113         }
27114
27115         var response = Roo.decode(xhr.responseText);
27116         
27117         if(!response.success){
27118             this.arrange();
27119             this.fireEvent('exception', this, xhr);
27120             return;
27121         }
27122         
27123         var file = this.renderPreview(response.data);
27124         
27125         this.files.push(file);
27126         
27127         this.arrange();
27128         
27129     },
27130     
27131     xhrOnError : function(xhr)
27132     {
27133         Roo.log('xhr on error');
27134         
27135         var response = Roo.decode(xhr.responseText);
27136           
27137         Roo.log(response);
27138         
27139         this.arrange();
27140     },
27141     
27142     process : function(file)
27143     {
27144         if(this.fireEvent('process', this, file) !== false){
27145             if(this.editable && file.type.indexOf('image') != -1){
27146                 this.fireEvent('edit', this, file);
27147                 return;
27148             }
27149
27150             this.uploadStart(file, false);
27151
27152             return;
27153         }
27154         
27155     },
27156     
27157     uploadStart : function(file, crop)
27158     {
27159         this.xhr = new XMLHttpRequest();
27160         
27161         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27162             this.arrange();
27163             return;
27164         }
27165         
27166         file.xhr = this.xhr;
27167             
27168         this.managerEl.createChild({
27169             tag : 'div',
27170             cls : 'roo-document-manager-loading',
27171             cn : [
27172                 {
27173                     tag : 'div',
27174                     tooltip : file.name,
27175                     cls : 'roo-document-manager-thumb',
27176                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27177                 }
27178             ]
27179
27180         });
27181
27182         this.xhr.open(this.method, this.url, true);
27183         
27184         var headers = {
27185             "Accept": "application/json",
27186             "Cache-Control": "no-cache",
27187             "X-Requested-With": "XMLHttpRequest"
27188         };
27189         
27190         for (var headerName in headers) {
27191             var headerValue = headers[headerName];
27192             if (headerValue) {
27193                 this.xhr.setRequestHeader(headerName, headerValue);
27194             }
27195         }
27196         
27197         var _this = this;
27198         
27199         this.xhr.onload = function()
27200         {
27201             _this.xhrOnLoad(_this.xhr);
27202         }
27203         
27204         this.xhr.onerror = function()
27205         {
27206             _this.xhrOnError(_this.xhr);
27207         }
27208         
27209         var formData = new FormData();
27210
27211         formData.append('returnHTML', 'NO');
27212         
27213         if(crop){
27214             formData.append('crop', crop);
27215         }
27216         
27217         formData.append(this.paramName, file, file.name);
27218         
27219         if(this.fireEvent('prepare', this, formData) != false){
27220             this.xhr.send(formData);
27221         };
27222     },
27223     
27224     uploadCancel : function()
27225     {
27226         if (this.xhr) {
27227             this.xhr.abort();
27228         }
27229         
27230         
27231         this.delegates = [];
27232         
27233         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27234             el.remove();
27235         }, this);
27236         
27237         this.arrange();
27238     },
27239     
27240     renderPreview : function(file)
27241     {
27242         if(typeof(file.target) != 'undefined' && file.target){
27243             return file;
27244         }
27245         
27246         var previewEl = this.managerEl.createChild({
27247             tag : 'div',
27248             cls : 'roo-document-manager-preview',
27249             cn : [
27250                 {
27251                     tag : 'div',
27252                     tooltip : file.filename,
27253                     cls : 'roo-document-manager-thumb',
27254                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27255                 },
27256                 {
27257                     tag : 'button',
27258                     cls : 'close',
27259                     html : '<i class="fa fa-times-circle"></i>'
27260                 }
27261             ]
27262         });
27263
27264         var close = previewEl.select('button.close', true).first();
27265
27266         close.on('click', this.onRemove, this, file);
27267
27268         file.target = previewEl;
27269
27270         var image = previewEl.select('img', true).first();
27271         
27272         var _this = this;
27273         
27274         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27275         
27276         image.on('click', this.onClick, this, file);
27277         
27278         return file;
27279         
27280     },
27281     
27282     onPreviewLoad : function(file, image)
27283     {
27284         if(typeof(file.target) == 'undefined' || !file.target){
27285             return;
27286         }
27287         
27288         var width = image.dom.naturalWidth || image.dom.width;
27289         var height = image.dom.naturalHeight || image.dom.height;
27290         
27291         if(width > height){
27292             file.target.addClass('wide');
27293             return;
27294         }
27295         
27296         file.target.addClass('tall');
27297         return;
27298         
27299     },
27300     
27301     uploadFromSource : function(file, crop)
27302     {
27303         this.xhr = new XMLHttpRequest();
27304         
27305         this.managerEl.createChild({
27306             tag : 'div',
27307             cls : 'roo-document-manager-loading',
27308             cn : [
27309                 {
27310                     tag : 'div',
27311                     tooltip : file.name,
27312                     cls : 'roo-document-manager-thumb',
27313                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27314                 }
27315             ]
27316
27317         });
27318
27319         this.xhr.open(this.method, this.url, true);
27320         
27321         var headers = {
27322             "Accept": "application/json",
27323             "Cache-Control": "no-cache",
27324             "X-Requested-With": "XMLHttpRequest"
27325         };
27326         
27327         for (var headerName in headers) {
27328             var headerValue = headers[headerName];
27329             if (headerValue) {
27330                 this.xhr.setRequestHeader(headerName, headerValue);
27331             }
27332         }
27333         
27334         var _this = this;
27335         
27336         this.xhr.onload = function()
27337         {
27338             _this.xhrOnLoad(_this.xhr);
27339         }
27340         
27341         this.xhr.onerror = function()
27342         {
27343             _this.xhrOnError(_this.xhr);
27344         }
27345         
27346         var formData = new FormData();
27347
27348         formData.append('returnHTML', 'NO');
27349         
27350         formData.append('crop', crop);
27351         
27352         if(typeof(file.filename) != 'undefined'){
27353             formData.append('filename', file.filename);
27354         }
27355         
27356         if(typeof(file.mimetype) != 'undefined'){
27357             formData.append('mimetype', file.mimetype);
27358         }
27359         
27360         if(this.fireEvent('prepare', this, formData) != false){
27361             this.xhr.send(formData);
27362         };
27363     }
27364 });
27365
27366 /*
27367 * Licence: LGPL
27368 */
27369
27370 /**
27371  * @class Roo.bootstrap.DocumentViewer
27372  * @extends Roo.bootstrap.Component
27373  * Bootstrap DocumentViewer class
27374  * 
27375  * @constructor
27376  * Create a new DocumentViewer
27377  * @param {Object} config The config object
27378  */
27379
27380 Roo.bootstrap.DocumentViewer = function(config){
27381     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27382     
27383     this.addEvents({
27384         /**
27385          * @event initial
27386          * Fire after initEvent
27387          * @param {Roo.bootstrap.DocumentViewer} this
27388          */
27389         "initial" : true,
27390         /**
27391          * @event click
27392          * Fire after click
27393          * @param {Roo.bootstrap.DocumentViewer} this
27394          */
27395         "click" : true,
27396         /**
27397          * @event trash
27398          * Fire after trash button
27399          * @param {Roo.bootstrap.DocumentViewer} this
27400          */
27401         "trash" : true
27402         
27403     });
27404 };
27405
27406 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27407     
27408     getAutoCreate : function()
27409     {
27410         var cfg = {
27411             tag : 'div',
27412             cls : 'roo-document-viewer',
27413             cn : [
27414                 {
27415                     tag : 'div',
27416                     cls : 'roo-document-viewer-body',
27417                     cn : [
27418                         {
27419                             tag : 'div',
27420                             cls : 'roo-document-viewer-thumb',
27421                             cn : [
27422                                 {
27423                                     tag : 'img',
27424                                     cls : 'roo-document-viewer-image'
27425                                 }
27426                             ]
27427                         }
27428                     ]
27429                 },
27430                 {
27431                     tag : 'div',
27432                     cls : 'roo-document-viewer-footer',
27433                     cn : {
27434                         tag : 'div',
27435                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27436                         cn : [
27437                             {
27438                                 tag : 'div',
27439                                 cls : 'btn-group',
27440                                 cn : [
27441                                     {
27442                                         tag : 'button',
27443                                         cls : 'btn btn-default roo-document-viewer-trash',
27444                                         html : '<i class="fa fa-trash"></i>'
27445                                     }
27446                                 ]
27447                             }
27448                         ]
27449                     }
27450                 }
27451             ]
27452         };
27453         
27454         return cfg;
27455     },
27456     
27457     initEvents : function()
27458     {
27459         
27460         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27461         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27462         
27463         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27464         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27465         
27466         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27467         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27468         
27469         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27470         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27471         
27472         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27473         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27474         
27475         this.bodyEl.on('click', this.onClick, this);
27476         
27477         this.trashBtn.on('click', this.onTrash, this);
27478         
27479     },
27480     
27481     initial : function()
27482     {
27483 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27484         
27485         
27486         this.fireEvent('initial', this);
27487         
27488     },
27489     
27490     onClick : function(e)
27491     {
27492         e.preventDefault();
27493         
27494         this.fireEvent('click', this);
27495     },
27496     
27497     onTrash : function(e)
27498     {
27499         e.preventDefault();
27500         
27501         this.fireEvent('trash', this);
27502     }
27503     
27504 });
27505 /*
27506  * - LGPL
27507  *
27508  * nav progress bar
27509  * 
27510  */
27511
27512 /**
27513  * @class Roo.bootstrap.NavProgressBar
27514  * @extends Roo.bootstrap.Component
27515  * Bootstrap NavProgressBar class
27516  * 
27517  * @constructor
27518  * Create a new nav progress bar
27519  * @param {Object} config The config object
27520  */
27521
27522 Roo.bootstrap.NavProgressBar = function(config){
27523     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27524
27525     this.bullets = this.bullets || [];
27526    
27527 //    Roo.bootstrap.NavProgressBar.register(this);
27528      this.addEvents({
27529         /**
27530              * @event changed
27531              * Fires when the active item changes
27532              * @param {Roo.bootstrap.NavProgressBar} this
27533              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27534              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27535          */
27536         'changed': true
27537      });
27538     
27539 };
27540
27541 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27542     
27543     bullets : [],
27544     barItems : [],
27545     
27546     getAutoCreate : function()
27547     {
27548         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27549         
27550         cfg = {
27551             tag : 'div',
27552             cls : 'roo-navigation-bar-group',
27553             cn : [
27554                 {
27555                     tag : 'div',
27556                     cls : 'roo-navigation-top-bar'
27557                 },
27558                 {
27559                     tag : 'div',
27560                     cls : 'roo-navigation-bullets-bar',
27561                     cn : [
27562                         {
27563                             tag : 'ul',
27564                             cls : 'roo-navigation-bar'
27565                         }
27566                     ]
27567                 },
27568                 
27569                 {
27570                     tag : 'div',
27571                     cls : 'roo-navigation-bottom-bar'
27572                 }
27573             ]
27574             
27575         };
27576         
27577         return cfg;
27578         
27579     },
27580     
27581     initEvents: function() 
27582     {
27583         
27584     },
27585     
27586     onRender : function(ct, position) 
27587     {
27588         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27589         
27590         if(this.bullets.length){
27591             Roo.each(this.bullets, function(b){
27592                this.addItem(b);
27593             }, this);
27594         }
27595         
27596         this.format();
27597         
27598     },
27599     
27600     addItem : function(cfg)
27601     {
27602         var item = new Roo.bootstrap.NavProgressItem(cfg);
27603         
27604         item.parentId = this.id;
27605         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27606         
27607         if(cfg.html){
27608             var top = new Roo.bootstrap.Element({
27609                 tag : 'div',
27610                 cls : 'roo-navigation-bar-text'
27611             });
27612             
27613             var bottom = new Roo.bootstrap.Element({
27614                 tag : 'div',
27615                 cls : 'roo-navigation-bar-text'
27616             });
27617             
27618             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27619             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27620             
27621             var topText = new Roo.bootstrap.Element({
27622                 tag : 'span',
27623                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27624             });
27625             
27626             var bottomText = new Roo.bootstrap.Element({
27627                 tag : 'span',
27628                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27629             });
27630             
27631             topText.onRender(top.el, null);
27632             bottomText.onRender(bottom.el, null);
27633             
27634             item.topEl = top;
27635             item.bottomEl = bottom;
27636         }
27637         
27638         this.barItems.push(item);
27639         
27640         return item;
27641     },
27642     
27643     getActive : function()
27644     {
27645         var active = false;
27646         
27647         Roo.each(this.barItems, function(v){
27648             
27649             if (!v.isActive()) {
27650                 return;
27651             }
27652             
27653             active = v;
27654             return false;
27655             
27656         });
27657         
27658         return active;
27659     },
27660     
27661     setActiveItem : function(item)
27662     {
27663         var prev = false;
27664         
27665         Roo.each(this.barItems, function(v){
27666             if (v.rid == item.rid) {
27667                 return ;
27668             }
27669             
27670             if (v.isActive()) {
27671                 v.setActive(false);
27672                 prev = v;
27673             }
27674         });
27675
27676         item.setActive(true);
27677         
27678         this.fireEvent('changed', this, item, prev);
27679     },
27680     
27681     getBarItem: function(rid)
27682     {
27683         var ret = false;
27684         
27685         Roo.each(this.barItems, function(e) {
27686             if (e.rid != rid) {
27687                 return;
27688             }
27689             
27690             ret =  e;
27691             return false;
27692         });
27693         
27694         return ret;
27695     },
27696     
27697     indexOfItem : function(item)
27698     {
27699         var index = false;
27700         
27701         Roo.each(this.barItems, function(v, i){
27702             
27703             if (v.rid != item.rid) {
27704                 return;
27705             }
27706             
27707             index = i;
27708             return false
27709         });
27710         
27711         return index;
27712     },
27713     
27714     setActiveNext : function()
27715     {
27716         var i = this.indexOfItem(this.getActive());
27717         
27718         if (i > this.barItems.length) {
27719             return;
27720         }
27721         
27722         this.setActiveItem(this.barItems[i+1]);
27723     },
27724     
27725     setActivePrev : function()
27726     {
27727         var i = this.indexOfItem(this.getActive());
27728         
27729         if (i  < 1) {
27730             return;
27731         }
27732         
27733         this.setActiveItem(this.barItems[i-1]);
27734     },
27735     
27736     format : function()
27737     {
27738         if(!this.barItems.length){
27739             return;
27740         }
27741      
27742         var width = 100 / this.barItems.length;
27743         
27744         Roo.each(this.barItems, function(i){
27745             i.el.setStyle('width', width + '%');
27746             i.topEl.el.setStyle('width', width + '%');
27747             i.bottomEl.el.setStyle('width', width + '%');
27748         }, this);
27749         
27750     }
27751     
27752 });
27753 /*
27754  * - LGPL
27755  *
27756  * Nav Progress Item
27757  * 
27758  */
27759
27760 /**
27761  * @class Roo.bootstrap.NavProgressItem
27762  * @extends Roo.bootstrap.Component
27763  * Bootstrap NavProgressItem class
27764  * @cfg {String} rid the reference id
27765  * @cfg {Boolean} active (true|false) Is item active default false
27766  * @cfg {Boolean} disabled (true|false) Is item active default false
27767  * @cfg {String} html
27768  * @cfg {String} position (top|bottom) text position default bottom
27769  * @cfg {String} icon show icon instead of number
27770  * 
27771  * @constructor
27772  * Create a new NavProgressItem
27773  * @param {Object} config The config object
27774  */
27775 Roo.bootstrap.NavProgressItem = function(config){
27776     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27777     this.addEvents({
27778         // raw events
27779         /**
27780          * @event click
27781          * The raw click event for the entire grid.
27782          * @param {Roo.bootstrap.NavProgressItem} this
27783          * @param {Roo.EventObject} e
27784          */
27785         "click" : true
27786     });
27787    
27788 };
27789
27790 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27791     
27792     rid : '',
27793     active : false,
27794     disabled : false,
27795     html : '',
27796     position : 'bottom',
27797     icon : false,
27798     
27799     getAutoCreate : function()
27800     {
27801         var iconCls = 'roo-navigation-bar-item-icon';
27802         
27803         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27804         
27805         var cfg = {
27806             tag: 'li',
27807             cls: 'roo-navigation-bar-item',
27808             cn : [
27809                 {
27810                     tag : 'i',
27811                     cls : iconCls
27812                 }
27813             ]
27814         };
27815         
27816         if(this.active){
27817             cfg.cls += ' active';
27818         }
27819         if(this.disabled){
27820             cfg.cls += ' disabled';
27821         }
27822         
27823         return cfg;
27824     },
27825     
27826     disable : function()
27827     {
27828         this.setDisabled(true);
27829     },
27830     
27831     enable : function()
27832     {
27833         this.setDisabled(false);
27834     },
27835     
27836     initEvents: function() 
27837     {
27838         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27839         
27840         this.iconEl.on('click', this.onClick, this);
27841     },
27842     
27843     onClick : function(e)
27844     {
27845         e.preventDefault();
27846         
27847         if(this.disabled){
27848             return;
27849         }
27850         
27851         if(this.fireEvent('click', this, e) === false){
27852             return;
27853         };
27854         
27855         this.parent().setActiveItem(this);
27856     },
27857     
27858     isActive: function () 
27859     {
27860         return this.active;
27861     },
27862     
27863     setActive : function(state)
27864     {
27865         if(this.active == state){
27866             return;
27867         }
27868         
27869         this.active = state;
27870         
27871         if (state) {
27872             this.el.addClass('active');
27873             return;
27874         }
27875         
27876         this.el.removeClass('active');
27877         
27878         return;
27879     },
27880     
27881     setDisabled : function(state)
27882     {
27883         if(this.disabled == state){
27884             return;
27885         }
27886         
27887         this.disabled = state;
27888         
27889         if (state) {
27890             this.el.addClass('disabled');
27891             return;
27892         }
27893         
27894         this.el.removeClass('disabled');
27895     },
27896     
27897     tooltipEl : function()
27898     {
27899         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27900     }
27901 });
27902  
27903
27904  /*
27905  * - LGPL
27906  *
27907  * FieldLabel
27908  * 
27909  */
27910
27911 /**
27912  * @class Roo.bootstrap.FieldLabel
27913  * @extends Roo.bootstrap.Component
27914  * Bootstrap FieldLabel class
27915  * @cfg {String} html contents of the element
27916  * @cfg {String} tag tag of the element default label
27917  * @cfg {String} cls class of the element
27918  * @cfg {String} target label target 
27919  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27920  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27921  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27922  * @cfg {String} iconTooltip default "This field is required"
27923  * 
27924  * @constructor
27925  * Create a new FieldLabel
27926  * @param {Object} config The config object
27927  */
27928
27929 Roo.bootstrap.FieldLabel = function(config){
27930     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27931     
27932     this.addEvents({
27933             /**
27934              * @event invalid
27935              * Fires after the field has been marked as invalid.
27936              * @param {Roo.form.FieldLabel} this
27937              * @param {String} msg The validation message
27938              */
27939             invalid : true,
27940             /**
27941              * @event valid
27942              * Fires after the field has been validated with no errors.
27943              * @param {Roo.form.FieldLabel} this
27944              */
27945             valid : true
27946         });
27947 };
27948
27949 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27950     
27951     tag: 'label',
27952     cls: '',
27953     html: '',
27954     target: '',
27955     allowBlank : true,
27956     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27957     validClass : 'text-success fa fa-lg fa-check',
27958     iconTooltip : 'This field is required',
27959     
27960     getAutoCreate : function(){
27961         
27962         var cfg = {
27963             tag : this.tag,
27964             cls : 'roo-bootstrap-field-label ' + this.cls,
27965             for : this.target,
27966             cn : [
27967                 {
27968                     tag : 'i',
27969                     cls : '',
27970                     tooltip : this.iconTooltip
27971                 },
27972                 {
27973                     tag : 'span',
27974                     html : this.html
27975                 }
27976             ] 
27977         };
27978         
27979         return cfg;
27980     },
27981     
27982     initEvents: function() 
27983     {
27984         Roo.bootstrap.Element.superclass.initEvents.call(this);
27985         
27986         this.iconEl = this.el.select('i', true).first();
27987         
27988         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27989         
27990         Roo.bootstrap.FieldLabel.register(this);
27991     },
27992     
27993     /**
27994      * Mark this field as valid
27995      */
27996     markValid : function()
27997     {
27998         this.iconEl.show();
27999         
28000         this.iconEl.removeClass(this.invalidClass);
28001         
28002         this.iconEl.addClass(this.validClass);
28003         
28004         this.fireEvent('valid', this);
28005     },
28006     
28007     /**
28008      * Mark this field as invalid
28009      * @param {String} msg The validation message
28010      */
28011     markInvalid : function(msg)
28012     {
28013         this.iconEl.show();
28014         
28015         this.iconEl.removeClass(this.validClass);
28016         
28017         this.iconEl.addClass(this.invalidClass);
28018         
28019         this.fireEvent('invalid', this, msg);
28020     }
28021     
28022    
28023 });
28024
28025 Roo.apply(Roo.bootstrap.FieldLabel, {
28026     
28027     groups: {},
28028     
28029      /**
28030     * register a FieldLabel Group
28031     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28032     */
28033     register : function(label)
28034     {
28035         if(this.groups.hasOwnProperty(label.target)){
28036             return;
28037         }
28038      
28039         this.groups[label.target] = label;
28040         
28041     },
28042     /**
28043     * fetch a FieldLabel Group based on the target
28044     * @param {string} target
28045     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28046     */
28047     get: function(target) {
28048         if (typeof(this.groups[target]) == 'undefined') {
28049             return false;
28050         }
28051         
28052         return this.groups[target] ;
28053     }
28054 });
28055
28056  
28057
28058  /*
28059  * - LGPL
28060  *
28061  * page DateSplitField.
28062  * 
28063  */
28064
28065
28066 /**
28067  * @class Roo.bootstrap.DateSplitField
28068  * @extends Roo.bootstrap.Component
28069  * Bootstrap DateSplitField class
28070  * @cfg {string} fieldLabel - the label associated
28071  * @cfg {Number} labelWidth set the width of label (0-12)
28072  * @cfg {String} labelAlign (top|left)
28073  * @cfg {Boolean} dayAllowBlank (true|false) default false
28074  * @cfg {Boolean} monthAllowBlank (true|false) default false
28075  * @cfg {Boolean} yearAllowBlank (true|false) default false
28076  * @cfg {string} dayPlaceholder 
28077  * @cfg {string} monthPlaceholder
28078  * @cfg {string} yearPlaceholder
28079  * @cfg {string} dayFormat default 'd'
28080  * @cfg {string} monthFormat default 'm'
28081  * @cfg {string} yearFormat default 'Y'
28082
28083  *     
28084  * @constructor
28085  * Create a new DateSplitField
28086  * @param {Object} config The config object
28087  */
28088
28089 Roo.bootstrap.DateSplitField = function(config){
28090     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28091     
28092     this.addEvents({
28093         // raw events
28094          /**
28095          * @event years
28096          * getting the data of years
28097          * @param {Roo.bootstrap.DateSplitField} this
28098          * @param {Object} years
28099          */
28100         "years" : true,
28101         /**
28102          * @event days
28103          * getting the data of days
28104          * @param {Roo.bootstrap.DateSplitField} this
28105          * @param {Object} days
28106          */
28107         "days" : true,
28108         /**
28109          * @event invalid
28110          * Fires after the field has been marked as invalid.
28111          * @param {Roo.form.Field} this
28112          * @param {String} msg The validation message
28113          */
28114         invalid : true,
28115        /**
28116          * @event valid
28117          * Fires after the field has been validated with no errors.
28118          * @param {Roo.form.Field} this
28119          */
28120         valid : true
28121     });
28122 };
28123
28124 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28125     
28126     fieldLabel : '',
28127     labelAlign : 'top',
28128     labelWidth : 3,
28129     dayAllowBlank : false,
28130     monthAllowBlank : false,
28131     yearAllowBlank : false,
28132     dayPlaceholder : '',
28133     monthPlaceholder : '',
28134     yearPlaceholder : '',
28135     dayFormat : 'd',
28136     monthFormat : 'm',
28137     yearFormat : 'Y',
28138     isFormField : true,
28139     
28140     getAutoCreate : function()
28141     {
28142         var cfg = {
28143             tag : 'div',
28144             cls : 'row roo-date-split-field-group',
28145             cn : [
28146                 {
28147                     tag : 'input',
28148                     type : 'hidden',
28149                     cls : 'form-hidden-field roo-date-split-field-group-value',
28150                     name : this.name
28151                 }
28152             ]
28153         };
28154         
28155         if(this.fieldLabel){
28156             cfg.cn.push({
28157                 tag : 'div',
28158                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28159                 cn : [
28160                     {
28161                         tag : 'label',
28162                         html : this.fieldLabel
28163                     }
28164                 ]
28165             });
28166         }
28167         
28168         Roo.each(['day', 'month', 'year'], function(t){
28169             cfg.cn.push({
28170                 tag : 'div',
28171                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28172             });
28173         }, this);
28174         
28175         return cfg;
28176     },
28177     
28178     inputEl: function ()
28179     {
28180         return this.el.select('.roo-date-split-field-group-value', true).first();
28181     },
28182     
28183     onRender : function(ct, position) 
28184     {
28185         var _this = this;
28186         
28187         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28188         
28189         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28190         
28191         this.dayField = new Roo.bootstrap.ComboBox({
28192             allowBlank : this.dayAllowBlank,
28193             alwaysQuery : true,
28194             displayField : 'value',
28195             editable : false,
28196             fieldLabel : '',
28197             forceSelection : true,
28198             mode : 'local',
28199             placeholder : this.dayPlaceholder,
28200             selectOnFocus : true,
28201             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28202             triggerAction : 'all',
28203             typeAhead : true,
28204             valueField : 'value',
28205             store : new Roo.data.SimpleStore({
28206                 data : (function() {    
28207                     var days = [];
28208                     _this.fireEvent('days', _this, days);
28209                     return days;
28210                 })(),
28211                 fields : [ 'value' ]
28212             }),
28213             listeners : {
28214                 select : function (_self, record, index)
28215                 {
28216                     _this.setValue(_this.getValue());
28217                 }
28218             }
28219         });
28220
28221         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28222         
28223         this.monthField = new Roo.bootstrap.MonthField({
28224             after : '<i class=\"fa fa-calendar\"></i>',
28225             allowBlank : this.monthAllowBlank,
28226             placeholder : this.monthPlaceholder,
28227             readOnly : true,
28228             listeners : {
28229                 render : function (_self)
28230                 {
28231                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28232                         e.preventDefault();
28233                         _self.focus();
28234                     });
28235                 },
28236                 select : function (_self, oldvalue, newvalue)
28237                 {
28238                     _this.setValue(_this.getValue());
28239                 }
28240             }
28241         });
28242         
28243         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28244         
28245         this.yearField = new Roo.bootstrap.ComboBox({
28246             allowBlank : this.yearAllowBlank,
28247             alwaysQuery : true,
28248             displayField : 'value',
28249             editable : false,
28250             fieldLabel : '',
28251             forceSelection : true,
28252             mode : 'local',
28253             placeholder : this.yearPlaceholder,
28254             selectOnFocus : true,
28255             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28256             triggerAction : 'all',
28257             typeAhead : true,
28258             valueField : 'value',
28259             store : new Roo.data.SimpleStore({
28260                 data : (function() {
28261                     var years = [];
28262                     _this.fireEvent('years', _this, years);
28263                     return years;
28264                 })(),
28265                 fields : [ 'value' ]
28266             }),
28267             listeners : {
28268                 select : function (_self, record, index)
28269                 {
28270                     _this.setValue(_this.getValue());
28271                 }
28272             }
28273         });
28274
28275         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28276     },
28277     
28278     setValue : function(v, format)
28279     {
28280         this.inputEl.dom.value = v;
28281         
28282         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28283         
28284         var d = Date.parseDate(v, f);
28285         
28286         if(!d){
28287             this.validate();
28288             return;
28289         }
28290         
28291         this.setDay(d.format(this.dayFormat));
28292         this.setMonth(d.format(this.monthFormat));
28293         this.setYear(d.format(this.yearFormat));
28294         
28295         this.validate();
28296         
28297         return;
28298     },
28299     
28300     setDay : function(v)
28301     {
28302         this.dayField.setValue(v);
28303         this.inputEl.dom.value = this.getValue();
28304         this.validate();
28305         return;
28306     },
28307     
28308     setMonth : function(v)
28309     {
28310         this.monthField.setValue(v, true);
28311         this.inputEl.dom.value = this.getValue();
28312         this.validate();
28313         return;
28314     },
28315     
28316     setYear : function(v)
28317     {
28318         this.yearField.setValue(v);
28319         this.inputEl.dom.value = this.getValue();
28320         this.validate();
28321         return;
28322     },
28323     
28324     getDay : function()
28325     {
28326         return this.dayField.getValue();
28327     },
28328     
28329     getMonth : function()
28330     {
28331         return this.monthField.getValue();
28332     },
28333     
28334     getYear : function()
28335     {
28336         return this.yearField.getValue();
28337     },
28338     
28339     getValue : function()
28340     {
28341         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28342         
28343         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28344         
28345         return date;
28346     },
28347     
28348     reset : function()
28349     {
28350         this.setDay('');
28351         this.setMonth('');
28352         this.setYear('');
28353         this.inputEl.dom.value = '';
28354         this.validate();
28355         return;
28356     },
28357     
28358     validate : function()
28359     {
28360         var d = this.dayField.validate();
28361         var m = this.monthField.validate();
28362         var y = this.yearField.validate();
28363         
28364         var valid = true;
28365         
28366         if(
28367                 (!this.dayAllowBlank && !d) ||
28368                 (!this.monthAllowBlank && !m) ||
28369                 (!this.yearAllowBlank && !y)
28370         ){
28371             valid = false;
28372         }
28373         
28374         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28375             return valid;
28376         }
28377         
28378         if(valid){
28379             this.markValid();
28380             return valid;
28381         }
28382         
28383         this.markInvalid();
28384         
28385         return valid;
28386     },
28387     
28388     markValid : function()
28389     {
28390         
28391         var label = this.el.select('label', true).first();
28392         var icon = this.el.select('i.fa-star', true).first();
28393
28394         if(label && icon){
28395             icon.remove();
28396         }
28397         
28398         this.fireEvent('valid', this);
28399     },
28400     
28401      /**
28402      * Mark this field as invalid
28403      * @param {String} msg The validation message
28404      */
28405     markInvalid : function(msg)
28406     {
28407         
28408         var label = this.el.select('label', true).first();
28409         var icon = this.el.select('i.fa-star', true).first();
28410
28411         if(label && !icon){
28412             this.el.select('.roo-date-split-field-label', true).createChild({
28413                 tag : 'i',
28414                 cls : 'text-danger fa fa-lg fa-star',
28415                 tooltip : 'This field is required',
28416                 style : 'margin-right:5px;'
28417             }, label, true);
28418         }
28419         
28420         this.fireEvent('invalid', this, msg);
28421     },
28422     
28423     clearInvalid : function()
28424     {
28425         var label = this.el.select('label', true).first();
28426         var icon = this.el.select('i.fa-star', true).first();
28427
28428         if(label && icon){
28429             icon.remove();
28430         }
28431         
28432         this.fireEvent('valid', this);
28433     },
28434     
28435     getName: function()
28436     {
28437         return this.name;
28438     }
28439     
28440 });
28441
28442  /**
28443  *
28444  * This is based on 
28445  * http://masonry.desandro.com
28446  *
28447  * The idea is to render all the bricks based on vertical width...
28448  *
28449  * The original code extends 'outlayer' - we might need to use that....
28450  * 
28451  */
28452
28453
28454 /**
28455  * @class Roo.bootstrap.LayoutMasonry
28456  * @extends Roo.bootstrap.Component
28457  * Bootstrap Layout Masonry class
28458  * 
28459  * @constructor
28460  * Create a new Element
28461  * @param {Object} config The config object
28462  */
28463
28464 Roo.bootstrap.LayoutMasonry = function(config){
28465     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28466     
28467     this.bricks = [];
28468     
28469 };
28470
28471 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28472     
28473     /**
28474      * @cfg {Boolean} isLayoutInstant = no animation?
28475      */   
28476     isLayoutInstant : false, // needed?
28477    
28478     /**
28479      * @cfg {Number} boxWidth  width of the columns
28480      */   
28481     boxWidth : 450,
28482     
28483       /**
28484      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28485      */   
28486     boxHeight : 0,
28487     
28488     /**
28489      * @cfg {Number} padWidth padding below box..
28490      */   
28491     padWidth : 10, 
28492     
28493     /**
28494      * @cfg {Number} gutter gutter width..
28495      */   
28496     gutter : 10,
28497     
28498      /**
28499      * @cfg {Number} maxCols maximum number of columns
28500      */   
28501     
28502     maxCols: 0,
28503     
28504     /**
28505      * @cfg {Boolean} isAutoInitial defalut true
28506      */   
28507     isAutoInitial : true, 
28508     
28509     containerWidth: 0,
28510     
28511     /**
28512      * @cfg {Boolean} isHorizontal defalut false
28513      */   
28514     isHorizontal : false, 
28515
28516     currentSize : null,
28517     
28518     tag: 'div',
28519     
28520     cls: '',
28521     
28522     bricks: null, //CompositeElement
28523     
28524     cols : 1,
28525     
28526     _isLayoutInited : false,
28527     
28528 //    isAlternative : false, // only use for vertical layout...
28529     
28530     /**
28531      * @cfg {Number} alternativePadWidth padding below box..
28532      */   
28533     alternativePadWidth : 50, 
28534     
28535     getAutoCreate : function(){
28536         
28537         var cfg = {
28538             tag: this.tag,
28539             cls: 'blog-masonary-wrapper ' + this.cls,
28540             cn : {
28541                 cls : 'mas-boxes masonary'
28542             }
28543         };
28544         
28545         return cfg;
28546     },
28547     
28548     getChildContainer: function( )
28549     {
28550         if (this.boxesEl) {
28551             return this.boxesEl;
28552         }
28553         
28554         this.boxesEl = this.el.select('.mas-boxes').first();
28555         
28556         return this.boxesEl;
28557     },
28558     
28559     
28560     initEvents : function()
28561     {
28562         var _this = this;
28563         
28564         if(this.isAutoInitial){
28565             Roo.log('hook children rendered');
28566             this.on('childrenrendered', function() {
28567                 Roo.log('children rendered');
28568                 _this.initial();
28569             } ,this);
28570         }
28571     },
28572     
28573     initial : function()
28574     {
28575         this.currentSize = this.el.getBox(true);
28576         
28577         Roo.EventManager.onWindowResize(this.resize, this); 
28578
28579         if(!this.isAutoInitial){
28580             this.layout();
28581             return;
28582         }
28583         
28584         this.layout();
28585         
28586         return;
28587         //this.layout.defer(500,this);
28588         
28589     },
28590     
28591     resize : function()
28592     {
28593         Roo.log('resize');
28594         
28595         var cs = this.el.getBox(true);
28596         
28597         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28598             Roo.log("no change in with or X");
28599             return;
28600         }
28601         
28602         this.currentSize = cs;
28603         
28604         this.layout();
28605         
28606     },
28607     
28608     layout : function()
28609     {   
28610         this._resetLayout();
28611         
28612         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28613         
28614         this.layoutItems( isInstant );
28615       
28616         this._isLayoutInited = true;
28617         
28618     },
28619     
28620     _resetLayout : function()
28621     {
28622         if(this.isHorizontal){
28623             this.horizontalMeasureColumns();
28624             return;
28625         }
28626         
28627         this.verticalMeasureColumns();
28628         
28629     },
28630     
28631     verticalMeasureColumns : function()
28632     {
28633         this.getContainerWidth();
28634         
28635 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28636 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28637 //            return;
28638 //        }
28639         
28640         var boxWidth = this.boxWidth + this.padWidth;
28641         
28642         if(this.containerWidth < this.boxWidth){
28643             boxWidth = this.containerWidth
28644         }
28645         
28646         var containerWidth = this.containerWidth;
28647         
28648         var cols = Math.floor(containerWidth / boxWidth);
28649         
28650         this.cols = Math.max( cols, 1 );
28651         
28652         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28653         
28654         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28655         
28656         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28657         
28658         this.colWidth = boxWidth + avail - this.padWidth;
28659         
28660         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28661         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28662     },
28663     
28664     horizontalMeasureColumns : function()
28665     {
28666         this.getContainerWidth();
28667         
28668         var boxWidth = this.boxWidth;
28669         
28670         if(this.containerWidth < boxWidth){
28671             boxWidth = this.containerWidth;
28672         }
28673         
28674         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28675         
28676         this.el.setHeight(boxWidth);
28677         
28678     },
28679     
28680     getContainerWidth : function()
28681     {
28682         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28683     },
28684     
28685     layoutItems : function( isInstant )
28686     {
28687         var items = Roo.apply([], this.bricks);
28688         
28689         if(this.isHorizontal){
28690             this._horizontalLayoutItems( items , isInstant );
28691             return;
28692         }
28693         
28694 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28695 //            this._verticalAlternativeLayoutItems( items , isInstant );
28696 //            return;
28697 //        }
28698         
28699         this._verticalLayoutItems( items , isInstant );
28700         
28701     },
28702     
28703     _verticalLayoutItems : function ( items , isInstant)
28704     {
28705         if ( !items || !items.length ) {
28706             return;
28707         }
28708         
28709         var standard = [
28710             ['xs', 'xs', 'xs', 'tall'],
28711             ['xs', 'xs', 'tall'],
28712             ['xs', 'xs', 'sm'],
28713             ['xs', 'xs', 'xs'],
28714             ['xs', 'tall'],
28715             ['xs', 'sm'],
28716             ['xs', 'xs'],
28717             ['xs'],
28718             
28719             ['sm', 'xs', 'xs'],
28720             ['sm', 'xs'],
28721             ['sm'],
28722             
28723             ['tall', 'xs', 'xs', 'xs'],
28724             ['tall', 'xs', 'xs'],
28725             ['tall', 'xs'],
28726             ['tall']
28727             
28728         ];
28729         
28730         var queue = [];
28731         
28732         var boxes = [];
28733         
28734         var box = [];
28735         
28736         Roo.each(items, function(item, k){
28737             
28738             switch (item.size) {
28739                 // these layouts take up a full box,
28740                 case 'md' :
28741                 case 'md-left' :
28742                 case 'md-right' :
28743                 case 'wide' :
28744                     
28745                     if(box.length){
28746                         boxes.push(box);
28747                         box = [];
28748                     }
28749                     
28750                     boxes.push([item]);
28751                     
28752                     break;
28753                     
28754                 case 'xs' :
28755                 case 'sm' :
28756                 case 'tall' :
28757                     
28758                     box.push(item);
28759                     
28760                     break;
28761                 default :
28762                     break;
28763                     
28764             }
28765             
28766         }, this);
28767         
28768         if(box.length){
28769             boxes.push(box);
28770             box = [];
28771         }
28772         
28773         var filterPattern = function(box, length)
28774         {
28775             if(!box.length){
28776                 return;
28777             }
28778             
28779             var match = false;
28780             
28781             var pattern = box.slice(0, length);
28782             
28783             var format = [];
28784             
28785             Roo.each(pattern, function(i){
28786                 format.push(i.size);
28787             }, this);
28788             
28789             Roo.each(standard, function(s){
28790                 
28791                 if(String(s) != String(format)){
28792                     return;
28793                 }
28794                 
28795                 match = true;
28796                 return false;
28797                 
28798             }, this);
28799             
28800             if(!match && length == 1){
28801                 return;
28802             }
28803             
28804             if(!match){
28805                 filterPattern(box, length - 1);
28806                 return;
28807             }
28808                 
28809             queue.push(pattern);
28810
28811             box = box.slice(length, box.length);
28812
28813             filterPattern(box, 4);
28814
28815             return;
28816             
28817         }
28818         
28819         Roo.each(boxes, function(box, k){
28820             
28821             if(!box.length){
28822                 return;
28823             }
28824             
28825             if(box.length == 1){
28826                 queue.push(box);
28827                 return;
28828             }
28829             
28830             filterPattern(box, 4);
28831             
28832         }, this);
28833         
28834         this._processVerticalLayoutQueue( queue, isInstant );
28835         
28836     },
28837     
28838 //    _verticalAlternativeLayoutItems : function( items , isInstant )
28839 //    {
28840 //        if ( !items || !items.length ) {
28841 //            return;
28842 //        }
28843 //
28844 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
28845 //        
28846 //    },
28847     
28848     _horizontalLayoutItems : function ( items , isInstant)
28849     {
28850         if ( !items || !items.length || items.length < 3) {
28851             return;
28852         }
28853         
28854         items.reverse();
28855         
28856         var eItems = items.slice(0, 3);
28857         
28858         items = items.slice(3, items.length);
28859         
28860         var standard = [
28861             ['xs', 'xs', 'xs', 'wide'],
28862             ['xs', 'xs', 'wide'],
28863             ['xs', 'xs', 'sm'],
28864             ['xs', 'xs', 'xs'],
28865             ['xs', 'wide'],
28866             ['xs', 'sm'],
28867             ['xs', 'xs'],
28868             ['xs'],
28869             
28870             ['sm', 'xs', 'xs'],
28871             ['sm', 'xs'],
28872             ['sm'],
28873             
28874             ['wide', 'xs', 'xs', 'xs'],
28875             ['wide', 'xs', 'xs'],
28876             ['wide', 'xs'],
28877             ['wide'],
28878             
28879             ['wide-thin']
28880         ];
28881         
28882         var queue = [];
28883         
28884         var boxes = [];
28885         
28886         var box = [];
28887         
28888         Roo.each(items, function(item, k){
28889             
28890             switch (item.size) {
28891                 case 'md' :
28892                 case 'md-left' :
28893                 case 'md-right' :
28894                 case 'tall' :
28895                     
28896                     if(box.length){
28897                         boxes.push(box);
28898                         box = [];
28899                     }
28900                     
28901                     boxes.push([item]);
28902                     
28903                     break;
28904                     
28905                 case 'xs' :
28906                 case 'sm' :
28907                 case 'wide' :
28908                 case 'wide-thin' :
28909                     
28910                     box.push(item);
28911                     
28912                     break;
28913                 default :
28914                     break;
28915                     
28916             }
28917             
28918         }, this);
28919         
28920         if(box.length){
28921             boxes.push(box);
28922             box = [];
28923         }
28924         
28925         var filterPattern = function(box, length)
28926         {
28927             if(!box.length){
28928                 return;
28929             }
28930             
28931             var match = false;
28932             
28933             var pattern = box.slice(0, length);
28934             
28935             var format = [];
28936             
28937             Roo.each(pattern, function(i){
28938                 format.push(i.size);
28939             }, this);
28940             
28941             Roo.each(standard, function(s){
28942                 
28943                 if(String(s) != String(format)){
28944                     return;
28945                 }
28946                 
28947                 match = true;
28948                 return false;
28949                 
28950             }, this);
28951             
28952             if(!match && length == 1){
28953                 return;
28954             }
28955             
28956             if(!match){
28957                 filterPattern(box, length - 1);
28958                 return;
28959             }
28960                 
28961             queue.push(pattern);
28962
28963             box = box.slice(length, box.length);
28964
28965             filterPattern(box, 4);
28966
28967             return;
28968             
28969         }
28970         
28971         Roo.each(boxes, function(box, k){
28972             
28973             if(!box.length){
28974                 return;
28975             }
28976             
28977             if(box.length == 1){
28978                 queue.push(box);
28979                 return;
28980             }
28981             
28982             filterPattern(box, 4);
28983             
28984         }, this);
28985         
28986         
28987         var prune = [];
28988         
28989         var pos = this.el.getBox(true);
28990         
28991         var minX = pos.x;
28992         
28993         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
28994         
28995         var hit_end = false;
28996         
28997         Roo.each(queue, function(box){
28998             
28999             if(hit_end){
29000                 
29001                 Roo.each(box, function(b){
29002                 
29003                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29004                     b.el.hide();
29005
29006                 }, this);
29007
29008                 return;
29009             }
29010             
29011             var mx = 0;
29012             
29013             Roo.each(box, function(b){
29014                 
29015                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29016                 b.el.show();
29017
29018                 mx = Math.max(mx, b.x);
29019                 
29020             }, this);
29021             
29022             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29023             
29024             if(maxX < minX){
29025                 
29026                 Roo.each(box, function(b){
29027                 
29028                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29029                     b.el.hide();
29030                     
29031                 }, this);
29032                 
29033                 hit_end = true;
29034                 
29035                 return;
29036             }
29037             
29038             prune.push(box);
29039             
29040         }, this);
29041         
29042         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29043     },
29044     
29045     /** Sets position of item in DOM
29046     * @param {Element} item
29047     * @param {Number} x - horizontal position
29048     * @param {Number} y - vertical position
29049     * @param {Boolean} isInstant - disables transitions
29050     */
29051     _processVerticalLayoutQueue : function( queue, isInstant )
29052     {
29053         var pos = this.el.getBox(true);
29054         var x = pos.x;
29055         var y = pos.y;
29056         var maxY = [];
29057         
29058         for (var i = 0; i < this.cols; i++){
29059             maxY[i] = pos.y;
29060         }
29061         
29062         Roo.each(queue, function(box, k){
29063             
29064             var col = k % this.cols;
29065             
29066             Roo.each(box, function(b,kk){
29067                 
29068                 b.el.position('absolute');
29069                 
29070                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29071                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29072                 
29073                 if(b.size == 'md-left' || b.size == 'md-right'){
29074                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29075                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29076                 }
29077                 
29078                 b.el.setWidth(width);
29079                 b.el.setHeight(height);
29080                 // iframe?
29081                 b.el.select('iframe',true).setSize(width,height);
29082                 
29083             }, this);
29084             
29085             for (var i = 0; i < this.cols; i++){
29086                 
29087                 if(maxY[i] < maxY[col]){
29088                     col = i;
29089                     continue;
29090                 }
29091                 
29092                 col = Math.min(col, i);
29093                 
29094             }
29095             
29096             x = pos.x + col * (this.colWidth + this.padWidth);
29097             
29098             y = maxY[col];
29099             
29100             var positions = [];
29101             
29102             switch (box.length){
29103                 case 1 :
29104                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29105                     break;
29106                 case 2 :
29107                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29108                     break;
29109                 case 3 :
29110                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29111                     break;
29112                 case 4 :
29113                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29114                     break;
29115                 default :
29116                     break;
29117             }
29118             
29119             Roo.each(box, function(b,kk){
29120                 
29121                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29122                 
29123                 var sz = b.el.getSize();
29124                 
29125                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29126                 
29127             }, this);
29128             
29129         }, this);
29130         
29131         var mY = 0;
29132         
29133         for (var i = 0; i < this.cols; i++){
29134             mY = Math.max(mY, maxY[i]);
29135         }
29136         
29137         this.el.setHeight(mY - pos.y);
29138         
29139     },
29140     
29141 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29142 //    {
29143 //        var pos = this.el.getBox(true);
29144 //        var x = pos.x;
29145 //        var y = pos.y;
29146 //        var maxX = pos.right;
29147 //        
29148 //        var maxHeight = 0;
29149 //        
29150 //        Roo.each(items, function(item, k){
29151 //            
29152 //            var c = k % 2;
29153 //            
29154 //            item.el.position('absolute');
29155 //                
29156 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29157 //
29158 //            item.el.setWidth(width);
29159 //
29160 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29161 //
29162 //            item.el.setHeight(height);
29163 //            
29164 //            if(c == 0){
29165 //                item.el.setXY([x, y], isInstant ? false : true);
29166 //            } else {
29167 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29168 //            }
29169 //            
29170 //            y = y + height + this.alternativePadWidth;
29171 //            
29172 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29173 //            
29174 //        }, this);
29175 //        
29176 //        this.el.setHeight(maxHeight);
29177 //        
29178 //    },
29179     
29180     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29181     {
29182         var pos = this.el.getBox(true);
29183         
29184         var minX = pos.x;
29185         var minY = pos.y;
29186         
29187         var maxX = pos.right;
29188         
29189         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29190         
29191         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29192         
29193         Roo.each(queue, function(box, k){
29194             
29195             Roo.each(box, function(b, kk){
29196                 
29197                 b.el.position('absolute');
29198                 
29199                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29200                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29201                 
29202                 if(b.size == 'md-left' || b.size == 'md-right'){
29203                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29204                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29205                 }
29206                 
29207                 b.el.setWidth(width);
29208                 b.el.setHeight(height);
29209                 
29210             }, this);
29211             
29212             if(!box.length){
29213                 return;
29214             }
29215             
29216             var positions = [];
29217             
29218             switch (box.length){
29219                 case 1 :
29220                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29221                     break;
29222                 case 2 :
29223                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29224                     break;
29225                 case 3 :
29226                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29227                     break;
29228                 case 4 :
29229                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29230                     break;
29231                 default :
29232                     break;
29233             }
29234             
29235             Roo.each(box, function(b,kk){
29236                 
29237                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29238                 
29239                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29240                 
29241             }, this);
29242             
29243         }, this);
29244         
29245     },
29246     
29247     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29248     {
29249         Roo.each(eItems, function(b,k){
29250             
29251             b.size = (k == 0) ? 'sm' : 'xs';
29252             b.x = (k == 0) ? 2 : 1;
29253             b.y = (k == 0) ? 2 : 1;
29254             
29255             b.el.position('absolute');
29256             
29257             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29258                 
29259             b.el.setWidth(width);
29260             
29261             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29262             
29263             b.el.setHeight(height);
29264             
29265         }, this);
29266
29267         var positions = [];
29268         
29269         positions.push({
29270             x : maxX - this.unitWidth * 2 - this.gutter,
29271             y : minY
29272         });
29273         
29274         positions.push({
29275             x : maxX - this.unitWidth,
29276             y : minY + (this.unitWidth + this.gutter) * 2
29277         });
29278         
29279         positions.push({
29280             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29281             y : minY
29282         });
29283         
29284         Roo.each(eItems, function(b,k){
29285             
29286             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29287
29288         }, this);
29289         
29290     },
29291     
29292     getVerticalOneBoxColPositions : function(x, y, box)
29293     {
29294         var pos = [];
29295         
29296         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29297         
29298         if(box[0].size == 'md-left'){
29299             rand = 0;
29300         }
29301         
29302         if(box[0].size == 'md-right'){
29303             rand = 1;
29304         }
29305         
29306         pos.push({
29307             x : x + (this.unitWidth + this.gutter) * rand,
29308             y : y
29309         });
29310         
29311         return pos;
29312     },
29313     
29314     getVerticalTwoBoxColPositions : function(x, y, box)
29315     {
29316         var pos = [];
29317         
29318         if(box[0].size == 'xs'){
29319             
29320             pos.push({
29321                 x : x,
29322                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29323             });
29324
29325             pos.push({
29326                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29327                 y : y
29328             });
29329             
29330             return pos;
29331             
29332         }
29333         
29334         pos.push({
29335             x : x,
29336             y : y
29337         });
29338
29339         pos.push({
29340             x : x + (this.unitWidth + this.gutter) * 2,
29341             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29342         });
29343         
29344         return pos;
29345         
29346     },
29347     
29348     getVerticalThreeBoxColPositions : function(x, y, box)
29349     {
29350         var pos = [];
29351         
29352         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29353             
29354             pos.push({
29355                 x : x,
29356                 y : y
29357             });
29358
29359             pos.push({
29360                 x : x + (this.unitWidth + this.gutter) * 1,
29361                 y : y
29362             });
29363             
29364             pos.push({
29365                 x : x + (this.unitWidth + this.gutter) * 2,
29366                 y : y
29367             });
29368             
29369             return pos;
29370             
29371         }
29372         
29373         if(box[0].size == 'xs' && box[1].size == 'xs'){
29374             
29375             pos.push({
29376                 x : x,
29377                 y : y
29378             });
29379
29380             pos.push({
29381                 x : x,
29382                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29383             });
29384             
29385             pos.push({
29386                 x : x + (this.unitWidth + this.gutter) * 1,
29387                 y : y
29388             });
29389             
29390             return pos;
29391             
29392         }
29393         
29394         pos.push({
29395             x : x,
29396             y : y
29397         });
29398
29399         pos.push({
29400             x : x + (this.unitWidth + this.gutter) * 2,
29401             y : y
29402         });
29403
29404         pos.push({
29405             x : x + (this.unitWidth + this.gutter) * 2,
29406             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29407         });
29408             
29409         return pos;
29410         
29411     },
29412     
29413     getVerticalFourBoxColPositions : function(x, y, box)
29414     {
29415         var pos = [];
29416         
29417         if(box[0].size == 'xs'){
29418             
29419             pos.push({
29420                 x : x,
29421                 y : y
29422             });
29423
29424             pos.push({
29425                 x : x,
29426                 y : y + (this.unitHeight + this.gutter) * 1
29427             });
29428             
29429             pos.push({
29430                 x : x,
29431                 y : y + (this.unitHeight + this.gutter) * 2
29432             });
29433             
29434             pos.push({
29435                 x : x + (this.unitWidth + this.gutter) * 1,
29436                 y : y
29437             });
29438             
29439             return pos;
29440             
29441         }
29442         
29443         pos.push({
29444             x : x,
29445             y : y
29446         });
29447
29448         pos.push({
29449             x : x + (this.unitWidth + this.gutter) * 2,
29450             y : y
29451         });
29452
29453         pos.push({
29454             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29455             y : y + (this.unitHeight + this.gutter) * 1
29456         });
29457
29458         pos.push({
29459             x : x + (this.unitWidth + this.gutter) * 2,
29460             y : y + (this.unitWidth + this.gutter) * 2
29461         });
29462
29463         return pos;
29464         
29465     },
29466     
29467     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29468     {
29469         var pos = [];
29470         
29471         if(box[0].size == 'md-left'){
29472             pos.push({
29473                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29474                 y : minY
29475             });
29476             
29477             return pos;
29478         }
29479         
29480         if(box[0].size == 'md-right'){
29481             pos.push({
29482                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29483                 y : minY + (this.unitWidth + this.gutter) * 1
29484             });
29485             
29486             return pos;
29487         }
29488         
29489         var rand = Math.floor(Math.random() * (4 - box[0].y));
29490         
29491         pos.push({
29492             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29493             y : minY + (this.unitWidth + this.gutter) * rand
29494         });
29495         
29496         return pos;
29497         
29498     },
29499     
29500     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29501     {
29502         var pos = [];
29503         
29504         if(box[0].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[1].x - this.gutter * (box[1].x - 1),
29513                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29514             });
29515             
29516             return pos;
29517             
29518         }
29519         
29520         pos.push({
29521             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29522             y : minY
29523         });
29524
29525         pos.push({
29526             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29527             y : minY + (this.unitWidth + this.gutter) * 2
29528         });
29529         
29530         return pos;
29531         
29532     },
29533     
29534     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29535     {
29536         var pos = [];
29537         
29538         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29539             
29540             pos.push({
29541                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29542                 y : minY
29543             });
29544
29545             pos.push({
29546                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29547                 y : minY + (this.unitWidth + this.gutter) * 1
29548             });
29549             
29550             pos.push({
29551                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29552                 y : minY + (this.unitWidth + this.gutter) * 2
29553             });
29554             
29555             return pos;
29556             
29557         }
29558         
29559         if(box[0].size == 'xs' && box[1].size == 'xs'){
29560             
29561             pos.push({
29562                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29563                 y : minY
29564             });
29565
29566             pos.push({
29567                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29568                 y : minY
29569             });
29570             
29571             pos.push({
29572                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29573                 y : minY + (this.unitWidth + this.gutter) * 1
29574             });
29575             
29576             return pos;
29577             
29578         }
29579         
29580         pos.push({
29581             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29582             y : minY
29583         });
29584
29585         pos.push({
29586             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29587             y : minY + (this.unitWidth + this.gutter) * 2
29588         });
29589
29590         pos.push({
29591             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29592             y : minY + (this.unitWidth + this.gutter) * 2
29593         });
29594             
29595         return pos;
29596         
29597     },
29598     
29599     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29600     {
29601         var pos = [];
29602         
29603         if(box[0].size == 'xs'){
29604             
29605             pos.push({
29606                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29607                 y : minY
29608             });
29609
29610             pos.push({
29611                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29612                 y : minY
29613             });
29614             
29615             pos.push({
29616                 x : maxX - this.unitWidth * box[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),
29617                 y : minY
29618             });
29619             
29620             pos.push({
29621                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29622                 y : minY + (this.unitWidth + this.gutter) * 1
29623             });
29624             
29625             return pos;
29626             
29627         }
29628         
29629         pos.push({
29630             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29631             y : minY
29632         });
29633         
29634         pos.push({
29635             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29636             y : minY + (this.unitWidth + this.gutter) * 2
29637         });
29638         
29639         pos.push({
29640             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29641             y : minY + (this.unitWidth + this.gutter) * 2
29642         });
29643         
29644         pos.push({
29645             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),
29646             y : minY + (this.unitWidth + this.gutter) * 2
29647         });
29648
29649         return pos;
29650         
29651     }
29652     
29653 });
29654
29655  
29656
29657  /**
29658  *
29659  * This is based on 
29660  * http://masonry.desandro.com
29661  *
29662  * The idea is to render all the bricks based on vertical width...
29663  *
29664  * The original code extends 'outlayer' - we might need to use that....
29665  * 
29666  */
29667
29668
29669 /**
29670  * @class Roo.bootstrap.LayoutMasonryAuto
29671  * @extends Roo.bootstrap.Component
29672  * Bootstrap Layout Masonry class
29673  * 
29674  * @constructor
29675  * Create a new Element
29676  * @param {Object} config The config object
29677  */
29678
29679 Roo.bootstrap.LayoutMasonryAuto = function(config){
29680     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29681 };
29682
29683 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
29684     
29685       /**
29686      * @cfg {Boolean} isFitWidth  - resize the width..
29687      */   
29688     isFitWidth : false,  // options..
29689     /**
29690      * @cfg {Boolean} isOriginLeft = left align?
29691      */   
29692     isOriginLeft : true,
29693     /**
29694      * @cfg {Boolean} isOriginTop = top align?
29695      */   
29696     isOriginTop : false,
29697     /**
29698      * @cfg {Boolean} isLayoutInstant = no animation?
29699      */   
29700     isLayoutInstant : false, // needed?
29701     /**
29702      * @cfg {Boolean} isResizingContainer = not sure if this is used..
29703      */   
29704     isResizingContainer : true,
29705     /**
29706      * @cfg {Number} columnWidth  width of the columns 
29707      */   
29708     
29709     columnWidth : 0,
29710     
29711     /**
29712      * @cfg {Number} maxCols maximum number of columns
29713      */   
29714     
29715     maxCols: 0,
29716     /**
29717      * @cfg {Number} padHeight padding below box..
29718      */   
29719     
29720     padHeight : 10, 
29721     
29722     /**
29723      * @cfg {Boolean} isAutoInitial defalut true
29724      */   
29725     
29726     isAutoInitial : true, 
29727     
29728     // private?
29729     gutter : 0,
29730     
29731     containerWidth: 0,
29732     initialColumnWidth : 0,
29733     currentSize : null,
29734     
29735     colYs : null, // array.
29736     maxY : 0,
29737     padWidth: 10,
29738     
29739     
29740     tag: 'div',
29741     cls: '',
29742     bricks: null, //CompositeElement
29743     cols : 0, // array?
29744     // element : null, // wrapped now this.el
29745     _isLayoutInited : null, 
29746     
29747     
29748     getAutoCreate : function(){
29749         
29750         var cfg = {
29751             tag: this.tag,
29752             cls: 'blog-masonary-wrapper ' + this.cls,
29753             cn : {
29754                 cls : 'mas-boxes masonary'
29755             }
29756         };
29757         
29758         return cfg;
29759     },
29760     
29761     getChildContainer: function( )
29762     {
29763         if (this.boxesEl) {
29764             return this.boxesEl;
29765         }
29766         
29767         this.boxesEl = this.el.select('.mas-boxes').first();
29768         
29769         return this.boxesEl;
29770     },
29771     
29772     
29773     initEvents : function()
29774     {
29775         var _this = this;
29776         
29777         if(this.isAutoInitial){
29778             Roo.log('hook children rendered');
29779             this.on('childrenrendered', function() {
29780                 Roo.log('children rendered');
29781                 _this.initial();
29782             } ,this);
29783         }
29784         
29785     },
29786     
29787     initial : function()
29788     {
29789         this.reloadItems();
29790
29791         this.currentSize = this.el.getBox(true);
29792
29793         /// was window resize... - let's see if this works..
29794         Roo.EventManager.onWindowResize(this.resize, this); 
29795
29796         if(!this.isAutoInitial){
29797             this.layout();
29798             return;
29799         }
29800         
29801         this.layout.defer(500,this);
29802     },
29803     
29804     reloadItems: function()
29805     {
29806         this.bricks = this.el.select('.masonry-brick', true);
29807         
29808         this.bricks.each(function(b) {
29809             //Roo.log(b.getSize());
29810             if (!b.attr('originalwidth')) {
29811                 b.attr('originalwidth',  b.getSize().width);
29812             }
29813             
29814         });
29815         
29816         Roo.log(this.bricks.elements.length);
29817     },
29818     
29819     resize : function()
29820     {
29821         Roo.log('resize');
29822         var cs = this.el.getBox(true);
29823         
29824         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29825             Roo.log("no change in with or X");
29826             return;
29827         }
29828         this.currentSize = cs;
29829         this.layout();
29830     },
29831     
29832     layout : function()
29833     {
29834          Roo.log('layout');
29835         this._resetLayout();
29836         //this._manageStamps();
29837       
29838         // don't animate first layout
29839         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29840         this.layoutItems( isInstant );
29841       
29842         // flag for initalized
29843         this._isLayoutInited = true;
29844     },
29845     
29846     layoutItems : function( isInstant )
29847     {
29848         //var items = this._getItemsForLayout( this.items );
29849         // original code supports filtering layout items.. we just ignore it..
29850         
29851         this._layoutItems( this.bricks , isInstant );
29852       
29853         this._postLayout();
29854     },
29855     _layoutItems : function ( items , isInstant)
29856     {
29857        //this.fireEvent( 'layout', this, items );
29858     
29859
29860         if ( !items || !items.elements.length ) {
29861           // no items, emit event with empty array
29862             return;
29863         }
29864
29865         var queue = [];
29866         items.each(function(item) {
29867             Roo.log("layout item");
29868             Roo.log(item);
29869             // get x/y object from method
29870             var position = this._getItemLayoutPosition( item );
29871             // enqueue
29872             position.item = item;
29873             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29874             queue.push( position );
29875         }, this);
29876       
29877         this._processLayoutQueue( queue );
29878     },
29879     /** Sets position of item in DOM
29880     * @param {Element} item
29881     * @param {Number} x - horizontal position
29882     * @param {Number} y - vertical position
29883     * @param {Boolean} isInstant - disables transitions
29884     */
29885     _processLayoutQueue : function( queue )
29886     {
29887         for ( var i=0, len = queue.length; i < len; i++ ) {
29888             var obj = queue[i];
29889             obj.item.position('absolute');
29890             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29891         }
29892     },
29893       
29894     
29895     /**
29896     * Any logic you want to do after each layout,
29897     * i.e. size the container
29898     */
29899     _postLayout : function()
29900     {
29901         this.resizeContainer();
29902     },
29903     
29904     resizeContainer : function()
29905     {
29906         if ( !this.isResizingContainer ) {
29907             return;
29908         }
29909         var size = this._getContainerSize();
29910         if ( size ) {
29911             this.el.setSize(size.width,size.height);
29912             this.boxesEl.setSize(size.width,size.height);
29913         }
29914     },
29915     
29916     
29917     
29918     _resetLayout : function()
29919     {
29920         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
29921         this.colWidth = this.el.getWidth();
29922         //this.gutter = this.el.getWidth(); 
29923         
29924         this.measureColumns();
29925
29926         // reset column Y
29927         var i = this.cols;
29928         this.colYs = [];
29929         while (i--) {
29930             this.colYs.push( 0 );
29931         }
29932     
29933         this.maxY = 0;
29934     },
29935
29936     measureColumns : function()
29937     {
29938         this.getContainerWidth();
29939       // if columnWidth is 0, default to outerWidth of first item
29940         if ( !this.columnWidth ) {
29941             var firstItem = this.bricks.first();
29942             Roo.log(firstItem);
29943             this.columnWidth  = this.containerWidth;
29944             if (firstItem && firstItem.attr('originalwidth') ) {
29945                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
29946             }
29947             // columnWidth fall back to item of first element
29948             Roo.log("set column width?");
29949                         this.initialColumnWidth = this.columnWidth  ;
29950
29951             // if first elem has no width, default to size of container
29952             
29953         }
29954         
29955         
29956         if (this.initialColumnWidth) {
29957             this.columnWidth = this.initialColumnWidth;
29958         }
29959         
29960         
29961             
29962         // column width is fixed at the top - however if container width get's smaller we should
29963         // reduce it...
29964         
29965         // this bit calcs how man columns..
29966             
29967         var columnWidth = this.columnWidth += this.gutter;
29968       
29969         // calculate columns
29970         var containerWidth = this.containerWidth + this.gutter;
29971         
29972         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
29973         // fix rounding errors, typically with gutters
29974         var excess = columnWidth - containerWidth % columnWidth;
29975         
29976         
29977         // if overshoot is less than a pixel, round up, otherwise floor it
29978         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
29979         cols = Math[ mathMethod ]( cols );
29980         this.cols = Math.max( cols, 1 );
29981         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29982         
29983          // padding positioning..
29984         var totalColWidth = this.cols * this.columnWidth;
29985         var padavail = this.containerWidth - totalColWidth;
29986         // so for 2 columns - we need 3 'pads'
29987         
29988         var padNeeded = (1+this.cols) * this.padWidth;
29989         
29990         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
29991         
29992         this.columnWidth += padExtra
29993         //this.padWidth = Math.floor(padavail /  ( this.cols));
29994         
29995         // adjust colum width so that padding is fixed??
29996         
29997         // we have 3 columns ... total = width * 3
29998         // we have X left over... that should be used by 
29999         
30000         //if (this.expandC) {
30001             
30002         //}
30003         
30004         
30005         
30006     },
30007     
30008     getContainerWidth : function()
30009     {
30010        /* // container is parent if fit width
30011         var container = this.isFitWidth ? this.element.parentNode : this.element;
30012         // check that this.size and size are there
30013         // IE8 triggers resize on body size change, so they might not be
30014         
30015         var size = getSize( container );  //FIXME
30016         this.containerWidth = size && size.innerWidth; //FIXME
30017         */
30018          
30019         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30020         
30021     },
30022     
30023     _getItemLayoutPosition : function( item )  // what is item?
30024     {
30025         // we resize the item to our columnWidth..
30026       
30027         item.setWidth(this.columnWidth);
30028         item.autoBoxAdjust  = false;
30029         
30030         var sz = item.getSize();
30031  
30032         // how many columns does this brick span
30033         var remainder = this.containerWidth % this.columnWidth;
30034         
30035         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30036         // round if off by 1 pixel, otherwise use ceil
30037         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30038         colSpan = Math.min( colSpan, this.cols );
30039         
30040         // normally this should be '1' as we dont' currently allow multi width columns..
30041         
30042         var colGroup = this._getColGroup( colSpan );
30043         // get the minimum Y value from the columns
30044         var minimumY = Math.min.apply( Math, colGroup );
30045         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30046         
30047         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30048          
30049         // position the brick
30050         var position = {
30051             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30052             y: this.currentSize.y + minimumY + this.padHeight
30053         };
30054         
30055         Roo.log(position);
30056         // apply setHeight to necessary columns
30057         var setHeight = minimumY + sz.height + this.padHeight;
30058         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30059         
30060         var setSpan = this.cols + 1 - colGroup.length;
30061         for ( var i = 0; i < setSpan; i++ ) {
30062           this.colYs[ shortColIndex + i ] = setHeight ;
30063         }
30064       
30065         return position;
30066     },
30067     
30068     /**
30069      * @param {Number} colSpan - number of columns the element spans
30070      * @returns {Array} colGroup
30071      */
30072     _getColGroup : function( colSpan )
30073     {
30074         if ( colSpan < 2 ) {
30075           // if brick spans only one column, use all the column Ys
30076           return this.colYs;
30077         }
30078       
30079         var colGroup = [];
30080         // how many different places could this brick fit horizontally
30081         var groupCount = this.cols + 1 - colSpan;
30082         // for each group potential horizontal position
30083         for ( var i = 0; i < groupCount; i++ ) {
30084           // make an array of colY values for that one group
30085           var groupColYs = this.colYs.slice( i, i + colSpan );
30086           // and get the max value of the array
30087           colGroup[i] = Math.max.apply( Math, groupColYs );
30088         }
30089         return colGroup;
30090     },
30091     /*
30092     _manageStamp : function( stamp )
30093     {
30094         var stampSize =  stamp.getSize();
30095         var offset = stamp.getBox();
30096         // get the columns that this stamp affects
30097         var firstX = this.isOriginLeft ? offset.x : offset.right;
30098         var lastX = firstX + stampSize.width;
30099         var firstCol = Math.floor( firstX / this.columnWidth );
30100         firstCol = Math.max( 0, firstCol );
30101         
30102         var lastCol = Math.floor( lastX / this.columnWidth );
30103         // lastCol should not go over if multiple of columnWidth #425
30104         lastCol -= lastX % this.columnWidth ? 0 : 1;
30105         lastCol = Math.min( this.cols - 1, lastCol );
30106         
30107         // set colYs to bottom of the stamp
30108         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30109             stampSize.height;
30110             
30111         for ( var i = firstCol; i <= lastCol; i++ ) {
30112           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30113         }
30114     },
30115     */
30116     
30117     _getContainerSize : function()
30118     {
30119         this.maxY = Math.max.apply( Math, this.colYs );
30120         var size = {
30121             height: this.maxY
30122         };
30123       
30124         if ( this.isFitWidth ) {
30125             size.width = this._getContainerFitWidth();
30126         }
30127       
30128         return size;
30129     },
30130     
30131     _getContainerFitWidth : function()
30132     {
30133         var unusedCols = 0;
30134         // count unused columns
30135         var i = this.cols;
30136         while ( --i ) {
30137           if ( this.colYs[i] !== 0 ) {
30138             break;
30139           }
30140           unusedCols++;
30141         }
30142         // fit container to columns that have been used
30143         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30144     },
30145     
30146     needsResizeLayout : function()
30147     {
30148         var previousWidth = this.containerWidth;
30149         this.getContainerWidth();
30150         return previousWidth !== this.containerWidth;
30151     }
30152  
30153 });
30154
30155  
30156
30157  /*
30158  * - LGPL
30159  *
30160  * element
30161  * 
30162  */
30163
30164 /**
30165  * @class Roo.bootstrap.MasonryBrick
30166  * @extends Roo.bootstrap.Component
30167  * Bootstrap MasonryBrick class
30168  * 
30169  * @constructor
30170  * Create a new MasonryBrick
30171  * @param {Object} config The config object
30172  */
30173
30174 Roo.bootstrap.MasonryBrick = function(config){
30175     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30176     
30177     this.addEvents({
30178         // raw events
30179         /**
30180          * @event click
30181          * When a MasonryBrick is clcik
30182          * @param {Roo.bootstrap.MasonryBrick} this
30183          * @param {Roo.EventObject} e
30184          */
30185         "click" : true
30186     });
30187 };
30188
30189 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30190     
30191     /**
30192      * @cfg {String} title
30193      */   
30194     title : '',
30195     /**
30196      * @cfg {String} html
30197      */   
30198     html : '',
30199     /**
30200      * @cfg {String} bgimage
30201      */   
30202     bgimage : '',
30203     /**
30204      * @cfg {String} videourl
30205      */   
30206     videourl : '',
30207     /**
30208      * @cfg {String} cls
30209      */   
30210     cls : '',
30211     /**
30212      * @cfg {String} href
30213      */   
30214     href : '',
30215     /**
30216      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30217      */   
30218     size : 'xs',
30219     
30220     /**
30221      * @cfg {String} (center|bottom) placetitle
30222      */   
30223     placetitle : '',
30224     
30225     getAutoCreate : function()
30226     {
30227         var cls = 'masonry-brick';
30228         
30229         if(this.href.length){
30230             cls += ' masonry-brick-link';
30231         }
30232         
30233         if(this.bgimage.length){
30234             cls += ' masonry-brick-image';
30235         }
30236         
30237         if(this.size){
30238             cls += ' masonry-' + this.size + '-brick';
30239         }
30240         
30241         if(this.placetitle.length){
30242             
30243             switch (this.placetitle) {
30244                 case 'center' :
30245                     cls += ' masonry-center-title';
30246                     break;
30247                 case 'bottom' :
30248                     cls += ' masonry-bottom-title';
30249                     break;
30250                 default:
30251                     break;
30252             }
30253             
30254         } else {
30255             if(!this.html.length && !this.bgimage.length){
30256                 cls += ' masonry-center-title';
30257             }
30258
30259             if(!this.html.length && this.bgimage.length){
30260                 cls += ' masonry-bottom-title';
30261             }
30262         }
30263         
30264         if(this.cls){
30265             cls += ' ' + this.cls;
30266         }
30267         
30268         var cfg = {
30269             tag: (this.href.length) ? 'a' : 'div',
30270             cls: cls,
30271             cn: [
30272                 {
30273                     tag: 'div',
30274                     cls: 'masonry-brick-paragraph',
30275                     cn: []
30276                 }
30277             ]
30278         };
30279         
30280         if(this.href.length){
30281             cfg.href = this.href;
30282         }
30283         
30284         var cn = cfg.cn[0].cn;
30285         
30286         if(this.title.length){
30287             cn.push({
30288                 tag: 'h4',
30289                 cls: 'masonry-brick-title',
30290                 html: this.title
30291             });
30292         }
30293         
30294         if(this.html.length){
30295             cn.push({
30296                 tag: 'p',
30297                 cls: 'masonry-brick-text',
30298                 html: this.html
30299             });
30300         }  
30301         if (!this.title.length && !this.html.length) {
30302             cfg.cn[0].cls += ' hide';
30303         }
30304         
30305         if(this.bgimage.length){
30306             cfg.cn.push({
30307                 tag: 'img',
30308                 cls: 'masonry-brick-image-view',
30309                 src: this.bgimage
30310             });
30311         }
30312         if(this.videourl.length){
30313             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30314             // youtube support only?
30315             cfg.cn.push({
30316                 tag: 'iframe',
30317                 cls: 'masonry-brick-image-view',
30318                 src: vurl,
30319                 frameborder : 0,
30320                 allowfullscreen : true
30321             });
30322             
30323             
30324         }
30325         return cfg;
30326         
30327     },
30328     
30329     initEvents: function() 
30330     {
30331         switch (this.size) {
30332             case 'xs' :
30333 //                this.intSize = 1;
30334                 this.x = 1;
30335                 this.y = 1;
30336                 break;
30337             case 'sm' :
30338 //                this.intSize = 2;
30339                 this.x = 2;
30340                 this.y = 2;
30341                 break;
30342             case 'md' :
30343             case 'md-left' :
30344             case 'md-right' :
30345 //                this.intSize = 3;
30346                 this.x = 3;
30347                 this.y = 3;
30348                 break;
30349             case 'tall' :
30350 //                this.intSize = 3;
30351                 this.x = 2;
30352                 this.y = 3;
30353                 break;
30354             case 'wide' :
30355 //                this.intSize = 3;
30356                 this.x = 3;
30357                 this.y = 2;
30358                 break;
30359             case 'wide-thin' :
30360 //                this.intSize = 3;
30361                 this.x = 3;
30362                 this.y = 1;
30363                 break;
30364                         
30365             default :
30366                 break;
30367         }
30368         
30369         
30370         
30371         if(Roo.isTouch){
30372             this.el.on('touchstart', this.onTouchStart, this);
30373             this.el.on('touchmove', this.onTouchMove, this);
30374             this.el.on('touchend', this.onTouchEnd, this);
30375             this.el.on('contextmenu', this.onContextMenu, this);
30376         } else {
30377             this.el.on('mouseenter'  ,this.enter, this);
30378             this.el.on('mouseleave', this.leave, this);
30379         }
30380         
30381         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30382             this.parent().bricks.push(this);   
30383         }
30384         
30385     },
30386     
30387     onClick: function(e, el)
30388     {
30389        // alert('click');
30390         
30391         if(!Roo.isTouch){
30392             return;
30393         }
30394         
30395         var time = this.endTimer - this.startTimer;
30396         
30397         //alert(time);
30398         
30399         if(time < 1000){
30400             return;
30401         }
30402         
30403         e.preventDefault();
30404     },
30405     
30406     enter: function(e, el)
30407     {
30408         e.preventDefault();
30409         
30410         if(this.bgimage.length && this.html.length){
30411             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30412         }
30413     },
30414     
30415     leave: function(e, el)
30416     {
30417         e.preventDefault();
30418         
30419         if(this.bgimage.length && this.html.length){
30420             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30421         }
30422     },
30423     
30424     onTouchStart: function(e, el)
30425     {
30426 //        e.preventDefault();
30427         
30428         if(!this.bgimage.length || !this.html.length){
30429             return;
30430         }
30431         
30432         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30433         
30434         this.timer = new Date().getTime();
30435         
30436         this.touchmoved = false;
30437     },
30438     
30439     onTouchMove: function(e, el)
30440     {
30441         this.touchmoved = true;
30442     },
30443     onContextMenu : function(e,el)
30444     {
30445             e.preventDefault();
30446             e.stopPropagation();
30447             return false;
30448     },
30449     
30450     
30451     onTouchEnd: function(e, el)
30452     {
30453 //        e.preventDefault();
30454         
30455         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30456             
30457             this.leave(e,el);
30458             
30459             return;
30460         }
30461         
30462         if(!this.bgimage.length || !this.html.length){
30463             
30464             if(this.href.length){
30465                 window.location.href = this.href;
30466             }
30467             
30468             return;
30469         }
30470         
30471         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30472         
30473         window.location.href = this.href;
30474     }
30475     
30476 });
30477
30478  
30479
30480  /*
30481  * - LGPL
30482  *
30483  * element
30484  * 
30485  */
30486
30487 /**
30488  * @class Roo.bootstrap.Brick
30489  * @extends Roo.bootstrap.Component
30490  * Bootstrap Brick class
30491  * 
30492  * @constructor
30493  * Create a new Brick
30494  * @param {Object} config The config object
30495  */
30496
30497 Roo.bootstrap.Brick = function(config){
30498     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30499     
30500     this.addEvents({
30501         // raw events
30502         /**
30503          * @event click
30504          * When a Brick is click
30505          * @param {Roo.bootstrap.Brick} this
30506          * @param {Roo.EventObject} e
30507          */
30508         "click" : true
30509     });
30510 };
30511
30512 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30513     
30514     /**
30515      * @cfg {String} title
30516      */   
30517     title : '',
30518     /**
30519      * @cfg {String} html
30520      */   
30521     html : '',
30522     /**
30523      * @cfg {String} bgimage
30524      */   
30525     bgimage : '',
30526     /**
30527      * @cfg {String} cls
30528      */   
30529     cls : '',
30530     /**
30531      * @cfg {String} href
30532      */   
30533     href : '',
30534     /**
30535      * @cfg {String} video
30536      */   
30537     video : '',
30538     /**
30539      * @cfg {Boolean} square
30540      */   
30541     square : true,
30542     
30543     getAutoCreate : function()
30544     {
30545         var cls = 'roo-brick';
30546         
30547         if(this.href.length){
30548             cls += ' roo-brick-link';
30549         }
30550         
30551         if(this.bgimage.length){
30552             cls += ' roo-brick-image';
30553         }
30554         
30555         if(!this.html.length && !this.bgimage.length){
30556             cls += ' roo-brick-center-title';
30557         }
30558         
30559         if(!this.html.length && this.bgimage.length){
30560             cls += ' roo-brick-bottom-title';
30561         }
30562         
30563         if(this.cls){
30564             cls += ' ' + this.cls;
30565         }
30566         
30567         var cfg = {
30568             tag: (this.href.length) ? 'a' : 'div',
30569             cls: cls,
30570             cn: [
30571                 {
30572                     tag: 'div',
30573                     cls: 'roo-brick-paragraph',
30574                     cn: []
30575                 }
30576             ]
30577         };
30578         
30579         if(this.href.length){
30580             cfg.href = this.href;
30581         }
30582         
30583         var cn = cfg.cn[0].cn;
30584         
30585         if(this.title.length){
30586             cn.push({
30587                 tag: 'h4',
30588                 cls: 'roo-brick-title',
30589                 html: this.title
30590             });
30591         }
30592         
30593         if(this.html.length){
30594             cn.push({
30595                 tag: 'p',
30596                 cls: 'roo-brick-text',
30597                 html: this.html
30598             });
30599         } else {
30600             cn.cls += ' hide';
30601         }
30602         
30603         if(this.bgimage.length){
30604             cfg.cn.push({
30605                 tag: 'img',
30606                 cls: 'roo-brick-image-view',
30607                 src: this.bgimage
30608             });
30609         }
30610         
30611         return cfg;
30612     },
30613     
30614     initEvents: function() 
30615     {
30616         if(this.title.length || this.html.length){
30617             this.el.on('mouseenter'  ,this.enter, this);
30618             this.el.on('mouseleave', this.leave, this);
30619         }
30620         
30621         
30622         Roo.EventManager.onWindowResize(this.resize, this); 
30623         
30624         this.resize();
30625     },
30626     
30627     resize : function()
30628     {
30629         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30630         
30631         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30632 //        paragraph.setHeight(paragraph.getWidth());
30633         
30634         if(this.bgimage.length){
30635             var image = this.el.select('.roo-brick-image-view', true).first();
30636             image.setWidth(paragraph.getWidth());
30637             image.setHeight(paragraph.getWidth());
30638         }
30639         
30640     },
30641     
30642     enter: function(e, el)
30643     {
30644         e.preventDefault();
30645         
30646         if(this.bgimage.length){
30647             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30648             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30649         }
30650     },
30651     
30652     leave: function(e, el)
30653     {
30654         e.preventDefault();
30655         
30656         if(this.bgimage.length){
30657             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30658             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30659         }
30660     }
30661     
30662 });
30663
30664  
30665
30666  /*
30667  * Based on:
30668  * Ext JS Library 1.1.1
30669  * Copyright(c) 2006-2007, Ext JS, LLC.
30670  *
30671  * Originally Released Under LGPL - original licence link has changed is not relivant.
30672  *
30673  * Fork - LGPL
30674  * <script type="text/javascript">
30675  */
30676
30677
30678 /**
30679  * @class Roo.bootstrap.SplitBar
30680  * @extends Roo.util.Observable
30681  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30682  * <br><br>
30683  * Usage:
30684  * <pre><code>
30685 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30686                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30687 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30688 split.minSize = 100;
30689 split.maxSize = 600;
30690 split.animate = true;
30691 split.on('moved', splitterMoved);
30692 </code></pre>
30693  * @constructor
30694  * Create a new SplitBar
30695  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
30696  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
30697  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30698  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
30699                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30700                         position of the SplitBar).
30701  */
30702 Roo.bootstrap.SplitBar = function(cfg){
30703     
30704     /** @private */
30705     
30706     //{
30707     //  dragElement : elm
30708     //  resizingElement: el,
30709         // optional..
30710     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30711     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
30712         // existingProxy ???
30713     //}
30714     
30715     this.el = Roo.get(cfg.dragElement, true);
30716     this.el.dom.unselectable = "on";
30717     /** @private */
30718     this.resizingEl = Roo.get(cfg.resizingElement, true);
30719
30720     /**
30721      * @private
30722      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30723      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30724      * @type Number
30725      */
30726     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30727     
30728     /**
30729      * The minimum size of the resizing element. (Defaults to 0)
30730      * @type Number
30731      */
30732     this.minSize = 0;
30733     
30734     /**
30735      * The maximum size of the resizing element. (Defaults to 2000)
30736      * @type Number
30737      */
30738     this.maxSize = 2000;
30739     
30740     /**
30741      * Whether to animate the transition to the new size
30742      * @type Boolean
30743      */
30744     this.animate = false;
30745     
30746     /**
30747      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30748      * @type Boolean
30749      */
30750     this.useShim = false;
30751     
30752     /** @private */
30753     this.shim = null;
30754     
30755     if(!cfg.existingProxy){
30756         /** @private */
30757         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30758     }else{
30759         this.proxy = Roo.get(cfg.existingProxy).dom;
30760     }
30761     /** @private */
30762     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30763     
30764     /** @private */
30765     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30766     
30767     /** @private */
30768     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30769     
30770     /** @private */
30771     this.dragSpecs = {};
30772     
30773     /**
30774      * @private The adapter to use to positon and resize elements
30775      */
30776     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30777     this.adapter.init(this);
30778     
30779     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30780         /** @private */
30781         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30782         this.el.addClass("roo-splitbar-h");
30783     }else{
30784         /** @private */
30785         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30786         this.el.addClass("roo-splitbar-v");
30787     }
30788     
30789     this.addEvents({
30790         /**
30791          * @event resize
30792          * Fires when the splitter is moved (alias for {@link #event-moved})
30793          * @param {Roo.bootstrap.SplitBar} this
30794          * @param {Number} newSize the new width or height
30795          */
30796         "resize" : true,
30797         /**
30798          * @event moved
30799          * Fires when the splitter is moved
30800          * @param {Roo.bootstrap.SplitBar} this
30801          * @param {Number} newSize the new width or height
30802          */
30803         "moved" : true,
30804         /**
30805          * @event beforeresize
30806          * Fires before the splitter is dragged
30807          * @param {Roo.bootstrap.SplitBar} this
30808          */
30809         "beforeresize" : true,
30810
30811         "beforeapply" : true
30812     });
30813
30814     Roo.util.Observable.call(this);
30815 };
30816
30817 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30818     onStartProxyDrag : function(x, y){
30819         this.fireEvent("beforeresize", this);
30820         if(!this.overlay){
30821             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
30822             o.unselectable();
30823             o.enableDisplayMode("block");
30824             // all splitbars share the same overlay
30825             Roo.bootstrap.SplitBar.prototype.overlay = o;
30826         }
30827         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30828         this.overlay.show();
30829         Roo.get(this.proxy).setDisplayed("block");
30830         var size = this.adapter.getElementSize(this);
30831         this.activeMinSize = this.getMinimumSize();;
30832         this.activeMaxSize = this.getMaximumSize();;
30833         var c1 = size - this.activeMinSize;
30834         var c2 = Math.max(this.activeMaxSize - size, 0);
30835         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30836             this.dd.resetConstraints();
30837             this.dd.setXConstraint(
30838                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
30839                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30840             );
30841             this.dd.setYConstraint(0, 0);
30842         }else{
30843             this.dd.resetConstraints();
30844             this.dd.setXConstraint(0, 0);
30845             this.dd.setYConstraint(
30846                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
30847                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30848             );
30849          }
30850         this.dragSpecs.startSize = size;
30851         this.dragSpecs.startPoint = [x, y];
30852         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30853     },
30854     
30855     /** 
30856      * @private Called after the drag operation by the DDProxy
30857      */
30858     onEndProxyDrag : function(e){
30859         Roo.get(this.proxy).setDisplayed(false);
30860         var endPoint = Roo.lib.Event.getXY(e);
30861         if(this.overlay){
30862             this.overlay.hide();
30863         }
30864         var newSize;
30865         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30866             newSize = this.dragSpecs.startSize + 
30867                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30868                     endPoint[0] - this.dragSpecs.startPoint[0] :
30869                     this.dragSpecs.startPoint[0] - endPoint[0]
30870                 );
30871         }else{
30872             newSize = this.dragSpecs.startSize + 
30873                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30874                     endPoint[1] - this.dragSpecs.startPoint[1] :
30875                     this.dragSpecs.startPoint[1] - endPoint[1]
30876                 );
30877         }
30878         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30879         if(newSize != this.dragSpecs.startSize){
30880             if(this.fireEvent('beforeapply', this, newSize) !== false){
30881                 this.adapter.setElementSize(this, newSize);
30882                 this.fireEvent("moved", this, newSize);
30883                 this.fireEvent("resize", this, newSize);
30884             }
30885         }
30886     },
30887     
30888     /**
30889      * Get the adapter this SplitBar uses
30890      * @return The adapter object
30891      */
30892     getAdapter : function(){
30893         return this.adapter;
30894     },
30895     
30896     /**
30897      * Set the adapter this SplitBar uses
30898      * @param {Object} adapter A SplitBar adapter object
30899      */
30900     setAdapter : function(adapter){
30901         this.adapter = adapter;
30902         this.adapter.init(this);
30903     },
30904     
30905     /**
30906      * Gets the minimum size for the resizing element
30907      * @return {Number} The minimum size
30908      */
30909     getMinimumSize : function(){
30910         return this.minSize;
30911     },
30912     
30913     /**
30914      * Sets the minimum size for the resizing element
30915      * @param {Number} minSize The minimum size
30916      */
30917     setMinimumSize : function(minSize){
30918         this.minSize = minSize;
30919     },
30920     
30921     /**
30922      * Gets the maximum size for the resizing element
30923      * @return {Number} The maximum size
30924      */
30925     getMaximumSize : function(){
30926         return this.maxSize;
30927     },
30928     
30929     /**
30930      * Sets the maximum size for the resizing element
30931      * @param {Number} maxSize The maximum size
30932      */
30933     setMaximumSize : function(maxSize){
30934         this.maxSize = maxSize;
30935     },
30936     
30937     /**
30938      * Sets the initialize size for the resizing element
30939      * @param {Number} size The initial size
30940      */
30941     setCurrentSize : function(size){
30942         var oldAnimate = this.animate;
30943         this.animate = false;
30944         this.adapter.setElementSize(this, size);
30945         this.animate = oldAnimate;
30946     },
30947     
30948     /**
30949      * Destroy this splitbar. 
30950      * @param {Boolean} removeEl True to remove the element
30951      */
30952     destroy : function(removeEl){
30953         if(this.shim){
30954             this.shim.remove();
30955         }
30956         this.dd.unreg();
30957         this.proxy.parentNode.removeChild(this.proxy);
30958         if(removeEl){
30959             this.el.remove();
30960         }
30961     }
30962 });
30963
30964 /**
30965  * @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.
30966  */
30967 Roo.bootstrap.SplitBar.createProxy = function(dir){
30968     var proxy = new Roo.Element(document.createElement("div"));
30969     proxy.unselectable();
30970     var cls = 'roo-splitbar-proxy';
30971     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
30972     document.body.appendChild(proxy.dom);
30973     return proxy.dom;
30974 };
30975
30976 /** 
30977  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
30978  * Default Adapter. It assumes the splitter and resizing element are not positioned
30979  * elements and only gets/sets the width of the element. Generally used for table based layouts.
30980  */
30981 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
30982 };
30983
30984 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
30985     // do nothing for now
30986     init : function(s){
30987     
30988     },
30989     /**
30990      * Called before drag operations to get the current size of the resizing element. 
30991      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30992      */
30993      getElementSize : function(s){
30994         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30995             return s.resizingEl.getWidth();
30996         }else{
30997             return s.resizingEl.getHeight();
30998         }
30999     },
31000     
31001     /**
31002      * Called after drag operations to set the size of the resizing element.
31003      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31004      * @param {Number} newSize The new size to set
31005      * @param {Function} onComplete A function to be invoked when resizing is complete
31006      */
31007     setElementSize : function(s, newSize, onComplete){
31008         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31009             if(!s.animate){
31010                 s.resizingEl.setWidth(newSize);
31011                 if(onComplete){
31012                     onComplete(s, newSize);
31013                 }
31014             }else{
31015                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31016             }
31017         }else{
31018             
31019             if(!s.animate){
31020                 s.resizingEl.setHeight(newSize);
31021                 if(onComplete){
31022                     onComplete(s, newSize);
31023                 }
31024             }else{
31025                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31026             }
31027         }
31028     }
31029 };
31030
31031 /** 
31032  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31033  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31034  * Adapter that  moves the splitter element to align with the resized sizing element. 
31035  * Used with an absolute positioned SplitBar.
31036  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31037  * document.body, make sure you assign an id to the body element.
31038  */
31039 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31040     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31041     this.container = Roo.get(container);
31042 };
31043
31044 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31045     init : function(s){
31046         this.basic.init(s);
31047     },
31048     
31049     getElementSize : function(s){
31050         return this.basic.getElementSize(s);
31051     },
31052     
31053     setElementSize : function(s, newSize, onComplete){
31054         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31055     },
31056     
31057     moveSplitter : function(s){
31058         var yes = Roo.bootstrap.SplitBar;
31059         switch(s.placement){
31060             case yes.LEFT:
31061                 s.el.setX(s.resizingEl.getRight());
31062                 break;
31063             case yes.RIGHT:
31064                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31065                 break;
31066             case yes.TOP:
31067                 s.el.setY(s.resizingEl.getBottom());
31068                 break;
31069             case yes.BOTTOM:
31070                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31071                 break;
31072         }
31073     }
31074 };
31075
31076 /**
31077  * Orientation constant - Create a vertical SplitBar
31078  * @static
31079  * @type Number
31080  */
31081 Roo.bootstrap.SplitBar.VERTICAL = 1;
31082
31083 /**
31084  * Orientation constant - Create a horizontal SplitBar
31085  * @static
31086  * @type Number
31087  */
31088 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31089
31090 /**
31091  * Placement constant - The resizing element is to the left of the splitter element
31092  * @static
31093  * @type Number
31094  */
31095 Roo.bootstrap.SplitBar.LEFT = 1;
31096
31097 /**
31098  * Placement constant - The resizing element is to the right of the splitter element
31099  * @static
31100  * @type Number
31101  */
31102 Roo.bootstrap.SplitBar.RIGHT = 2;
31103
31104 /**
31105  * Placement constant - The resizing element is positioned above the splitter element
31106  * @static
31107  * @type Number
31108  */
31109 Roo.bootstrap.SplitBar.TOP = 3;
31110
31111 /**
31112  * Placement constant - The resizing element is positioned under splitter element
31113  * @static
31114  * @type Number
31115  */
31116 Roo.bootstrap.SplitBar.BOTTOM = 4;
31117 Roo.namespace("Roo.bootstrap.layout");/*
31118  * Based on:
31119  * Ext JS Library 1.1.1
31120  * Copyright(c) 2006-2007, Ext JS, LLC.
31121  *
31122  * Originally Released Under LGPL - original licence link has changed is not relivant.
31123  *
31124  * Fork - LGPL
31125  * <script type="text/javascript">
31126  */
31127  
31128 /**
31129  * @class Roo.bootstrap.layout.Manager
31130  * @extends Roo.bootstrap.Component
31131  * Base class for layout managers.
31132  */
31133 Roo.bootstrap.layout.Manager = function(config)
31134 {
31135     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31136     
31137     
31138      
31139     
31140     
31141     /** false to disable window resize monitoring @type Boolean */
31142     this.monitorWindowResize = true;
31143     this.regions = {};
31144     this.addEvents({
31145         /**
31146          * @event layout
31147          * Fires when a layout is performed. 
31148          * @param {Roo.LayoutManager} this
31149          */
31150         "layout" : true,
31151         /**
31152          * @event regionresized
31153          * Fires when the user resizes a region. 
31154          * @param {Roo.LayoutRegion} region The resized region
31155          * @param {Number} newSize The new size (width for east/west, height for north/south)
31156          */
31157         "regionresized" : true,
31158         /**
31159          * @event regioncollapsed
31160          * Fires when a region is collapsed. 
31161          * @param {Roo.LayoutRegion} region The collapsed region
31162          */
31163         "regioncollapsed" : true,
31164         /**
31165          * @event regionexpanded
31166          * Fires when a region is expanded.  
31167          * @param {Roo.LayoutRegion} region The expanded region
31168          */
31169         "regionexpanded" : true
31170     });
31171     this.updating = false;
31172     
31173     if (config.el) {
31174         this.el = Roo.get(config.el);
31175         this.initEvents();
31176     }
31177     
31178 };
31179
31180 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31181     
31182     
31183     regions : null,
31184     
31185     monitorWindowResize : true,
31186     
31187     
31188     updating : false,
31189     
31190     
31191     onRender : function(ct, position)
31192     {
31193         if(!this.el){
31194             this.el = Roo.get(ct);
31195             this.initEvents();
31196         }
31197     },
31198     
31199     
31200     initEvents: function()
31201     {
31202         
31203         
31204         // ie scrollbar fix
31205         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31206             document.body.scroll = "no";
31207         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31208             this.el.position('relative');
31209         }
31210         this.id = this.el.id;
31211         this.el.addClass("roo-layout-container");
31212         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31213         if(this.el.dom != document.body ) {
31214             this.el.on('resize', this.layout,this);
31215             this.el.on('show', this.layout,this);
31216         }
31217
31218     },
31219     
31220     /**
31221      * Returns true if this layout is currently being updated
31222      * @return {Boolean}
31223      */
31224     isUpdating : function(){
31225         return this.updating; 
31226     },
31227     
31228     /**
31229      * Suspend the LayoutManager from doing auto-layouts while
31230      * making multiple add or remove calls
31231      */
31232     beginUpdate : function(){
31233         this.updating = true;    
31234     },
31235     
31236     /**
31237      * Restore auto-layouts and optionally disable the manager from performing a layout
31238      * @param {Boolean} noLayout true to disable a layout update 
31239      */
31240     endUpdate : function(noLayout){
31241         this.updating = false;
31242         if(!noLayout){
31243             this.layout();
31244         }    
31245     },
31246     
31247     layout: function(){
31248         // abstract...
31249     },
31250     
31251     onRegionResized : function(region, newSize){
31252         this.fireEvent("regionresized", region, newSize);
31253         this.layout();
31254     },
31255     
31256     onRegionCollapsed : function(region){
31257         this.fireEvent("regioncollapsed", region);
31258     },
31259     
31260     onRegionExpanded : function(region){
31261         this.fireEvent("regionexpanded", region);
31262     },
31263         
31264     /**
31265      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31266      * performs box-model adjustments.
31267      * @return {Object} The size as an object {width: (the width), height: (the height)}
31268      */
31269     getViewSize : function()
31270     {
31271         var size;
31272         if(this.el.dom != document.body){
31273             size = this.el.getSize();
31274         }else{
31275             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31276         }
31277         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31278         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31279         return size;
31280     },
31281     
31282     /**
31283      * Returns the Element this layout is bound to.
31284      * @return {Roo.Element}
31285      */
31286     getEl : function(){
31287         return this.el;
31288     },
31289     
31290     /**
31291      * Returns the specified region.
31292      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31293      * @return {Roo.LayoutRegion}
31294      */
31295     getRegion : function(target){
31296         return this.regions[target.toLowerCase()];
31297     },
31298     
31299     onWindowResize : function(){
31300         if(this.monitorWindowResize){
31301             this.layout();
31302         }
31303     }
31304 });/*
31305  * Based on:
31306  * Ext JS Library 1.1.1
31307  * Copyright(c) 2006-2007, Ext JS, LLC.
31308  *
31309  * Originally Released Under LGPL - original licence link has changed is not relivant.
31310  *
31311  * Fork - LGPL
31312  * <script type="text/javascript">
31313  */
31314 /**
31315  * @class Roo.bootstrap.layout.Border
31316  * @extends Roo.bootstrap.layout.Manager
31317  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31318  * please see: examples/bootstrap/nested.html<br><br>
31319  
31320 <b>The container the layout is rendered into can be either the body element or any other element.
31321 If it is not the body element, the container needs to either be an absolute positioned element,
31322 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31323 the container size if it is not the body element.</b>
31324
31325 * @constructor
31326 * Create a new Border
31327 * @param {Object} config Configuration options
31328  */
31329 Roo.bootstrap.layout.Border = function(config){
31330     config = config || {};
31331     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31332     
31333     
31334     
31335     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31336         if(config[region]){
31337             config[region].region = region;
31338             this.addRegion(config[region]);
31339         }
31340     },this);
31341     
31342 };
31343
31344 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31345
31346 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31347     /**
31348      * Creates and adds a new region if it doesn't already exist.
31349      * @param {String} target The target region key (north, south, east, west or center).
31350      * @param {Object} config The regions config object
31351      * @return {BorderLayoutRegion} The new region
31352      */
31353     addRegion : function(config)
31354     {
31355         if(!this.regions[config.region]){
31356             var r = this.factory(config);
31357             this.bindRegion(r);
31358         }
31359         return this.regions[config.region];
31360     },
31361
31362     // private (kinda)
31363     bindRegion : function(r){
31364         this.regions[r.config.region] = r;
31365         
31366         r.on("visibilitychange",    this.layout, this);
31367         r.on("paneladded",          this.layout, this);
31368         r.on("panelremoved",        this.layout, this);
31369         r.on("invalidated",         this.layout, this);
31370         r.on("resized",             this.onRegionResized, this);
31371         r.on("collapsed",           this.onRegionCollapsed, this);
31372         r.on("expanded",            this.onRegionExpanded, this);
31373     },
31374
31375     /**
31376      * Performs a layout update.
31377      */
31378     layout : function()
31379     {
31380         if(this.updating) {
31381             return;
31382         }
31383         var size = this.getViewSize();
31384         var w = size.width;
31385         var h = size.height;
31386         var centerW = w;
31387         var centerH = h;
31388         var centerY = 0;
31389         var centerX = 0;
31390         //var x = 0, y = 0;
31391
31392         var rs = this.regions;
31393         var north = rs["north"];
31394         var south = rs["south"]; 
31395         var west = rs["west"];
31396         var east = rs["east"];
31397         var center = rs["center"];
31398         //if(this.hideOnLayout){ // not supported anymore
31399             //c.el.setStyle("display", "none");
31400         //}
31401         if(north && north.isVisible()){
31402             var b = north.getBox();
31403             var m = north.getMargins();
31404             b.width = w - (m.left+m.right);
31405             b.x = m.left;
31406             b.y = m.top;
31407             centerY = b.height + b.y + m.bottom;
31408             centerH -= centerY;
31409             north.updateBox(this.safeBox(b));
31410         }
31411         if(south && south.isVisible()){
31412             var b = south.getBox();
31413             var m = south.getMargins();
31414             b.width = w - (m.left+m.right);
31415             b.x = m.left;
31416             var totalHeight = (b.height + m.top + m.bottom);
31417             b.y = h - totalHeight + m.top;
31418             centerH -= totalHeight;
31419             south.updateBox(this.safeBox(b));
31420         }
31421         if(west && west.isVisible()){
31422             var b = west.getBox();
31423             var m = west.getMargins();
31424             b.height = centerH - (m.top+m.bottom);
31425             b.x = m.left;
31426             b.y = centerY + m.top;
31427             var totalWidth = (b.width + m.left + m.right);
31428             centerX += totalWidth;
31429             centerW -= totalWidth;
31430             west.updateBox(this.safeBox(b));
31431         }
31432         if(east && east.isVisible()){
31433             var b = east.getBox();
31434             var m = east.getMargins();
31435             b.height = centerH - (m.top+m.bottom);
31436             var totalWidth = (b.width + m.left + m.right);
31437             b.x = w - totalWidth + m.left;
31438             b.y = centerY + m.top;
31439             centerW -= totalWidth;
31440             east.updateBox(this.safeBox(b));
31441         }
31442         if(center){
31443             var m = center.getMargins();
31444             var centerBox = {
31445                 x: centerX + m.left,
31446                 y: centerY + m.top,
31447                 width: centerW - (m.left+m.right),
31448                 height: centerH - (m.top+m.bottom)
31449             };
31450             //if(this.hideOnLayout){
31451                 //center.el.setStyle("display", "block");
31452             //}
31453             center.updateBox(this.safeBox(centerBox));
31454         }
31455         this.el.repaint();
31456         this.fireEvent("layout", this);
31457     },
31458
31459     // private
31460     safeBox : function(box){
31461         box.width = Math.max(0, box.width);
31462         box.height = Math.max(0, box.height);
31463         return box;
31464     },
31465
31466     /**
31467      * Adds a ContentPanel (or subclass) to this layout.
31468      * @param {String} target The target region key (north, south, east, west or center).
31469      * @param {Roo.ContentPanel} panel The panel to add
31470      * @return {Roo.ContentPanel} The added panel
31471      */
31472     add : function(target, panel){
31473          
31474         target = target.toLowerCase();
31475         return this.regions[target].add(panel);
31476     },
31477
31478     /**
31479      * Remove a ContentPanel (or subclass) to this layout.
31480      * @param {String} target The target region key (north, south, east, west or center).
31481      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31482      * @return {Roo.ContentPanel} The removed panel
31483      */
31484     remove : function(target, panel){
31485         target = target.toLowerCase();
31486         return this.regions[target].remove(panel);
31487     },
31488
31489     /**
31490      * Searches all regions for a panel with the specified id
31491      * @param {String} panelId
31492      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31493      */
31494     findPanel : function(panelId){
31495         var rs = this.regions;
31496         for(var target in rs){
31497             if(typeof rs[target] != "function"){
31498                 var p = rs[target].getPanel(panelId);
31499                 if(p){
31500                     return p;
31501                 }
31502             }
31503         }
31504         return null;
31505     },
31506
31507     /**
31508      * Searches all regions for a panel with the specified id and activates (shows) it.
31509      * @param {String/ContentPanel} panelId The panels id or the panel itself
31510      * @return {Roo.ContentPanel} The shown panel or null
31511      */
31512     showPanel : function(panelId) {
31513       var rs = this.regions;
31514       for(var target in rs){
31515          var r = rs[target];
31516          if(typeof r != "function"){
31517             if(r.hasPanel(panelId)){
31518                return r.showPanel(panelId);
31519             }
31520          }
31521       }
31522       return null;
31523    },
31524
31525    /**
31526      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31527      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31528      */
31529    /*
31530     restoreState : function(provider){
31531         if(!provider){
31532             provider = Roo.state.Manager;
31533         }
31534         var sm = new Roo.LayoutStateManager();
31535         sm.init(this, provider);
31536     },
31537 */
31538  
31539  
31540     /**
31541      * Adds a xtype elements to the layout.
31542      * <pre><code>
31543
31544 layout.addxtype({
31545        xtype : 'ContentPanel',
31546        region: 'west',
31547        items: [ .... ]
31548    }
31549 );
31550
31551 layout.addxtype({
31552         xtype : 'NestedLayoutPanel',
31553         region: 'west',
31554         layout: {
31555            center: { },
31556            west: { }   
31557         },
31558         items : [ ... list of content panels or nested layout panels.. ]
31559    }
31560 );
31561 </code></pre>
31562      * @param {Object} cfg Xtype definition of item to add.
31563      */
31564     addxtype : function(cfg)
31565     {
31566         // basically accepts a pannel...
31567         // can accept a layout region..!?!?
31568         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31569         
31570         
31571         // theory?  children can only be panels??
31572         
31573         //if (!cfg.xtype.match(/Panel$/)) {
31574         //    return false;
31575         //}
31576         var ret = false;
31577         
31578         if (typeof(cfg.region) == 'undefined') {
31579             Roo.log("Failed to add Panel, region was not set");
31580             Roo.log(cfg);
31581             return false;
31582         }
31583         var region = cfg.region;
31584         delete cfg.region;
31585         
31586           
31587         var xitems = [];
31588         if (cfg.items) {
31589             xitems = cfg.items;
31590             delete cfg.items;
31591         }
31592         var nb = false;
31593         
31594         switch(cfg.xtype) 
31595         {
31596             case 'Content':  // ContentPanel (el, cfg)
31597             case 'Scroll':  // ContentPanel (el, cfg)
31598             case 'View': 
31599                 cfg.autoCreate = true;
31600                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31601                 //} else {
31602                 //    var el = this.el.createChild();
31603                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31604                 //}
31605                 
31606                 this.add(region, ret);
31607                 break;
31608             
31609             /*
31610             case 'TreePanel': // our new panel!
31611                 cfg.el = this.el.createChild();
31612                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31613                 this.add(region, ret);
31614                 break;
31615             */
31616             
31617             case 'Nest': 
31618                 // create a new Layout (which is  a Border Layout...
31619                 
31620                 var clayout = cfg.layout;
31621                 clayout.el  = this.el.createChild();
31622                 clayout.items   = clayout.items  || [];
31623                 
31624                 delete cfg.layout;
31625                 
31626                 // replace this exitems with the clayout ones..
31627                 xitems = clayout.items;
31628                  
31629                 // force background off if it's in center...
31630                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31631                     cfg.background = false;
31632                 }
31633                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31634                 
31635                 
31636                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31637                 //console.log('adding nested layout panel '  + cfg.toSource());
31638                 this.add(region, ret);
31639                 nb = {}; /// find first...
31640                 break;
31641             
31642             case 'Grid':
31643                 
31644                 // needs grid and region
31645                 
31646                 //var el = this.getRegion(region).el.createChild();
31647                 /*
31648                  *var el = this.el.createChild();
31649                 // create the grid first...
31650                 cfg.grid.container = el;
31651                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31652                 */
31653                 
31654                 if (region == 'center' && this.active ) {
31655                     cfg.background = false;
31656                 }
31657                 
31658                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31659                 
31660                 this.add(region, ret);
31661                 /*
31662                 if (cfg.background) {
31663                     // render grid on panel activation (if panel background)
31664                     ret.on('activate', function(gp) {
31665                         if (!gp.grid.rendered) {
31666                     //        gp.grid.render(el);
31667                         }
31668                     });
31669                 } else {
31670                   //  cfg.grid.render(el);
31671                 }
31672                 */
31673                 break;
31674            
31675            
31676             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31677                 // it was the old xcomponent building that caused this before.
31678                 // espeically if border is the top element in the tree.
31679                 ret = this;
31680                 break; 
31681                 
31682                     
31683                 
31684                 
31685                 
31686             default:
31687                 /*
31688                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31689                     
31690                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31691                     this.add(region, ret);
31692                 } else {
31693                 */
31694                     Roo.log(cfg);
31695                     throw "Can not add '" + cfg.xtype + "' to Border";
31696                     return null;
31697              
31698                                 
31699              
31700         }
31701         this.beginUpdate();
31702         // add children..
31703         var region = '';
31704         var abn = {};
31705         Roo.each(xitems, function(i)  {
31706             region = nb && i.region ? i.region : false;
31707             
31708             var add = ret.addxtype(i);
31709            
31710             if (region) {
31711                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31712                 if (!i.background) {
31713                     abn[region] = nb[region] ;
31714                 }
31715             }
31716             
31717         });
31718         this.endUpdate();
31719
31720         // make the last non-background panel active..
31721         //if (nb) { Roo.log(abn); }
31722         if (nb) {
31723             
31724             for(var r in abn) {
31725                 region = this.getRegion(r);
31726                 if (region) {
31727                     // tried using nb[r], but it does not work..
31728                      
31729                     region.showPanel(abn[r]);
31730                    
31731                 }
31732             }
31733         }
31734         return ret;
31735         
31736     },
31737     
31738     
31739 // private
31740     factory : function(cfg)
31741     {
31742         
31743         var validRegions = Roo.bootstrap.layout.Border.regions;
31744
31745         var target = cfg.region;
31746         cfg.mgr = this;
31747         
31748         var r = Roo.bootstrap.layout;
31749         Roo.log(target);
31750         switch(target){
31751             case "north":
31752                 return new r.North(cfg);
31753             case "south":
31754                 return new r.South(cfg);
31755             case "east":
31756                 return new r.East(cfg);
31757             case "west":
31758                 return new r.West(cfg);
31759             case "center":
31760                 return new r.Center(cfg);
31761         }
31762         throw 'Layout region "'+target+'" not supported.';
31763     }
31764     
31765     
31766 });
31767  /*
31768  * Based on:
31769  * Ext JS Library 1.1.1
31770  * Copyright(c) 2006-2007, Ext JS, LLC.
31771  *
31772  * Originally Released Under LGPL - original licence link has changed is not relivant.
31773  *
31774  * Fork - LGPL
31775  * <script type="text/javascript">
31776  */
31777  
31778 /**
31779  * @class Roo.bootstrap.layout.Basic
31780  * @extends Roo.util.Observable
31781  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31782  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31783  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31784  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31785  * @cfg {string}   region  the region that it inhabits..
31786  * @cfg {bool}   skipConfig skip config?
31787  * 
31788
31789  */
31790 Roo.bootstrap.layout.Basic = function(config){
31791     
31792     this.mgr = config.mgr;
31793     
31794     this.position = config.region;
31795     
31796     var skipConfig = config.skipConfig;
31797     
31798     this.events = {
31799         /**
31800          * @scope Roo.BasicLayoutRegion
31801          */
31802         
31803         /**
31804          * @event beforeremove
31805          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31806          * @param {Roo.LayoutRegion} this
31807          * @param {Roo.ContentPanel} panel The panel
31808          * @param {Object} e The cancel event object
31809          */
31810         "beforeremove" : true,
31811         /**
31812          * @event invalidated
31813          * Fires when the layout for this region is changed.
31814          * @param {Roo.LayoutRegion} this
31815          */
31816         "invalidated" : true,
31817         /**
31818          * @event visibilitychange
31819          * Fires when this region is shown or hidden 
31820          * @param {Roo.LayoutRegion} this
31821          * @param {Boolean} visibility true or false
31822          */
31823         "visibilitychange" : true,
31824         /**
31825          * @event paneladded
31826          * Fires when a panel is added. 
31827          * @param {Roo.LayoutRegion} this
31828          * @param {Roo.ContentPanel} panel The panel
31829          */
31830         "paneladded" : true,
31831         /**
31832          * @event panelremoved
31833          * Fires when a panel is removed. 
31834          * @param {Roo.LayoutRegion} this
31835          * @param {Roo.ContentPanel} panel The panel
31836          */
31837         "panelremoved" : true,
31838         /**
31839          * @event beforecollapse
31840          * Fires when this region before collapse.
31841          * @param {Roo.LayoutRegion} this
31842          */
31843         "beforecollapse" : true,
31844         /**
31845          * @event collapsed
31846          * Fires when this region is collapsed.
31847          * @param {Roo.LayoutRegion} this
31848          */
31849         "collapsed" : true,
31850         /**
31851          * @event expanded
31852          * Fires when this region is expanded.
31853          * @param {Roo.LayoutRegion} this
31854          */
31855         "expanded" : true,
31856         /**
31857          * @event slideshow
31858          * Fires when this region is slid into view.
31859          * @param {Roo.LayoutRegion} this
31860          */
31861         "slideshow" : true,
31862         /**
31863          * @event slidehide
31864          * Fires when this region slides out of view. 
31865          * @param {Roo.LayoutRegion} this
31866          */
31867         "slidehide" : true,
31868         /**
31869          * @event panelactivated
31870          * Fires when a panel is activated. 
31871          * @param {Roo.LayoutRegion} this
31872          * @param {Roo.ContentPanel} panel The activated panel
31873          */
31874         "panelactivated" : true,
31875         /**
31876          * @event resized
31877          * Fires when the user resizes this region. 
31878          * @param {Roo.LayoutRegion} this
31879          * @param {Number} newSize The new size (width for east/west, height for north/south)
31880          */
31881         "resized" : true
31882     };
31883     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31884     this.panels = new Roo.util.MixedCollection();
31885     this.panels.getKey = this.getPanelId.createDelegate(this);
31886     this.box = null;
31887     this.activePanel = null;
31888     // ensure listeners are added...
31889     
31890     if (config.listeners || config.events) {
31891         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31892             listeners : config.listeners || {},
31893             events : config.events || {}
31894         });
31895     }
31896     
31897     if(skipConfig !== true){
31898         this.applyConfig(config);
31899     }
31900 };
31901
31902 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31903 {
31904     getPanelId : function(p){
31905         return p.getId();
31906     },
31907     
31908     applyConfig : function(config){
31909         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31910         this.config = config;
31911         
31912     },
31913     
31914     /**
31915      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31916      * the width, for horizontal (north, south) the height.
31917      * @param {Number} newSize The new width or height
31918      */
31919     resizeTo : function(newSize){
31920         var el = this.el ? this.el :
31921                  (this.activePanel ? this.activePanel.getEl() : null);
31922         if(el){
31923             switch(this.position){
31924                 case "east":
31925                 case "west":
31926                     el.setWidth(newSize);
31927                     this.fireEvent("resized", this, newSize);
31928                 break;
31929                 case "north":
31930                 case "south":
31931                     el.setHeight(newSize);
31932                     this.fireEvent("resized", this, newSize);
31933                 break;                
31934             }
31935         }
31936     },
31937     
31938     getBox : function(){
31939         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31940     },
31941     
31942     getMargins : function(){
31943         return this.margins;
31944     },
31945     
31946     updateBox : function(box){
31947         this.box = box;
31948         var el = this.activePanel.getEl();
31949         el.dom.style.left = box.x + "px";
31950         el.dom.style.top = box.y + "px";
31951         this.activePanel.setSize(box.width, box.height);
31952     },
31953     
31954     /**
31955      * Returns the container element for this region.
31956      * @return {Roo.Element}
31957      */
31958     getEl : function(){
31959         return this.activePanel;
31960     },
31961     
31962     /**
31963      * Returns true if this region is currently visible.
31964      * @return {Boolean}
31965      */
31966     isVisible : function(){
31967         return this.activePanel ? true : false;
31968     },
31969     
31970     setActivePanel : function(panel){
31971         panel = this.getPanel(panel);
31972         if(this.activePanel && this.activePanel != panel){
31973             this.activePanel.setActiveState(false);
31974             this.activePanel.getEl().setLeftTop(-10000,-10000);
31975         }
31976         this.activePanel = panel;
31977         panel.setActiveState(true);
31978         if(this.box){
31979             panel.setSize(this.box.width, this.box.height);
31980         }
31981         this.fireEvent("panelactivated", this, panel);
31982         this.fireEvent("invalidated");
31983     },
31984     
31985     /**
31986      * Show the specified panel.
31987      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31988      * @return {Roo.ContentPanel} The shown panel or null
31989      */
31990     showPanel : function(panel){
31991         panel = this.getPanel(panel);
31992         if(panel){
31993             this.setActivePanel(panel);
31994         }
31995         return panel;
31996     },
31997     
31998     /**
31999      * Get the active panel for this region.
32000      * @return {Roo.ContentPanel} The active panel or null
32001      */
32002     getActivePanel : function(){
32003         return this.activePanel;
32004     },
32005     
32006     /**
32007      * Add the passed ContentPanel(s)
32008      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32009      * @return {Roo.ContentPanel} The panel added (if only one was added)
32010      */
32011     add : function(panel){
32012         if(arguments.length > 1){
32013             for(var i = 0, len = arguments.length; i < len; i++) {
32014                 this.add(arguments[i]);
32015             }
32016             return null;
32017         }
32018         if(this.hasPanel(panel)){
32019             this.showPanel(panel);
32020             return panel;
32021         }
32022         var el = panel.getEl();
32023         if(el.dom.parentNode != this.mgr.el.dom){
32024             this.mgr.el.dom.appendChild(el.dom);
32025         }
32026         if(panel.setRegion){
32027             panel.setRegion(this);
32028         }
32029         this.panels.add(panel);
32030         el.setStyle("position", "absolute");
32031         if(!panel.background){
32032             this.setActivePanel(panel);
32033             if(this.config.initialSize && this.panels.getCount()==1){
32034                 this.resizeTo(this.config.initialSize);
32035             }
32036         }
32037         this.fireEvent("paneladded", this, panel);
32038         return panel;
32039     },
32040     
32041     /**
32042      * Returns true if the panel is in this region.
32043      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32044      * @return {Boolean}
32045      */
32046     hasPanel : function(panel){
32047         if(typeof panel == "object"){ // must be panel obj
32048             panel = panel.getId();
32049         }
32050         return this.getPanel(panel) ? true : false;
32051     },
32052     
32053     /**
32054      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32055      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32056      * @param {Boolean} preservePanel Overrides the config preservePanel option
32057      * @return {Roo.ContentPanel} The panel that was removed
32058      */
32059     remove : function(panel, preservePanel){
32060         panel = this.getPanel(panel);
32061         if(!panel){
32062             return null;
32063         }
32064         var e = {};
32065         this.fireEvent("beforeremove", this, panel, e);
32066         if(e.cancel === true){
32067             return null;
32068         }
32069         var panelId = panel.getId();
32070         this.panels.removeKey(panelId);
32071         return panel;
32072     },
32073     
32074     /**
32075      * Returns the panel specified or null if it's not in this region.
32076      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32077      * @return {Roo.ContentPanel}
32078      */
32079     getPanel : function(id){
32080         if(typeof id == "object"){ // must be panel obj
32081             return id;
32082         }
32083         return this.panels.get(id);
32084     },
32085     
32086     /**
32087      * Returns this regions position (north/south/east/west/center).
32088      * @return {String} 
32089      */
32090     getPosition: function(){
32091         return this.position;    
32092     }
32093 });/*
32094  * Based on:
32095  * Ext JS Library 1.1.1
32096  * Copyright(c) 2006-2007, Ext JS, LLC.
32097  *
32098  * Originally Released Under LGPL - original licence link has changed is not relivant.
32099  *
32100  * Fork - LGPL
32101  * <script type="text/javascript">
32102  */
32103  
32104 /**
32105  * @class Roo.bootstrap.layout.Region
32106  * @extends Roo.bootstrap.layout.Basic
32107  * This class represents a region in a layout manager.
32108  
32109  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32110  * @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})
32111  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32112  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32113  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32114  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32115  * @cfg {String}    title           The title for the region (overrides panel titles)
32116  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32117  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32118  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32119  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32120  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32121  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32122  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32123  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32124  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32125  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32126
32127  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32128  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32129  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32130  * @cfg {Number}    width           For East/West panels
32131  * @cfg {Number}    height          For North/South panels
32132  * @cfg {Boolean}   split           To show the splitter
32133  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32134  * 
32135  * @cfg {string}   cls             Extra CSS classes to add to region
32136  * 
32137  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32138  * @cfg {string}   region  the region that it inhabits..
32139  *
32140
32141  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32142  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32143
32144  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32145  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32146  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32147  */
32148 Roo.bootstrap.layout.Region = function(config)
32149 {
32150     this.applyConfig(config);
32151
32152     var mgr = config.mgr;
32153     var pos = config.region;
32154     config.skipConfig = true;
32155     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32156     
32157     if (mgr.el) {
32158         this.onRender(mgr.el);   
32159     }
32160      
32161     this.visible = true;
32162     this.collapsed = false;
32163 };
32164
32165 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32166
32167     position: '', // set by wrapper (eg. north/south etc..)
32168
32169     createBody : function(){
32170         /** This region's body element 
32171         * @type Roo.Element */
32172         this.bodyEl = this.el.createChild({
32173                 tag: "div",
32174                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32175         });
32176     },
32177
32178     onRender: function(ctr, pos)
32179     {
32180         var dh = Roo.DomHelper;
32181         /** This region's container element 
32182         * @type Roo.Element */
32183         this.el = dh.append(ctr.dom, {
32184                 tag: "div",
32185                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32186             }, true);
32187         /** This region's title element 
32188         * @type Roo.Element */
32189     
32190         this.titleEl = dh.append(this.el.dom,
32191             {
32192                     tag: "div",
32193                     unselectable: "on",
32194                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32195                     children:[
32196                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32197                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32198                     ]}, true);
32199         
32200         this.titleEl.enableDisplayMode();
32201         /** This region's title text element 
32202         * @type HTMLElement */
32203         this.titleTextEl = this.titleEl.dom.firstChild;
32204         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32205         /*
32206         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32207         this.closeBtn.enableDisplayMode();
32208         this.closeBtn.on("click", this.closeClicked, this);
32209         this.closeBtn.hide();
32210     */
32211         this.createBody(this.config);
32212         if(this.config.hideWhenEmpty){
32213             this.hide();
32214             this.on("paneladded", this.validateVisibility, this);
32215             this.on("panelremoved", this.validateVisibility, this);
32216         }
32217         if(this.autoScroll){
32218             this.bodyEl.setStyle("overflow", "auto");
32219         }else{
32220             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32221         }
32222         //if(c.titlebar !== false){
32223             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32224                 this.titleEl.hide();
32225             }else{
32226                 this.titleEl.show();
32227                 if(this.config.title){
32228                     this.titleTextEl.innerHTML = this.config.title;
32229                 }
32230             }
32231         //}
32232         if(this.config.collapsed){
32233             this.collapse(true);
32234         }
32235         if(this.config.hidden){
32236             this.hide();
32237         }
32238     },
32239     
32240     applyConfig : function(c)
32241     {
32242         /*
32243          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32244             var dh = Roo.DomHelper;
32245             if(c.titlebar !== false){
32246                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32247                 this.collapseBtn.on("click", this.collapse, this);
32248                 this.collapseBtn.enableDisplayMode();
32249                 /*
32250                 if(c.showPin === true || this.showPin){
32251                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32252                     this.stickBtn.enableDisplayMode();
32253                     this.stickBtn.on("click", this.expand, this);
32254                     this.stickBtn.hide();
32255                 }
32256                 
32257             }
32258             */
32259             /** This region's collapsed element
32260             * @type Roo.Element */
32261             /*
32262              *
32263             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32264                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32265             ]}, true);
32266             
32267             if(c.floatable !== false){
32268                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32269                this.collapsedEl.on("click", this.collapseClick, this);
32270             }
32271
32272             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32273                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32274                    id: "message", unselectable: "on", style:{"float":"left"}});
32275                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32276              }
32277             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32278             this.expandBtn.on("click", this.expand, this);
32279             
32280         }
32281         
32282         if(this.collapseBtn){
32283             this.collapseBtn.setVisible(c.collapsible == true);
32284         }
32285         
32286         this.cmargins = c.cmargins || this.cmargins ||
32287                          (this.position == "west" || this.position == "east" ?
32288                              {top: 0, left: 2, right:2, bottom: 0} :
32289                              {top: 2, left: 0, right:0, bottom: 2});
32290         */
32291         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32292         
32293         
32294         this.bottomTabs = c.tabPosition != "top";
32295         
32296         this.autoScroll = c.autoScroll || false;
32297         
32298         
32299        
32300         
32301         this.duration = c.duration || .30;
32302         this.slideDuration = c.slideDuration || .45;
32303         this.config = c;
32304        
32305     },
32306     /**
32307      * Returns true if this region is currently visible.
32308      * @return {Boolean}
32309      */
32310     isVisible : function(){
32311         return this.visible;
32312     },
32313
32314     /**
32315      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32316      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32317      */
32318     //setCollapsedTitle : function(title){
32319     //    title = title || "&#160;";
32320      //   if(this.collapsedTitleTextEl){
32321       //      this.collapsedTitleTextEl.innerHTML = title;
32322        // }
32323     //},
32324
32325     getBox : function(){
32326         var b;
32327       //  if(!this.collapsed){
32328             b = this.el.getBox(false, true);
32329        // }else{
32330           //  b = this.collapsedEl.getBox(false, true);
32331         //}
32332         return b;
32333     },
32334
32335     getMargins : function(){
32336         return this.margins;
32337         //return this.collapsed ? this.cmargins : this.margins;
32338     },
32339 /*
32340     highlight : function(){
32341         this.el.addClass("x-layout-panel-dragover");
32342     },
32343
32344     unhighlight : function(){
32345         this.el.removeClass("x-layout-panel-dragover");
32346     },
32347 */
32348     updateBox : function(box)
32349     {
32350         this.box = box;
32351         if(!this.collapsed){
32352             this.el.dom.style.left = box.x + "px";
32353             this.el.dom.style.top = box.y + "px";
32354             this.updateBody(box.width, box.height);
32355         }else{
32356             this.collapsedEl.dom.style.left = box.x + "px";
32357             this.collapsedEl.dom.style.top = box.y + "px";
32358             this.collapsedEl.setSize(box.width, box.height);
32359         }
32360         if(this.tabs){
32361             this.tabs.autoSizeTabs();
32362         }
32363     },
32364
32365     updateBody : function(w, h)
32366     {
32367         if(w !== null){
32368             this.el.setWidth(w);
32369             w -= this.el.getBorderWidth("rl");
32370             if(this.config.adjustments){
32371                 w += this.config.adjustments[0];
32372             }
32373         }
32374         if(h !== null){
32375             this.el.setHeight(h);
32376             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32377             h -= this.el.getBorderWidth("tb");
32378             if(this.config.adjustments){
32379                 h += this.config.adjustments[1];
32380             }
32381             this.bodyEl.setHeight(h);
32382             if(this.tabs){
32383                 h = this.tabs.syncHeight(h);
32384             }
32385         }
32386         if(this.panelSize){
32387             w = w !== null ? w : this.panelSize.width;
32388             h = h !== null ? h : this.panelSize.height;
32389         }
32390         if(this.activePanel){
32391             var el = this.activePanel.getEl();
32392             w = w !== null ? w : el.getWidth();
32393             h = h !== null ? h : el.getHeight();
32394             this.panelSize = {width: w, height: h};
32395             this.activePanel.setSize(w, h);
32396         }
32397         if(Roo.isIE && this.tabs){
32398             this.tabs.el.repaint();
32399         }
32400     },
32401
32402     /**
32403      * Returns the container element for this region.
32404      * @return {Roo.Element}
32405      */
32406     getEl : function(){
32407         return this.el;
32408     },
32409
32410     /**
32411      * Hides this region.
32412      */
32413     hide : function(){
32414         //if(!this.collapsed){
32415             this.el.dom.style.left = "-2000px";
32416             this.el.hide();
32417         //}else{
32418          //   this.collapsedEl.dom.style.left = "-2000px";
32419          //   this.collapsedEl.hide();
32420        // }
32421         this.visible = false;
32422         this.fireEvent("visibilitychange", this, false);
32423     },
32424
32425     /**
32426      * Shows this region if it was previously hidden.
32427      */
32428     show : function(){
32429         //if(!this.collapsed){
32430             this.el.show();
32431         //}else{
32432         //    this.collapsedEl.show();
32433        // }
32434         this.visible = true;
32435         this.fireEvent("visibilitychange", this, true);
32436     },
32437 /*
32438     closeClicked : function(){
32439         if(this.activePanel){
32440             this.remove(this.activePanel);
32441         }
32442     },
32443
32444     collapseClick : function(e){
32445         if(this.isSlid){
32446            e.stopPropagation();
32447            this.slideIn();
32448         }else{
32449            e.stopPropagation();
32450            this.slideOut();
32451         }
32452     },
32453 */
32454     /**
32455      * Collapses this region.
32456      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32457      */
32458     /*
32459     collapse : function(skipAnim, skipCheck = false){
32460         if(this.collapsed) {
32461             return;
32462         }
32463         
32464         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32465             
32466             this.collapsed = true;
32467             if(this.split){
32468                 this.split.el.hide();
32469             }
32470             if(this.config.animate && skipAnim !== true){
32471                 this.fireEvent("invalidated", this);
32472                 this.animateCollapse();
32473             }else{
32474                 this.el.setLocation(-20000,-20000);
32475                 this.el.hide();
32476                 this.collapsedEl.show();
32477                 this.fireEvent("collapsed", this);
32478                 this.fireEvent("invalidated", this);
32479             }
32480         }
32481         
32482     },
32483 */
32484     animateCollapse : function(){
32485         // overridden
32486     },
32487
32488     /**
32489      * Expands this region if it was previously collapsed.
32490      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32491      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32492      */
32493     /*
32494     expand : function(e, skipAnim){
32495         if(e) {
32496             e.stopPropagation();
32497         }
32498         if(!this.collapsed || this.el.hasActiveFx()) {
32499             return;
32500         }
32501         if(this.isSlid){
32502             this.afterSlideIn();
32503             skipAnim = true;
32504         }
32505         this.collapsed = false;
32506         if(this.config.animate && skipAnim !== true){
32507             this.animateExpand();
32508         }else{
32509             this.el.show();
32510             if(this.split){
32511                 this.split.el.show();
32512             }
32513             this.collapsedEl.setLocation(-2000,-2000);
32514             this.collapsedEl.hide();
32515             this.fireEvent("invalidated", this);
32516             this.fireEvent("expanded", this);
32517         }
32518     },
32519 */
32520     animateExpand : function(){
32521         // overridden
32522     },
32523
32524     initTabs : function()
32525     {
32526         this.bodyEl.setStyle("overflow", "hidden");
32527         var ts = new Roo.bootstrap.panel.Tabs({
32528                 el: this.bodyEl.dom,
32529                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32530                 disableTooltips: this.config.disableTabTips,
32531                 toolbar : this.config.toolbar
32532             });
32533         
32534         if(this.config.hideTabs){
32535             ts.stripWrap.setDisplayed(false);
32536         }
32537         this.tabs = ts;
32538         ts.resizeTabs = this.config.resizeTabs === true;
32539         ts.minTabWidth = this.config.minTabWidth || 40;
32540         ts.maxTabWidth = this.config.maxTabWidth || 250;
32541         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32542         ts.monitorResize = false;
32543         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32544         ts.bodyEl.addClass('roo-layout-tabs-body');
32545         this.panels.each(this.initPanelAsTab, this);
32546     },
32547
32548     initPanelAsTab : function(panel){
32549         var ti = this.tabs.addTab(
32550                     panel.getEl().id,
32551                     panel.getTitle(), null,
32552                     this.config.closeOnTab && panel.isClosable()
32553             );
32554         if(panel.tabTip !== undefined){
32555             ti.setTooltip(panel.tabTip);
32556         }
32557         ti.on("activate", function(){
32558               this.setActivePanel(panel);
32559         }, this);
32560         
32561         if(this.config.closeOnTab){
32562             ti.on("beforeclose", function(t, e){
32563                 e.cancel = true;
32564                 this.remove(panel);
32565             }, this);
32566         }
32567         return ti;
32568     },
32569
32570     updatePanelTitle : function(panel, title)
32571     {
32572         if(this.activePanel == panel){
32573             this.updateTitle(title);
32574         }
32575         if(this.tabs){
32576             var ti = this.tabs.getTab(panel.getEl().id);
32577             ti.setText(title);
32578             if(panel.tabTip !== undefined){
32579                 ti.setTooltip(panel.tabTip);
32580             }
32581         }
32582     },
32583
32584     updateTitle : function(title){
32585         if(this.titleTextEl && !this.config.title){
32586             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32587         }
32588     },
32589
32590     setActivePanel : function(panel)
32591     {
32592         panel = this.getPanel(panel);
32593         if(this.activePanel && this.activePanel != panel){
32594             this.activePanel.setActiveState(false);
32595         }
32596         this.activePanel = panel;
32597         panel.setActiveState(true);
32598         if(this.panelSize){
32599             panel.setSize(this.panelSize.width, this.panelSize.height);
32600         }
32601         if(this.closeBtn){
32602             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32603         }
32604         this.updateTitle(panel.getTitle());
32605         if(this.tabs){
32606             this.fireEvent("invalidated", this);
32607         }
32608         this.fireEvent("panelactivated", this, panel);
32609     },
32610
32611     /**
32612      * Shows the specified panel.
32613      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32614      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32615      */
32616     showPanel : function(panel)
32617     {
32618         panel = this.getPanel(panel);
32619         if(panel){
32620             if(this.tabs){
32621                 var tab = this.tabs.getTab(panel.getEl().id);
32622                 if(tab.isHidden()){
32623                     this.tabs.unhideTab(tab.id);
32624                 }
32625                 tab.activate();
32626             }else{
32627                 this.setActivePanel(panel);
32628             }
32629         }
32630         return panel;
32631     },
32632
32633     /**
32634      * Get the active panel for this region.
32635      * @return {Roo.ContentPanel} The active panel or null
32636      */
32637     getActivePanel : function(){
32638         return this.activePanel;
32639     },
32640
32641     validateVisibility : function(){
32642         if(this.panels.getCount() < 1){
32643             this.updateTitle("&#160;");
32644             this.closeBtn.hide();
32645             this.hide();
32646         }else{
32647             if(!this.isVisible()){
32648                 this.show();
32649             }
32650         }
32651     },
32652
32653     /**
32654      * Adds the passed ContentPanel(s) to this region.
32655      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32656      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32657      */
32658     add : function(panel){
32659         if(arguments.length > 1){
32660             for(var i = 0, len = arguments.length; i < len; i++) {
32661                 this.add(arguments[i]);
32662             }
32663             return null;
32664         }
32665         if(this.hasPanel(panel)){
32666             this.showPanel(panel);
32667             return panel;
32668         }
32669         panel.setRegion(this);
32670         this.panels.add(panel);
32671         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32672             this.bodyEl.dom.appendChild(panel.getEl().dom);
32673             if(panel.background !== true){
32674                 this.setActivePanel(panel);
32675             }
32676             this.fireEvent("paneladded", this, panel);
32677             return panel;
32678         }
32679         if(!this.tabs){
32680             this.initTabs();
32681         }else{
32682             this.initPanelAsTab(panel);
32683         }
32684         
32685         
32686         if(panel.background !== true){
32687             this.tabs.activate(panel.getEl().id);
32688         }
32689         this.fireEvent("paneladded", this, panel);
32690         return panel;
32691     },
32692
32693     /**
32694      * Hides the tab for the specified panel.
32695      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32696      */
32697     hidePanel : function(panel){
32698         if(this.tabs && (panel = this.getPanel(panel))){
32699             this.tabs.hideTab(panel.getEl().id);
32700         }
32701     },
32702
32703     /**
32704      * Unhides the tab for a previously hidden panel.
32705      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32706      */
32707     unhidePanel : function(panel){
32708         if(this.tabs && (panel = this.getPanel(panel))){
32709             this.tabs.unhideTab(panel.getEl().id);
32710         }
32711     },
32712
32713     clearPanels : function(){
32714         while(this.panels.getCount() > 0){
32715              this.remove(this.panels.first());
32716         }
32717     },
32718
32719     /**
32720      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32721      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32722      * @param {Boolean} preservePanel Overrides the config preservePanel option
32723      * @return {Roo.ContentPanel} The panel that was removed
32724      */
32725     remove : function(panel, preservePanel)
32726     {
32727         panel = this.getPanel(panel);
32728         if(!panel){
32729             return null;
32730         }
32731         var e = {};
32732         this.fireEvent("beforeremove", this, panel, e);
32733         if(e.cancel === true){
32734             return null;
32735         }
32736         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32737         var panelId = panel.getId();
32738         this.panels.removeKey(panelId);
32739         if(preservePanel){
32740             document.body.appendChild(panel.getEl().dom);
32741         }
32742         if(this.tabs){
32743             this.tabs.removeTab(panel.getEl().id);
32744         }else if (!preservePanel){
32745             this.bodyEl.dom.removeChild(panel.getEl().dom);
32746         }
32747         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32748             var p = this.panels.first();
32749             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32750             tempEl.appendChild(p.getEl().dom);
32751             this.bodyEl.update("");
32752             this.bodyEl.dom.appendChild(p.getEl().dom);
32753             tempEl = null;
32754             this.updateTitle(p.getTitle());
32755             this.tabs = null;
32756             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32757             this.setActivePanel(p);
32758         }
32759         panel.setRegion(null);
32760         if(this.activePanel == panel){
32761             this.activePanel = null;
32762         }
32763         if(this.config.autoDestroy !== false && preservePanel !== true){
32764             try{panel.destroy();}catch(e){}
32765         }
32766         this.fireEvent("panelremoved", this, panel);
32767         return panel;
32768     },
32769
32770     /**
32771      * Returns the TabPanel component used by this region
32772      * @return {Roo.TabPanel}
32773      */
32774     getTabs : function(){
32775         return this.tabs;
32776     },
32777
32778     createTool : function(parentEl, className){
32779         var btn = Roo.DomHelper.append(parentEl, {
32780             tag: "div",
32781             cls: "x-layout-tools-button",
32782             children: [ {
32783                 tag: "div",
32784                 cls: "roo-layout-tools-button-inner " + className,
32785                 html: "&#160;"
32786             }]
32787         }, true);
32788         btn.addClassOnOver("roo-layout-tools-button-over");
32789         return btn;
32790     }
32791 });/*
32792  * Based on:
32793  * Ext JS Library 1.1.1
32794  * Copyright(c) 2006-2007, Ext JS, LLC.
32795  *
32796  * Originally Released Under LGPL - original licence link has changed is not relivant.
32797  *
32798  * Fork - LGPL
32799  * <script type="text/javascript">
32800  */
32801  
32802
32803
32804 /**
32805  * @class Roo.SplitLayoutRegion
32806  * @extends Roo.LayoutRegion
32807  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32808  */
32809 Roo.bootstrap.layout.Split = function(config){
32810     this.cursor = config.cursor;
32811     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32812 };
32813
32814 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32815 {
32816     splitTip : "Drag to resize.",
32817     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32818     useSplitTips : false,
32819
32820     applyConfig : function(config){
32821         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32822     },
32823     
32824     onRender : function(ctr,pos) {
32825         
32826         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
32827         if(!this.config.split){
32828             return;
32829         }
32830         if(!this.split){
32831             
32832             var splitEl = Roo.DomHelper.append(ctr.dom,  {
32833                             tag: "div",
32834                             id: this.el.id + "-split",
32835                             cls: "roo-layout-split roo-layout-split-"+this.position,
32836                             html: "&#160;"
32837             });
32838             /** The SplitBar for this region 
32839             * @type Roo.SplitBar */
32840             // does not exist yet...
32841             Roo.log([this.position, this.orientation]);
32842             
32843             this.split = new Roo.bootstrap.SplitBar({
32844                 dragElement : splitEl,
32845                 resizingElement: this.el,
32846                 orientation : this.orientation
32847             });
32848             
32849             this.split.on("moved", this.onSplitMove, this);
32850             this.split.useShim = this.config.useShim === true;
32851             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32852             if(this.useSplitTips){
32853                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32854             }
32855             //if(config.collapsible){
32856             //    this.split.el.on("dblclick", this.collapse,  this);
32857             //}
32858         }
32859         if(typeof this.config.minSize != "undefined"){
32860             this.split.minSize = this.config.minSize;
32861         }
32862         if(typeof this.config.maxSize != "undefined"){
32863             this.split.maxSize = this.config.maxSize;
32864         }
32865         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
32866             this.hideSplitter();
32867         }
32868         
32869     },
32870
32871     getHMaxSize : function(){
32872          var cmax = this.config.maxSize || 10000;
32873          var center = this.mgr.getRegion("center");
32874          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32875     },
32876
32877     getVMaxSize : function(){
32878          var cmax = this.config.maxSize || 10000;
32879          var center = this.mgr.getRegion("center");
32880          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32881     },
32882
32883     onSplitMove : function(split, newSize){
32884         this.fireEvent("resized", this, newSize);
32885     },
32886     
32887     /** 
32888      * Returns the {@link Roo.SplitBar} for this region.
32889      * @return {Roo.SplitBar}
32890      */
32891     getSplitBar : function(){
32892         return this.split;
32893     },
32894     
32895     hide : function(){
32896         this.hideSplitter();
32897         Roo.bootstrap.layout.Split.superclass.hide.call(this);
32898     },
32899
32900     hideSplitter : function(){
32901         if(this.split){
32902             this.split.el.setLocation(-2000,-2000);
32903             this.split.el.hide();
32904         }
32905     },
32906
32907     show : function(){
32908         if(this.split){
32909             this.split.el.show();
32910         }
32911         Roo.bootstrap.layout.Split.superclass.show.call(this);
32912     },
32913     
32914     beforeSlide: function(){
32915         if(Roo.isGecko){// firefox overflow auto bug workaround
32916             this.bodyEl.clip();
32917             if(this.tabs) {
32918                 this.tabs.bodyEl.clip();
32919             }
32920             if(this.activePanel){
32921                 this.activePanel.getEl().clip();
32922                 
32923                 if(this.activePanel.beforeSlide){
32924                     this.activePanel.beforeSlide();
32925                 }
32926             }
32927         }
32928     },
32929     
32930     afterSlide : function(){
32931         if(Roo.isGecko){// firefox overflow auto bug workaround
32932             this.bodyEl.unclip();
32933             if(this.tabs) {
32934                 this.tabs.bodyEl.unclip();
32935             }
32936             if(this.activePanel){
32937                 this.activePanel.getEl().unclip();
32938                 if(this.activePanel.afterSlide){
32939                     this.activePanel.afterSlide();
32940                 }
32941             }
32942         }
32943     },
32944
32945     initAutoHide : function(){
32946         if(this.autoHide !== false){
32947             if(!this.autoHideHd){
32948                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32949                 this.autoHideHd = {
32950                     "mouseout": function(e){
32951                         if(!e.within(this.el, true)){
32952                             st.delay(500);
32953                         }
32954                     },
32955                     "mouseover" : function(e){
32956                         st.cancel();
32957                     },
32958                     scope : this
32959                 };
32960             }
32961             this.el.on(this.autoHideHd);
32962         }
32963     },
32964
32965     clearAutoHide : function(){
32966         if(this.autoHide !== false){
32967             this.el.un("mouseout", this.autoHideHd.mouseout);
32968             this.el.un("mouseover", this.autoHideHd.mouseover);
32969         }
32970     },
32971
32972     clearMonitor : function(){
32973         Roo.get(document).un("click", this.slideInIf, this);
32974     },
32975
32976     // these names are backwards but not changed for compat
32977     slideOut : function(){
32978         if(this.isSlid || this.el.hasActiveFx()){
32979             return;
32980         }
32981         this.isSlid = true;
32982         if(this.collapseBtn){
32983             this.collapseBtn.hide();
32984         }
32985         this.closeBtnState = this.closeBtn.getStyle('display');
32986         this.closeBtn.hide();
32987         if(this.stickBtn){
32988             this.stickBtn.show();
32989         }
32990         this.el.show();
32991         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32992         this.beforeSlide();
32993         this.el.setStyle("z-index", 10001);
32994         this.el.slideIn(this.getSlideAnchor(), {
32995             callback: function(){
32996                 this.afterSlide();
32997                 this.initAutoHide();
32998                 Roo.get(document).on("click", this.slideInIf, this);
32999                 this.fireEvent("slideshow", this);
33000             },
33001             scope: this,
33002             block: true
33003         });
33004     },
33005
33006     afterSlideIn : function(){
33007         this.clearAutoHide();
33008         this.isSlid = false;
33009         this.clearMonitor();
33010         this.el.setStyle("z-index", "");
33011         if(this.collapseBtn){
33012             this.collapseBtn.show();
33013         }
33014         this.closeBtn.setStyle('display', this.closeBtnState);
33015         if(this.stickBtn){
33016             this.stickBtn.hide();
33017         }
33018         this.fireEvent("slidehide", this);
33019     },
33020
33021     slideIn : function(cb){
33022         if(!this.isSlid || this.el.hasActiveFx()){
33023             Roo.callback(cb);
33024             return;
33025         }
33026         this.isSlid = false;
33027         this.beforeSlide();
33028         this.el.slideOut(this.getSlideAnchor(), {
33029             callback: function(){
33030                 this.el.setLeftTop(-10000, -10000);
33031                 this.afterSlide();
33032                 this.afterSlideIn();
33033                 Roo.callback(cb);
33034             },
33035             scope: this,
33036             block: true
33037         });
33038     },
33039     
33040     slideInIf : function(e){
33041         if(!e.within(this.el)){
33042             this.slideIn();
33043         }
33044     },
33045
33046     animateCollapse : function(){
33047         this.beforeSlide();
33048         this.el.setStyle("z-index", 20000);
33049         var anchor = this.getSlideAnchor();
33050         this.el.slideOut(anchor, {
33051             callback : function(){
33052                 this.el.setStyle("z-index", "");
33053                 this.collapsedEl.slideIn(anchor, {duration:.3});
33054                 this.afterSlide();
33055                 this.el.setLocation(-10000,-10000);
33056                 this.el.hide();
33057                 this.fireEvent("collapsed", this);
33058             },
33059             scope: this,
33060             block: true
33061         });
33062     },
33063
33064     animateExpand : function(){
33065         this.beforeSlide();
33066         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33067         this.el.setStyle("z-index", 20000);
33068         this.collapsedEl.hide({
33069             duration:.1
33070         });
33071         this.el.slideIn(this.getSlideAnchor(), {
33072             callback : function(){
33073                 this.el.setStyle("z-index", "");
33074                 this.afterSlide();
33075                 if(this.split){
33076                     this.split.el.show();
33077                 }
33078                 this.fireEvent("invalidated", this);
33079                 this.fireEvent("expanded", this);
33080             },
33081             scope: this,
33082             block: true
33083         });
33084     },
33085
33086     anchors : {
33087         "west" : "left",
33088         "east" : "right",
33089         "north" : "top",
33090         "south" : "bottom"
33091     },
33092
33093     sanchors : {
33094         "west" : "l",
33095         "east" : "r",
33096         "north" : "t",
33097         "south" : "b"
33098     },
33099
33100     canchors : {
33101         "west" : "tl-tr",
33102         "east" : "tr-tl",
33103         "north" : "tl-bl",
33104         "south" : "bl-tl"
33105     },
33106
33107     getAnchor : function(){
33108         return this.anchors[this.position];
33109     },
33110
33111     getCollapseAnchor : function(){
33112         return this.canchors[this.position];
33113     },
33114
33115     getSlideAnchor : function(){
33116         return this.sanchors[this.position];
33117     },
33118
33119     getAlignAdj : function(){
33120         var cm = this.cmargins;
33121         switch(this.position){
33122             case "west":
33123                 return [0, 0];
33124             break;
33125             case "east":
33126                 return [0, 0];
33127             break;
33128             case "north":
33129                 return [0, 0];
33130             break;
33131             case "south":
33132                 return [0, 0];
33133             break;
33134         }
33135     },
33136
33137     getExpandAdj : function(){
33138         var c = this.collapsedEl, cm = this.cmargins;
33139         switch(this.position){
33140             case "west":
33141                 return [-(cm.right+c.getWidth()+cm.left), 0];
33142             break;
33143             case "east":
33144                 return [cm.right+c.getWidth()+cm.left, 0];
33145             break;
33146             case "north":
33147                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33148             break;
33149             case "south":
33150                 return [0, cm.top+cm.bottom+c.getHeight()];
33151             break;
33152         }
33153     }
33154 });/*
33155  * Based on:
33156  * Ext JS Library 1.1.1
33157  * Copyright(c) 2006-2007, Ext JS, LLC.
33158  *
33159  * Originally Released Under LGPL - original licence link has changed is not relivant.
33160  *
33161  * Fork - LGPL
33162  * <script type="text/javascript">
33163  */
33164 /*
33165  * These classes are private internal classes
33166  */
33167 Roo.bootstrap.layout.Center = function(config){
33168     config.region = "center";
33169     Roo.bootstrap.layout.Region.call(this, config);
33170     this.visible = true;
33171     this.minWidth = config.minWidth || 20;
33172     this.minHeight = config.minHeight || 20;
33173 };
33174
33175 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33176     hide : function(){
33177         // center panel can't be hidden
33178     },
33179     
33180     show : function(){
33181         // center panel can't be hidden
33182     },
33183     
33184     getMinWidth: function(){
33185         return this.minWidth;
33186     },
33187     
33188     getMinHeight: function(){
33189         return this.minHeight;
33190     }
33191 });
33192
33193
33194
33195
33196  
33197
33198
33199
33200
33201
33202 Roo.bootstrap.layout.North = function(config)
33203 {
33204     config.region = 'north';
33205     config.cursor = 'n-resize';
33206     
33207     Roo.bootstrap.layout.Split.call(this, config);
33208     
33209     
33210     if(this.split){
33211         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33212         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33213         this.split.el.addClass("roo-layout-split-v");
33214     }
33215     var size = config.initialSize || config.height;
33216     if(typeof size != "undefined"){
33217         this.el.setHeight(size);
33218     }
33219 };
33220 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33221 {
33222     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33223     
33224     
33225     
33226     getBox : function(){
33227         if(this.collapsed){
33228             return this.collapsedEl.getBox();
33229         }
33230         var box = this.el.getBox();
33231         if(this.split){
33232             box.height += this.split.el.getHeight();
33233         }
33234         return box;
33235     },
33236     
33237     updateBox : function(box){
33238         if(this.split && !this.collapsed){
33239             box.height -= this.split.el.getHeight();
33240             this.split.el.setLeft(box.x);
33241             this.split.el.setTop(box.y+box.height);
33242             this.split.el.setWidth(box.width);
33243         }
33244         if(this.collapsed){
33245             this.updateBody(box.width, null);
33246         }
33247         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33248     }
33249 });
33250
33251
33252
33253
33254
33255 Roo.bootstrap.layout.South = function(config){
33256     config.region = 'south';
33257     config.cursor = 's-resize';
33258     Roo.bootstrap.layout.Split.call(this, config);
33259     if(this.split){
33260         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33261         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33262         this.split.el.addClass("roo-layout-split-v");
33263     }
33264     var size = config.initialSize || config.height;
33265     if(typeof size != "undefined"){
33266         this.el.setHeight(size);
33267     }
33268 };
33269
33270 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33271     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33272     getBox : function(){
33273         if(this.collapsed){
33274             return this.collapsedEl.getBox();
33275         }
33276         var box = this.el.getBox();
33277         if(this.split){
33278             var sh = this.split.el.getHeight();
33279             box.height += sh;
33280             box.y -= sh;
33281         }
33282         return box;
33283     },
33284     
33285     updateBox : function(box){
33286         if(this.split && !this.collapsed){
33287             var sh = this.split.el.getHeight();
33288             box.height -= sh;
33289             box.y += sh;
33290             this.split.el.setLeft(box.x);
33291             this.split.el.setTop(box.y-sh);
33292             this.split.el.setWidth(box.width);
33293         }
33294         if(this.collapsed){
33295             this.updateBody(box.width, null);
33296         }
33297         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33298     }
33299 });
33300
33301 Roo.bootstrap.layout.East = function(config){
33302     config.region = "east";
33303     config.cursor = "e-resize";
33304     Roo.bootstrap.layout.Split.call(this, config);
33305     if(this.split){
33306         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33307         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33308         this.split.el.addClass("roo-layout-split-h");
33309     }
33310     var size = config.initialSize || config.width;
33311     if(typeof size != "undefined"){
33312         this.el.setWidth(size);
33313     }
33314 };
33315 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33316     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33317     getBox : function(){
33318         if(this.collapsed){
33319             return this.collapsedEl.getBox();
33320         }
33321         var box = this.el.getBox();
33322         if(this.split){
33323             var sw = this.split.el.getWidth();
33324             box.width += sw;
33325             box.x -= sw;
33326         }
33327         return box;
33328     },
33329
33330     updateBox : function(box){
33331         if(this.split && !this.collapsed){
33332             var sw = this.split.el.getWidth();
33333             box.width -= sw;
33334             this.split.el.setLeft(box.x);
33335             this.split.el.setTop(box.y);
33336             this.split.el.setHeight(box.height);
33337             box.x += sw;
33338         }
33339         if(this.collapsed){
33340             this.updateBody(null, box.height);
33341         }
33342         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33343     }
33344 });
33345
33346 Roo.bootstrap.layout.West = function(config){
33347     config.region = "west";
33348     config.cursor = "w-resize";
33349     
33350     Roo.bootstrap.layout.Split.call(this, config);
33351     if(this.split){
33352         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33353         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33354         this.split.el.addClass("roo-layout-split-h");
33355     }
33356     
33357 };
33358 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33359     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33360     
33361     onRender: function(ctr, pos)
33362     {
33363         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33364         var size = this.config.initialSize || this.config.width;
33365         if(typeof size != "undefined"){
33366             this.el.setWidth(size);
33367         }
33368     },
33369     
33370     getBox : function(){
33371         if(this.collapsed){
33372             return this.collapsedEl.getBox();
33373         }
33374         var box = this.el.getBox();
33375         if(this.split){
33376             box.width += this.split.el.getWidth();
33377         }
33378         return box;
33379     },
33380     
33381     updateBox : function(box){
33382         if(this.split && !this.collapsed){
33383             var sw = this.split.el.getWidth();
33384             box.width -= sw;
33385             this.split.el.setLeft(box.x+box.width);
33386             this.split.el.setTop(box.y);
33387             this.split.el.setHeight(box.height);
33388         }
33389         if(this.collapsed){
33390             this.updateBody(null, box.height);
33391         }
33392         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33393     }
33394 });
33395 Roo.namespace("Roo.bootstrap.panel");/*
33396  * Based on:
33397  * Ext JS Library 1.1.1
33398  * Copyright(c) 2006-2007, Ext JS, LLC.
33399  *
33400  * Originally Released Under LGPL - original licence link has changed is not relivant.
33401  *
33402  * Fork - LGPL
33403  * <script type="text/javascript">
33404  */
33405 /**
33406  * @class Roo.ContentPanel
33407  * @extends Roo.util.Observable
33408  * A basic ContentPanel element.
33409  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33410  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33411  * @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
33412  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33413  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33414  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33415  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33416  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33417  * @cfg {String} title          The title for this panel
33418  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33419  * @cfg {String} url            Calls {@link #setUrl} with this value
33420  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33421  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33422  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33423  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33424
33425  * @constructor
33426  * Create a new ContentPanel.
33427  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33428  * @param {String/Object} config A string to set only the title or a config object
33429  * @param {String} content (optional) Set the HTML content for this panel
33430  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33431  */
33432 Roo.bootstrap.panel.Content = function( config){
33433     
33434     var el = config.el;
33435     var content = config.content;
33436
33437     if(config.autoCreate){ // xtype is available if this is called from factory
33438         el = Roo.id();
33439     }
33440     this.el = Roo.get(el);
33441     if(!this.el && config && config.autoCreate){
33442         if(typeof config.autoCreate == "object"){
33443             if(!config.autoCreate.id){
33444                 config.autoCreate.id = config.id||el;
33445             }
33446             this.el = Roo.DomHelper.append(document.body,
33447                         config.autoCreate, true);
33448         }else{
33449             var elcfg =  {   tag: "div",
33450                             cls: "roo-layout-inactive-content",
33451                             id: config.id||el
33452                             };
33453             if (config.html) {
33454                 elcfg.html = config.html;
33455                 
33456             }
33457                         
33458             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33459         }
33460     } 
33461     this.closable = false;
33462     this.loaded = false;
33463     this.active = false;
33464    
33465       
33466     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33467         
33468         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33469         
33470         this.wrapEl = this.el.wrap();
33471         var ti = [];
33472         if (config.toolbar.items) {
33473             ti = config.toolbar.items ;
33474             delete config.toolbar.items ;
33475         }
33476         
33477         var nitems = [];
33478         this.toolbar.render(this.wrapEl, 'before');
33479         for(var i =0;i < ti.length;i++) {
33480           //  Roo.log(['add child', items[i]]);
33481             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33482         }
33483         this.toolbar.items = nitems;
33484         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33485         delete config.toolbar;
33486         
33487     }
33488     /*
33489     // xtype created footer. - not sure if will work as we normally have to render first..
33490     if (this.footer && !this.footer.el && this.footer.xtype) {
33491         if (!this.wrapEl) {
33492             this.wrapEl = this.el.wrap();
33493         }
33494     
33495         this.footer.container = this.wrapEl.createChild();
33496          
33497         this.footer = Roo.factory(this.footer, Roo);
33498         
33499     }
33500     */
33501     
33502      if(typeof config == "string"){
33503         this.title = config;
33504     }else{
33505         Roo.apply(this, config);
33506     }
33507     
33508     if(this.resizeEl){
33509         this.resizeEl = Roo.get(this.resizeEl, true);
33510     }else{
33511         this.resizeEl = this.el;
33512     }
33513     // handle view.xtype
33514     
33515  
33516     
33517     
33518     this.addEvents({
33519         /**
33520          * @event activate
33521          * Fires when this panel is activated. 
33522          * @param {Roo.ContentPanel} this
33523          */
33524         "activate" : true,
33525         /**
33526          * @event deactivate
33527          * Fires when this panel is activated. 
33528          * @param {Roo.ContentPanel} this
33529          */
33530         "deactivate" : true,
33531
33532         /**
33533          * @event resize
33534          * Fires when this panel is resized if fitToFrame is true.
33535          * @param {Roo.ContentPanel} this
33536          * @param {Number} width The width after any component adjustments
33537          * @param {Number} height The height after any component adjustments
33538          */
33539         "resize" : true,
33540         
33541          /**
33542          * @event render
33543          * Fires when this tab is created
33544          * @param {Roo.ContentPanel} this
33545          */
33546         "render" : true
33547         
33548         
33549         
33550     });
33551     
33552
33553     
33554     
33555     if(this.autoScroll){
33556         this.resizeEl.setStyle("overflow", "auto");
33557     } else {
33558         // fix randome scrolling
33559         //this.el.on('scroll', function() {
33560         //    Roo.log('fix random scolling');
33561         //    this.scrollTo('top',0); 
33562         //});
33563     }
33564     content = content || this.content;
33565     if(content){
33566         this.setContent(content);
33567     }
33568     if(config && config.url){
33569         this.setUrl(this.url, this.params, this.loadOnce);
33570     }
33571     
33572     
33573     
33574     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33575     
33576     if (this.view && typeof(this.view.xtype) != 'undefined') {
33577         this.view.el = this.el.appendChild(document.createElement("div"));
33578         this.view = Roo.factory(this.view); 
33579         this.view.render  &&  this.view.render(false, '');  
33580     }
33581     
33582     
33583     this.fireEvent('render', this);
33584 };
33585
33586 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33587     tabTip:'',
33588     setRegion : function(region){
33589         this.region = region;
33590         if(region){
33591            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33592         }else{
33593            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33594         } 
33595     },
33596     
33597     /**
33598      * Returns the toolbar for this Panel if one was configured. 
33599      * @return {Roo.Toolbar} 
33600      */
33601     getToolbar : function(){
33602         return this.toolbar;
33603     },
33604     
33605     setActiveState : function(active){
33606         this.active = active;
33607         if(!active){
33608             this.fireEvent("deactivate", this);
33609         }else{
33610             this.fireEvent("activate", this);
33611         }
33612     },
33613     /**
33614      * Updates this panel's element
33615      * @param {String} content The new content
33616      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33617     */
33618     setContent : function(content, loadScripts){
33619         this.el.update(content, loadScripts);
33620     },
33621
33622     ignoreResize : function(w, h){
33623         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33624             return true;
33625         }else{
33626             this.lastSize = {width: w, height: h};
33627             return false;
33628         }
33629     },
33630     /**
33631      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33632      * @return {Roo.UpdateManager} The UpdateManager
33633      */
33634     getUpdateManager : function(){
33635         return this.el.getUpdateManager();
33636     },
33637      /**
33638      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33639      * @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:
33640 <pre><code>
33641 panel.load({
33642     url: "your-url.php",
33643     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33644     callback: yourFunction,
33645     scope: yourObject, //(optional scope)
33646     discardUrl: false,
33647     nocache: false,
33648     text: "Loading...",
33649     timeout: 30,
33650     scripts: false
33651 });
33652 </code></pre>
33653      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33654      * 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.
33655      * @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}
33656      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33657      * @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.
33658      * @return {Roo.ContentPanel} this
33659      */
33660     load : function(){
33661         var um = this.el.getUpdateManager();
33662         um.update.apply(um, arguments);
33663         return this;
33664     },
33665
33666
33667     /**
33668      * 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.
33669      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33670      * @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)
33671      * @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)
33672      * @return {Roo.UpdateManager} The UpdateManager
33673      */
33674     setUrl : function(url, params, loadOnce){
33675         if(this.refreshDelegate){
33676             this.removeListener("activate", this.refreshDelegate);
33677         }
33678         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33679         this.on("activate", this.refreshDelegate);
33680         return this.el.getUpdateManager();
33681     },
33682     
33683     _handleRefresh : function(url, params, loadOnce){
33684         if(!loadOnce || !this.loaded){
33685             var updater = this.el.getUpdateManager();
33686             updater.update(url, params, this._setLoaded.createDelegate(this));
33687         }
33688     },
33689     
33690     _setLoaded : function(){
33691         this.loaded = true;
33692     }, 
33693     
33694     /**
33695      * Returns this panel's id
33696      * @return {String} 
33697      */
33698     getId : function(){
33699         return this.el.id;
33700     },
33701     
33702     /** 
33703      * Returns this panel's element - used by regiosn to add.
33704      * @return {Roo.Element} 
33705      */
33706     getEl : function(){
33707         return this.wrapEl || this.el;
33708     },
33709     
33710    
33711     
33712     adjustForComponents : function(width, height)
33713     {
33714         //Roo.log('adjustForComponents ');
33715         if(this.resizeEl != this.el){
33716             width -= this.el.getFrameWidth('lr');
33717             height -= this.el.getFrameWidth('tb');
33718         }
33719         if(this.toolbar){
33720             var te = this.toolbar.getEl();
33721             height -= te.getHeight();
33722             te.setWidth(width);
33723         }
33724         if(this.footer){
33725             var te = this.footer.getEl();
33726             Roo.log("footer:" + te.getHeight());
33727             
33728             height -= te.getHeight();
33729             te.setWidth(width);
33730         }
33731         
33732         
33733         if(this.adjustments){
33734             width += this.adjustments[0];
33735             height += this.adjustments[1];
33736         }
33737         return {"width": width, "height": height};
33738     },
33739     
33740     setSize : function(width, height){
33741         if(this.fitToFrame && !this.ignoreResize(width, height)){
33742             if(this.fitContainer && this.resizeEl != this.el){
33743                 this.el.setSize(width, height);
33744             }
33745             var size = this.adjustForComponents(width, height);
33746             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33747             this.fireEvent('resize', this, size.width, size.height);
33748         }
33749     },
33750     
33751     /**
33752      * Returns this panel's title
33753      * @return {String} 
33754      */
33755     getTitle : function(){
33756         return this.title;
33757     },
33758     
33759     /**
33760      * Set this panel's title
33761      * @param {String} title
33762      */
33763     setTitle : function(title){
33764         this.title = title;
33765         if(this.region){
33766             this.region.updatePanelTitle(this, title);
33767         }
33768     },
33769     
33770     /**
33771      * Returns true is this panel was configured to be closable
33772      * @return {Boolean} 
33773      */
33774     isClosable : function(){
33775         return this.closable;
33776     },
33777     
33778     beforeSlide : function(){
33779         this.el.clip();
33780         this.resizeEl.clip();
33781     },
33782     
33783     afterSlide : function(){
33784         this.el.unclip();
33785         this.resizeEl.unclip();
33786     },
33787     
33788     /**
33789      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33790      *   Will fail silently if the {@link #setUrl} method has not been called.
33791      *   This does not activate the panel, just updates its content.
33792      */
33793     refresh : function(){
33794         if(this.refreshDelegate){
33795            this.loaded = false;
33796            this.refreshDelegate();
33797         }
33798     },
33799     
33800     /**
33801      * Destroys this panel
33802      */
33803     destroy : function(){
33804         this.el.removeAllListeners();
33805         var tempEl = document.createElement("span");
33806         tempEl.appendChild(this.el.dom);
33807         tempEl.innerHTML = "";
33808         this.el.remove();
33809         this.el = null;
33810     },
33811     
33812     /**
33813      * form - if the content panel contains a form - this is a reference to it.
33814      * @type {Roo.form.Form}
33815      */
33816     form : false,
33817     /**
33818      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33819      *    This contains a reference to it.
33820      * @type {Roo.View}
33821      */
33822     view : false,
33823     
33824       /**
33825      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33826      * <pre><code>
33827
33828 layout.addxtype({
33829        xtype : 'Form',
33830        items: [ .... ]
33831    }
33832 );
33833
33834 </code></pre>
33835      * @param {Object} cfg Xtype definition of item to add.
33836      */
33837     
33838     
33839     getChildContainer: function () {
33840         return this.getEl();
33841     }
33842     
33843     
33844     /*
33845         var  ret = new Roo.factory(cfg);
33846         return ret;
33847         
33848         
33849         // add form..
33850         if (cfg.xtype.match(/^Form$/)) {
33851             
33852             var el;
33853             //if (this.footer) {
33854             //    el = this.footer.container.insertSibling(false, 'before');
33855             //} else {
33856                 el = this.el.createChild();
33857             //}
33858
33859             this.form = new  Roo.form.Form(cfg);
33860             
33861             
33862             if ( this.form.allItems.length) {
33863                 this.form.render(el.dom);
33864             }
33865             return this.form;
33866         }
33867         // should only have one of theses..
33868         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33869             // views.. should not be just added - used named prop 'view''
33870             
33871             cfg.el = this.el.appendChild(document.createElement("div"));
33872             // factory?
33873             
33874             var ret = new Roo.factory(cfg);
33875              
33876              ret.render && ret.render(false, ''); // render blank..
33877             this.view = ret;
33878             return ret;
33879         }
33880         return false;
33881     }
33882     \*/
33883 });
33884  
33885 /**
33886  * @class Roo.bootstrap.panel.Grid
33887  * @extends Roo.bootstrap.panel.Content
33888  * @constructor
33889  * Create a new GridPanel.
33890  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33891  * @param {Object} config A the config object
33892   
33893  */
33894
33895
33896
33897 Roo.bootstrap.panel.Grid = function(config)
33898 {
33899     
33900       
33901     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33902         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33903
33904     config.el = this.wrapper;
33905     //this.el = this.wrapper;
33906     
33907     if(config.toolbar){
33908         var tool_el = this.wrapper.createChild();    
33909         this.toolbar = Roo.factory(config.toolbar);
33910         var ti = [];
33911         if (config.toolbar.items) {
33912             ti = config.toolbar.items ;
33913             delete config.toolbar.items ;
33914         }
33915         
33916         var nitems = [];
33917         this.toolbar.render(tool_el);
33918         for(var i =0;i < ti.length;i++) {
33919           //  Roo.log(['add child', items[i]]);
33920             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33921         }
33922         this.toolbar.items = nitems;
33923         
33924         delete config.toolbar;
33925     }
33926     
33927     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
33928     
33929     config.grid.monitorWindowResize = false; // turn off autosizing
33930     config.grid.autoHeight = false;
33931     config.grid.autoWidth = false;
33932     
33933     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
33934     
33935     if (config.background) {
33936         // render grid on panel activation (if panel background)
33937         this.on('activate', function(gp) {
33938             if (!gp.grid.rendered) {
33939                 gp.grid.render(el);
33940                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
33941
33942             }
33943         });
33944             
33945     } else {
33946         this.grid.render(this.wrapper);
33947         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
33948
33949     }
33950     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
33951     // ??? needed ??? config.el = this.wrapper;
33952     
33953     
33954     
33955   
33956     // xtype created footer. - not sure if will work as we normally have to render first..
33957     if (this.footer && !this.footer.el && this.footer.xtype) {
33958         
33959         var ctr = this.grid.getView().getFooterPanel(true);
33960         this.footer.dataSource = this.grid.dataSource;
33961         this.footer = Roo.factory(this.footer, Roo);
33962         this.footer.render(ctr);
33963         
33964     }
33965     
33966     
33967     
33968     
33969      
33970 };
33971
33972 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
33973     getId : function(){
33974         return this.grid.id;
33975     },
33976     
33977     /**
33978      * Returns the grid for this panel
33979      * @return {Roo.bootstrap.Table} 
33980      */
33981     getGrid : function(){
33982         return this.grid;    
33983     },
33984     
33985     setSize : function(width, height){
33986         if(!this.ignoreResize(width, height)){
33987             var grid = this.grid;
33988             var size = this.adjustForComponents(width, height);
33989             var gridel = grid.getGridEl();
33990             gridel.setSize(size.width, size.height);
33991             /*
33992             var thd = grid.getGridEl().select('thead',true).first();
33993             var tbd = grid.getGridEl().select('tbody', true).first();
33994             if (tbd) {
33995                 tbd.setSize(width, height - thd.getHeight());
33996             }
33997             */
33998             grid.autoSize();
33999         }
34000     },
34001      
34002     
34003     
34004     beforeSlide : function(){
34005         this.grid.getView().scroller.clip();
34006     },
34007     
34008     afterSlide : function(){
34009         this.grid.getView().scroller.unclip();
34010     },
34011     
34012     destroy : function(){
34013         this.grid.destroy();
34014         delete this.grid;
34015         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34016     }
34017 });
34018
34019 /**
34020  * @class Roo.bootstrap.panel.Nest
34021  * @extends Roo.bootstrap.panel.Content
34022  * @constructor
34023  * Create a new Panel, that can contain a layout.Border.
34024  * 
34025  * 
34026  * @param {Roo.BorderLayout} layout The layout for this panel
34027  * @param {String/Object} config A string to set only the title or a config object
34028  */
34029 Roo.bootstrap.panel.Nest = function(config)
34030 {
34031     // construct with only one argument..
34032     /* FIXME - implement nicer consturctors
34033     if (layout.layout) {
34034         config = layout;
34035         layout = config.layout;
34036         delete config.layout;
34037     }
34038     if (layout.xtype && !layout.getEl) {
34039         // then layout needs constructing..
34040         layout = Roo.factory(layout, Roo);
34041     }
34042     */
34043     
34044     config.el =  config.layout.getEl();
34045     
34046     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34047     
34048     config.layout.monitorWindowResize = false; // turn off autosizing
34049     this.layout = config.layout;
34050     this.layout.getEl().addClass("roo-layout-nested-layout");
34051     
34052     
34053     
34054     
34055 };
34056
34057 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34058
34059     setSize : function(width, height){
34060         if(!this.ignoreResize(width, height)){
34061             var size = this.adjustForComponents(width, height);
34062             var el = this.layout.getEl();
34063             el.setSize(size.width, size.height);
34064             var touch = el.dom.offsetWidth;
34065             this.layout.layout();
34066             // ie requires a double layout on the first pass
34067             if(Roo.isIE && !this.initialized){
34068                 this.initialized = true;
34069                 this.layout.layout();
34070             }
34071         }
34072     },
34073     
34074     // activate all subpanels if not currently active..
34075     
34076     setActiveState : function(active){
34077         this.active = active;
34078         if(!active){
34079             this.fireEvent("deactivate", this);
34080             return;
34081         }
34082         
34083         this.fireEvent("activate", this);
34084         // not sure if this should happen before or after..
34085         if (!this.layout) {
34086             return; // should not happen..
34087         }
34088         var reg = false;
34089         for (var r in this.layout.regions) {
34090             reg = this.layout.getRegion(r);
34091             if (reg.getActivePanel()) {
34092                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34093                 reg.setActivePanel(reg.getActivePanel());
34094                 continue;
34095             }
34096             if (!reg.panels.length) {
34097                 continue;
34098             }
34099             reg.showPanel(reg.getPanel(0));
34100         }
34101         
34102         
34103         
34104         
34105     },
34106     
34107     /**
34108      * Returns the nested BorderLayout for this panel
34109      * @return {Roo.BorderLayout} 
34110      */
34111     getLayout : function(){
34112         return this.layout;
34113     },
34114     
34115      /**
34116      * Adds a xtype elements to the layout of the nested panel
34117      * <pre><code>
34118
34119 panel.addxtype({
34120        xtype : 'ContentPanel',
34121        region: 'west',
34122        items: [ .... ]
34123    }
34124 );
34125
34126 panel.addxtype({
34127         xtype : 'NestedLayoutPanel',
34128         region: 'west',
34129         layout: {
34130            center: { },
34131            west: { }   
34132         },
34133         items : [ ... list of content panels or nested layout panels.. ]
34134    }
34135 );
34136 </code></pre>
34137      * @param {Object} cfg Xtype definition of item to add.
34138      */
34139     addxtype : function(cfg) {
34140         return this.layout.addxtype(cfg);
34141     
34142     }
34143 });        /*
34144  * Based on:
34145  * Ext JS Library 1.1.1
34146  * Copyright(c) 2006-2007, Ext JS, LLC.
34147  *
34148  * Originally Released Under LGPL - original licence link has changed is not relivant.
34149  *
34150  * Fork - LGPL
34151  * <script type="text/javascript">
34152  */
34153 /**
34154  * @class Roo.TabPanel
34155  * @extends Roo.util.Observable
34156  * A lightweight tab container.
34157  * <br><br>
34158  * Usage:
34159  * <pre><code>
34160 // basic tabs 1, built from existing content
34161 var tabs = new Roo.TabPanel("tabs1");
34162 tabs.addTab("script", "View Script");
34163 tabs.addTab("markup", "View Markup");
34164 tabs.activate("script");
34165
34166 // more advanced tabs, built from javascript
34167 var jtabs = new Roo.TabPanel("jtabs");
34168 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34169
34170 // set up the UpdateManager
34171 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34172 var updater = tab2.getUpdateManager();
34173 updater.setDefaultUrl("ajax1.htm");
34174 tab2.on('activate', updater.refresh, updater, true);
34175
34176 // Use setUrl for Ajax loading
34177 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34178 tab3.setUrl("ajax2.htm", null, true);
34179
34180 // Disabled tab
34181 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34182 tab4.disable();
34183
34184 jtabs.activate("jtabs-1");
34185  * </code></pre>
34186  * @constructor
34187  * Create a new TabPanel.
34188  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34189  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34190  */
34191 Roo.bootstrap.panel.Tabs = function(config){
34192     /**
34193     * The container element for this TabPanel.
34194     * @type Roo.Element
34195     */
34196     this.el = Roo.get(config.el);
34197     delete config.el;
34198     if(config){
34199         if(typeof config == "boolean"){
34200             this.tabPosition = config ? "bottom" : "top";
34201         }else{
34202             Roo.apply(this, config);
34203         }
34204     }
34205     
34206     if(this.tabPosition == "bottom"){
34207         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34208         this.el.addClass("roo-tabs-bottom");
34209     }
34210     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34211     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34212     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34213     if(Roo.isIE){
34214         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34215     }
34216     if(this.tabPosition != "bottom"){
34217         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34218          * @type Roo.Element
34219          */
34220         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34221         this.el.addClass("roo-tabs-top");
34222     }
34223     this.items = [];
34224
34225     this.bodyEl.setStyle("position", "relative");
34226
34227     this.active = null;
34228     this.activateDelegate = this.activate.createDelegate(this);
34229
34230     this.addEvents({
34231         /**
34232          * @event tabchange
34233          * Fires when the active tab changes
34234          * @param {Roo.TabPanel} this
34235          * @param {Roo.TabPanelItem} activePanel The new active tab
34236          */
34237         "tabchange": true,
34238         /**
34239          * @event beforetabchange
34240          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34241          * @param {Roo.TabPanel} this
34242          * @param {Object} e Set cancel to true on this object to cancel the tab change
34243          * @param {Roo.TabPanelItem} tab The tab being changed to
34244          */
34245         "beforetabchange" : true
34246     });
34247
34248     Roo.EventManager.onWindowResize(this.onResize, this);
34249     this.cpad = this.el.getPadding("lr");
34250     this.hiddenCount = 0;
34251
34252
34253     // toolbar on the tabbar support...
34254     if (this.toolbar) {
34255         alert("no toolbar support yet");
34256         this.toolbar  = false;
34257         /*
34258         var tcfg = this.toolbar;
34259         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34260         this.toolbar = new Roo.Toolbar(tcfg);
34261         if (Roo.isSafari) {
34262             var tbl = tcfg.container.child('table', true);
34263             tbl.setAttribute('width', '100%');
34264         }
34265         */
34266         
34267     }
34268    
34269
34270
34271     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34272 };
34273
34274 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34275     /*
34276      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34277      */
34278     tabPosition : "top",
34279     /*
34280      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34281      */
34282     currentTabWidth : 0,
34283     /*
34284      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34285      */
34286     minTabWidth : 40,
34287     /*
34288      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34289      */
34290     maxTabWidth : 250,
34291     /*
34292      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34293      */
34294     preferredTabWidth : 175,
34295     /*
34296      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34297      */
34298     resizeTabs : false,
34299     /*
34300      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34301      */
34302     monitorResize : true,
34303     /*
34304      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34305      */
34306     toolbar : false,
34307
34308     /**
34309      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34310      * @param {String} id The id of the div to use <b>or create</b>
34311      * @param {String} text The text for the tab
34312      * @param {String} content (optional) Content to put in the TabPanelItem body
34313      * @param {Boolean} closable (optional) True to create a close icon on the tab
34314      * @return {Roo.TabPanelItem} The created TabPanelItem
34315      */
34316     addTab : function(id, text, content, closable)
34317     {
34318         var item = new Roo.bootstrap.panel.TabItem({
34319             panel: this,
34320             id : id,
34321             text : text,
34322             closable : closable
34323         });
34324         this.addTabItem(item);
34325         if(content){
34326             item.setContent(content);
34327         }
34328         return item;
34329     },
34330
34331     /**
34332      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34333      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34334      * @return {Roo.TabPanelItem}
34335      */
34336     getTab : function(id){
34337         return this.items[id];
34338     },
34339
34340     /**
34341      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34342      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34343      */
34344     hideTab : function(id){
34345         var t = this.items[id];
34346         if(!t.isHidden()){
34347            t.setHidden(true);
34348            this.hiddenCount++;
34349            this.autoSizeTabs();
34350         }
34351     },
34352
34353     /**
34354      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34355      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34356      */
34357     unhideTab : function(id){
34358         var t = this.items[id];
34359         if(t.isHidden()){
34360            t.setHidden(false);
34361            this.hiddenCount--;
34362            this.autoSizeTabs();
34363         }
34364     },
34365
34366     /**
34367      * Adds an existing {@link Roo.TabPanelItem}.
34368      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34369      */
34370     addTabItem : function(item){
34371         this.items[item.id] = item;
34372         this.items.push(item);
34373       //  if(this.resizeTabs){
34374     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34375   //         this.autoSizeTabs();
34376 //        }else{
34377 //            item.autoSize();
34378        // }
34379     },
34380
34381     /**
34382      * Removes a {@link Roo.TabPanelItem}.
34383      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34384      */
34385     removeTab : function(id){
34386         var items = this.items;
34387         var tab = items[id];
34388         if(!tab) { return; }
34389         var index = items.indexOf(tab);
34390         if(this.active == tab && items.length > 1){
34391             var newTab = this.getNextAvailable(index);
34392             if(newTab) {
34393                 newTab.activate();
34394             }
34395         }
34396         this.stripEl.dom.removeChild(tab.pnode.dom);
34397         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34398             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34399         }
34400         items.splice(index, 1);
34401         delete this.items[tab.id];
34402         tab.fireEvent("close", tab);
34403         tab.purgeListeners();
34404         this.autoSizeTabs();
34405     },
34406
34407     getNextAvailable : function(start){
34408         var items = this.items;
34409         var index = start;
34410         // look for a next tab that will slide over to
34411         // replace the one being removed
34412         while(index < items.length){
34413             var item = items[++index];
34414             if(item && !item.isHidden()){
34415                 return item;
34416             }
34417         }
34418         // if one isn't found select the previous tab (on the left)
34419         index = start;
34420         while(index >= 0){
34421             var item = items[--index];
34422             if(item && !item.isHidden()){
34423                 return item;
34424             }
34425         }
34426         return null;
34427     },
34428
34429     /**
34430      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34431      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34432      */
34433     disableTab : function(id){
34434         var tab = this.items[id];
34435         if(tab && this.active != tab){
34436             tab.disable();
34437         }
34438     },
34439
34440     /**
34441      * Enables a {@link Roo.TabPanelItem} that is disabled.
34442      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34443      */
34444     enableTab : function(id){
34445         var tab = this.items[id];
34446         tab.enable();
34447     },
34448
34449     /**
34450      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34451      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34452      * @return {Roo.TabPanelItem} The TabPanelItem.
34453      */
34454     activate : function(id){
34455         var tab = this.items[id];
34456         if(!tab){
34457             return null;
34458         }
34459         if(tab == this.active || tab.disabled){
34460             return tab;
34461         }
34462         var e = {};
34463         this.fireEvent("beforetabchange", this, e, tab);
34464         if(e.cancel !== true && !tab.disabled){
34465             if(this.active){
34466                 this.active.hide();
34467             }
34468             this.active = this.items[id];
34469             this.active.show();
34470             this.fireEvent("tabchange", this, this.active);
34471         }
34472         return tab;
34473     },
34474
34475     /**
34476      * Gets the active {@link Roo.TabPanelItem}.
34477      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34478      */
34479     getActiveTab : function(){
34480         return this.active;
34481     },
34482
34483     /**
34484      * Updates the tab body element to fit the height of the container element
34485      * for overflow scrolling
34486      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34487      */
34488     syncHeight : function(targetHeight){
34489         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34490         var bm = this.bodyEl.getMargins();
34491         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34492         this.bodyEl.setHeight(newHeight);
34493         return newHeight;
34494     },
34495
34496     onResize : function(){
34497         if(this.monitorResize){
34498             this.autoSizeTabs();
34499         }
34500     },
34501
34502     /**
34503      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34504      */
34505     beginUpdate : function(){
34506         this.updating = true;
34507     },
34508
34509     /**
34510      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34511      */
34512     endUpdate : function(){
34513         this.updating = false;
34514         this.autoSizeTabs();
34515     },
34516
34517     /**
34518      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34519      */
34520     autoSizeTabs : function(){
34521         var count = this.items.length;
34522         var vcount = count - this.hiddenCount;
34523         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34524             return;
34525         }
34526         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34527         var availWidth = Math.floor(w / vcount);
34528         var b = this.stripBody;
34529         if(b.getWidth() > w){
34530             var tabs = this.items;
34531             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34532             if(availWidth < this.minTabWidth){
34533                 /*if(!this.sleft){    // incomplete scrolling code
34534                     this.createScrollButtons();
34535                 }
34536                 this.showScroll();
34537                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34538             }
34539         }else{
34540             if(this.currentTabWidth < this.preferredTabWidth){
34541                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34542             }
34543         }
34544     },
34545
34546     /**
34547      * Returns the number of tabs in this TabPanel.
34548      * @return {Number}
34549      */
34550      getCount : function(){
34551          return this.items.length;
34552      },
34553
34554     /**
34555      * Resizes all the tabs to the passed width
34556      * @param {Number} The new width
34557      */
34558     setTabWidth : function(width){
34559         this.currentTabWidth = width;
34560         for(var i = 0, len = this.items.length; i < len; i++) {
34561                 if(!this.items[i].isHidden()) {
34562                 this.items[i].setWidth(width);
34563             }
34564         }
34565     },
34566
34567     /**
34568      * Destroys this TabPanel
34569      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34570      */
34571     destroy : function(removeEl){
34572         Roo.EventManager.removeResizeListener(this.onResize, this);
34573         for(var i = 0, len = this.items.length; i < len; i++){
34574             this.items[i].purgeListeners();
34575         }
34576         if(removeEl === true){
34577             this.el.update("");
34578             this.el.remove();
34579         }
34580     },
34581     
34582     createStrip : function(container)
34583     {
34584         var strip = document.createElement("nav");
34585         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34586         container.appendChild(strip);
34587         return strip;
34588     },
34589     
34590     createStripList : function(strip)
34591     {
34592         // div wrapper for retard IE
34593         // returns the "tr" element.
34594         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34595         //'<div class="x-tabs-strip-wrap">'+
34596           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34597           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34598         return strip.firstChild; //.firstChild.firstChild.firstChild;
34599     },
34600     createBody : function(container)
34601     {
34602         var body = document.createElement("div");
34603         Roo.id(body, "tab-body");
34604         //Roo.fly(body).addClass("x-tabs-body");
34605         Roo.fly(body).addClass("tab-content");
34606         container.appendChild(body);
34607         return body;
34608     },
34609     createItemBody :function(bodyEl, id){
34610         var body = Roo.getDom(id);
34611         if(!body){
34612             body = document.createElement("div");
34613             body.id = id;
34614         }
34615         //Roo.fly(body).addClass("x-tabs-item-body");
34616         Roo.fly(body).addClass("tab-pane");
34617          bodyEl.insertBefore(body, bodyEl.firstChild);
34618         return body;
34619     },
34620     /** @private */
34621     createStripElements :  function(stripEl, text, closable)
34622     {
34623         var td = document.createElement("li"); // was td..
34624         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34625         //stripEl.appendChild(td);
34626         /*if(closable){
34627             td.className = "x-tabs-closable";
34628             if(!this.closeTpl){
34629                 this.closeTpl = new Roo.Template(
34630                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34631                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34632                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34633                 );
34634             }
34635             var el = this.closeTpl.overwrite(td, {"text": text});
34636             var close = el.getElementsByTagName("div")[0];
34637             var inner = el.getElementsByTagName("em")[0];
34638             return {"el": el, "close": close, "inner": inner};
34639         } else {
34640         */
34641         // not sure what this is..
34642             if(!this.tabTpl){
34643                 //this.tabTpl = new Roo.Template(
34644                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34645                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34646                 //);
34647                 this.tabTpl = new Roo.Template(
34648                    '<a href="#">' +
34649                    '<span unselectable="on"' +
34650                             (this.disableTooltips ? '' : ' title="{text}"') +
34651                             ' >{text}</span></span></a>'
34652                 );
34653                 
34654             }
34655             var el = this.tabTpl.overwrite(td, {"text": text});
34656             var inner = el.getElementsByTagName("span")[0];
34657             return {"el": el, "inner": inner};
34658         //}
34659     }
34660         
34661     
34662 });
34663
34664 /**
34665  * @class Roo.TabPanelItem
34666  * @extends Roo.util.Observable
34667  * Represents an individual item (tab plus body) in a TabPanel.
34668  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34669  * @param {String} id The id of this TabPanelItem
34670  * @param {String} text The text for the tab of this TabPanelItem
34671  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34672  */
34673 Roo.bootstrap.panel.TabItem = function(config){
34674     /**
34675      * The {@link Roo.TabPanel} this TabPanelItem belongs to
34676      * @type Roo.TabPanel
34677      */
34678     this.tabPanel = config.panel;
34679     /**
34680      * The id for this TabPanelItem
34681      * @type String
34682      */
34683     this.id = config.id;
34684     /** @private */
34685     this.disabled = false;
34686     /** @private */
34687     this.text = config.text;
34688     /** @private */
34689     this.loaded = false;
34690     this.closable = config.closable;
34691
34692     /**
34693      * The body element for this TabPanelItem.
34694      * @type Roo.Element
34695      */
34696     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34697     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34698     this.bodyEl.setStyle("display", "block");
34699     this.bodyEl.setStyle("zoom", "1");
34700     //this.hideAction();
34701
34702     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34703     /** @private */
34704     this.el = Roo.get(els.el);
34705     this.inner = Roo.get(els.inner, true);
34706     this.textEl = Roo.get(this.el.dom.firstChild, true);
34707     this.pnode = Roo.get(els.el.parentNode, true);
34708     this.el.on("mousedown", this.onTabMouseDown, this);
34709     this.el.on("click", this.onTabClick, this);
34710     /** @private */
34711     if(config.closable){
34712         var c = Roo.get(els.close, true);
34713         c.dom.title = this.closeText;
34714         c.addClassOnOver("close-over");
34715         c.on("click", this.closeClick, this);
34716      }
34717
34718     this.addEvents({
34719          /**
34720          * @event activate
34721          * Fires when this tab becomes the active tab.
34722          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34723          * @param {Roo.TabPanelItem} this
34724          */
34725         "activate": true,
34726         /**
34727          * @event beforeclose
34728          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34729          * @param {Roo.TabPanelItem} this
34730          * @param {Object} e Set cancel to true on this object to cancel the close.
34731          */
34732         "beforeclose": true,
34733         /**
34734          * @event close
34735          * Fires when this tab is closed.
34736          * @param {Roo.TabPanelItem} this
34737          */
34738          "close": true,
34739         /**
34740          * @event deactivate
34741          * Fires when this tab is no longer the active tab.
34742          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34743          * @param {Roo.TabPanelItem} this
34744          */
34745          "deactivate" : true
34746     });
34747     this.hidden = false;
34748
34749     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34750 };
34751
34752 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34753            {
34754     purgeListeners : function(){
34755        Roo.util.Observable.prototype.purgeListeners.call(this);
34756        this.el.removeAllListeners();
34757     },
34758     /**
34759      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34760      */
34761     show : function(){
34762         this.pnode.addClass("active");
34763         this.showAction();
34764         if(Roo.isOpera){
34765             this.tabPanel.stripWrap.repaint();
34766         }
34767         this.fireEvent("activate", this.tabPanel, this);
34768     },
34769
34770     /**
34771      * Returns true if this tab is the active tab.
34772      * @return {Boolean}
34773      */
34774     isActive : function(){
34775         return this.tabPanel.getActiveTab() == this;
34776     },
34777
34778     /**
34779      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34780      */
34781     hide : function(){
34782         this.pnode.removeClass("active");
34783         this.hideAction();
34784         this.fireEvent("deactivate", this.tabPanel, this);
34785     },
34786
34787     hideAction : function(){
34788         this.bodyEl.hide();
34789         this.bodyEl.setStyle("position", "absolute");
34790         this.bodyEl.setLeft("-20000px");
34791         this.bodyEl.setTop("-20000px");
34792     },
34793
34794     showAction : function(){
34795         this.bodyEl.setStyle("position", "relative");
34796         this.bodyEl.setTop("");
34797         this.bodyEl.setLeft("");
34798         this.bodyEl.show();
34799     },
34800
34801     /**
34802      * Set the tooltip for the tab.
34803      * @param {String} tooltip The tab's tooltip
34804      */
34805     setTooltip : function(text){
34806         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34807             this.textEl.dom.qtip = text;
34808             this.textEl.dom.removeAttribute('title');
34809         }else{
34810             this.textEl.dom.title = text;
34811         }
34812     },
34813
34814     onTabClick : function(e){
34815         e.preventDefault();
34816         this.tabPanel.activate(this.id);
34817     },
34818
34819     onTabMouseDown : function(e){
34820         e.preventDefault();
34821         this.tabPanel.activate(this.id);
34822     },
34823 /*
34824     getWidth : function(){
34825         return this.inner.getWidth();
34826     },
34827
34828     setWidth : function(width){
34829         var iwidth = width - this.pnode.getPadding("lr");
34830         this.inner.setWidth(iwidth);
34831         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34832         this.pnode.setWidth(width);
34833     },
34834 */
34835     /**
34836      * Show or hide the tab
34837      * @param {Boolean} hidden True to hide or false to show.
34838      */
34839     setHidden : function(hidden){
34840         this.hidden = hidden;
34841         this.pnode.setStyle("display", hidden ? "none" : "");
34842     },
34843
34844     /**
34845      * Returns true if this tab is "hidden"
34846      * @return {Boolean}
34847      */
34848     isHidden : function(){
34849         return this.hidden;
34850     },
34851
34852     /**
34853      * Returns the text for this tab
34854      * @return {String}
34855      */
34856     getText : function(){
34857         return this.text;
34858     },
34859     /*
34860     autoSize : function(){
34861         //this.el.beginMeasure();
34862         this.textEl.setWidth(1);
34863         /*
34864          *  #2804 [new] Tabs in Roojs
34865          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34866          */
34867         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34868         //this.el.endMeasure();
34869     //},
34870
34871     /**
34872      * Sets the text for the tab (Note: this also sets the tooltip text)
34873      * @param {String} text The tab's text and tooltip
34874      */
34875     setText : function(text){
34876         this.text = text;
34877         this.textEl.update(text);
34878         this.setTooltip(text);
34879         //if(!this.tabPanel.resizeTabs){
34880         //    this.autoSize();
34881         //}
34882     },
34883     /**
34884      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34885      */
34886     activate : function(){
34887         this.tabPanel.activate(this.id);
34888     },
34889
34890     /**
34891      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34892      */
34893     disable : function(){
34894         if(this.tabPanel.active != this){
34895             this.disabled = true;
34896             this.pnode.addClass("disabled");
34897         }
34898     },
34899
34900     /**
34901      * Enables this TabPanelItem if it was previously disabled.
34902      */
34903     enable : function(){
34904         this.disabled = false;
34905         this.pnode.removeClass("disabled");
34906     },
34907
34908     /**
34909      * Sets the content for this TabPanelItem.
34910      * @param {String} content The content
34911      * @param {Boolean} loadScripts true to look for and load scripts
34912      */
34913     setContent : function(content, loadScripts){
34914         this.bodyEl.update(content, loadScripts);
34915     },
34916
34917     /**
34918      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
34919      * @return {Roo.UpdateManager} The UpdateManager
34920      */
34921     getUpdateManager : function(){
34922         return this.bodyEl.getUpdateManager();
34923     },
34924
34925     /**
34926      * Set a URL to be used to load the content for this TabPanelItem.
34927      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
34928      * @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)
34929      * @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)
34930      * @return {Roo.UpdateManager} The UpdateManager
34931      */
34932     setUrl : function(url, params, loadOnce){
34933         if(this.refreshDelegate){
34934             this.un('activate', this.refreshDelegate);
34935         }
34936         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34937         this.on("activate", this.refreshDelegate);
34938         return this.bodyEl.getUpdateManager();
34939     },
34940
34941     /** @private */
34942     _handleRefresh : function(url, params, loadOnce){
34943         if(!loadOnce || !this.loaded){
34944             var updater = this.bodyEl.getUpdateManager();
34945             updater.update(url, params, this._setLoaded.createDelegate(this));
34946         }
34947     },
34948
34949     /**
34950      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
34951      *   Will fail silently if the setUrl method has not been called.
34952      *   This does not activate the panel, just updates its content.
34953      */
34954     refresh : function(){
34955         if(this.refreshDelegate){
34956            this.loaded = false;
34957            this.refreshDelegate();
34958         }
34959     },
34960
34961     /** @private */
34962     _setLoaded : function(){
34963         this.loaded = true;
34964     },
34965
34966     /** @private */
34967     closeClick : function(e){
34968         var o = {};
34969         e.stopEvent();
34970         this.fireEvent("beforeclose", this, o);
34971         if(o.cancel !== true){
34972             this.tabPanel.removeTab(this.id);
34973         }
34974     },
34975     /**
34976      * The text displayed in the tooltip for the close icon.
34977      * @type String
34978      */
34979     closeText : "Close this tab"
34980 });