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   
5699     
5700     // BC...
5701     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5702     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5703     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5704     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5705     
5706     
5707     if (this.sm) {
5708         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5709         this.sm = this.selModel;
5710         this.sm.xmodule = this.xmodule || false;
5711     }
5712     if (this.cm && typeof(this.cm.config) == 'undefined') {
5713         this.colModel = new Roo.grid.ColumnModel(this.cm);
5714         this.cm = this.colModel;
5715         this.cm.xmodule = this.xmodule || false;
5716     }
5717     if (this.store) {
5718         this.store= Roo.factory(this.store, Roo.data);
5719         this.ds = this.store;
5720         this.ds.xmodule = this.xmodule || false;
5721          
5722     }
5723     if (this.footer && this.store) {
5724         this.footer.dataSource = this.ds;
5725         this.footer = Roo.factory(this.footer);
5726     }
5727     
5728     /** @private */
5729     this.addEvents({
5730         /**
5731          * @event cellclick
5732          * Fires when a cell is clicked
5733          * @param {Roo.bootstrap.Table} this
5734          * @param {Roo.Element} el
5735          * @param {Number} rowIndex
5736          * @param {Number} columnIndex
5737          * @param {Roo.EventObject} e
5738          */
5739         "cellclick" : true,
5740         /**
5741          * @event celldblclick
5742          * Fires when a cell is double clicked
5743          * @param {Roo.bootstrap.Table} this
5744          * @param {Roo.Element} el
5745          * @param {Number} rowIndex
5746          * @param {Number} columnIndex
5747          * @param {Roo.EventObject} e
5748          */
5749         "celldblclick" : true,
5750         /**
5751          * @event rowclick
5752          * Fires when a row is clicked
5753          * @param {Roo.bootstrap.Table} this
5754          * @param {Roo.Element} el
5755          * @param {Number} rowIndex
5756          * @param {Roo.EventObject} e
5757          */
5758         "rowclick" : true,
5759         /**
5760          * @event rowdblclick
5761          * Fires when a row is double clicked
5762          * @param {Roo.bootstrap.Table} this
5763          * @param {Roo.Element} el
5764          * @param {Number} rowIndex
5765          * @param {Roo.EventObject} e
5766          */
5767         "rowdblclick" : true,
5768         /**
5769          * @event mouseover
5770          * Fires when a mouseover occur
5771          * @param {Roo.bootstrap.Table} this
5772          * @param {Roo.Element} el
5773          * @param {Number} rowIndex
5774          * @param {Number} columnIndex
5775          * @param {Roo.EventObject} e
5776          */
5777         "mouseover" : true,
5778         /**
5779          * @event mouseout
5780          * Fires when a mouseout occur
5781          * @param {Roo.bootstrap.Table} this
5782          * @param {Roo.Element} el
5783          * @param {Number} rowIndex
5784          * @param {Number} columnIndex
5785          * @param {Roo.EventObject} e
5786          */
5787         "mouseout" : true,
5788         /**
5789          * @event rowclass
5790          * Fires when a row is rendered, so you can change add a style to it.
5791          * @param {Roo.bootstrap.Table} this
5792          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5793          */
5794         'rowclass' : true,
5795           /**
5796          * @event rowsrendered
5797          * Fires when all the  rows have been rendered
5798          * @param {Roo.bootstrap.Table} this
5799          */
5800         'rowsrendered' : true,
5801         /**
5802          * @event contextmenu
5803          * The raw contextmenu event for the entire grid.
5804          * @param {Roo.EventObject} e
5805          */
5806         "contextmenu" : true,
5807         /**
5808          * @event rowcontextmenu
5809          * Fires when a row is right clicked
5810          * @param {Roo.bootstrap.Table} this
5811          * @param {Number} rowIndex
5812          * @param {Roo.EventObject} e
5813          */
5814         "rowcontextmenu" : true,
5815         /**
5816          * @event cellcontextmenu
5817          * Fires when a cell is right clicked
5818          * @param {Roo.bootstrap.Table} this
5819          * @param {Number} rowIndex
5820          * @param {Number} cellIndex
5821          * @param {Roo.EventObject} e
5822          */
5823          "cellcontextmenu" : true,
5824          /**
5825          * @event headercontextmenu
5826          * Fires when a header is right clicked
5827          * @param {Roo.bootstrap.Table} this
5828          * @param {Number} columnIndex
5829          * @param {Roo.EventObject} e
5830          */
5831         "headercontextmenu" : true
5832     });
5833 };
5834
5835 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5836     
5837     cls: false,
5838     align: false,
5839     bgcolor: false,
5840     border: false,
5841     cellpadding: false,
5842     cellspacing: false,
5843     frame: false,
5844     rules: false,
5845     sortable: false,
5846     summary: false,
5847     width: false,
5848     striped : false,
5849     scrollBody : false,
5850     bordered: false,
5851     hover:  false,
5852     condensed : false,
5853     responsive : false,
5854     sm : false,
5855     cm : false,
5856     store : false,
5857     loadMask : false,
5858     footerShow : true,
5859     headerShow : true,
5860   
5861     rowSelection : false,
5862     cellSelection : false,
5863     layout : false,
5864     
5865     // Roo.Element - the tbody
5866     mainBody: false,
5867     // Roo.Element - thead element
5868     mainHead: false,
5869     
5870     container: false, // used by gridpanel...
5871     
5872     getAutoCreate : function()
5873     {
5874         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5875         
5876         cfg = {
5877             tag: 'table',
5878             cls : 'table',
5879             cn : []
5880         };
5881         if (this.scrollBody) {
5882             cfg.cls += ' table-body-fixed';
5883         }    
5884         if (this.striped) {
5885             cfg.cls += ' table-striped';
5886         }
5887         
5888         if (this.hover) {
5889             cfg.cls += ' table-hover';
5890         }
5891         if (this.bordered) {
5892             cfg.cls += ' table-bordered';
5893         }
5894         if (this.condensed) {
5895             cfg.cls += ' table-condensed';
5896         }
5897         if (this.responsive) {
5898             cfg.cls += ' table-responsive';
5899         }
5900         
5901         if (this.cls) {
5902             cfg.cls+=  ' ' +this.cls;
5903         }
5904         
5905         // this lot should be simplifed...
5906         
5907         if (this.align) {
5908             cfg.align=this.align;
5909         }
5910         if (this.bgcolor) {
5911             cfg.bgcolor=this.bgcolor;
5912         }
5913         if (this.border) {
5914             cfg.border=this.border;
5915         }
5916         if (this.cellpadding) {
5917             cfg.cellpadding=this.cellpadding;
5918         }
5919         if (this.cellspacing) {
5920             cfg.cellspacing=this.cellspacing;
5921         }
5922         if (this.frame) {
5923             cfg.frame=this.frame;
5924         }
5925         if (this.rules) {
5926             cfg.rules=this.rules;
5927         }
5928         if (this.sortable) {
5929             cfg.sortable=this.sortable;
5930         }
5931         if (this.summary) {
5932             cfg.summary=this.summary;
5933         }
5934         if (this.width) {
5935             cfg.width=this.width;
5936         }
5937         if (this.layout) {
5938             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5939         }
5940         
5941         if(this.store || this.cm){
5942             if(this.headerShow){
5943                 cfg.cn.push(this.renderHeader());
5944             }
5945             
5946             cfg.cn.push(this.renderBody());
5947             
5948             if(this.footerShow){
5949                 cfg.cn.push(this.renderFooter());
5950             }
5951             // where does this come from?
5952             //cfg.cls+=  ' TableGrid';
5953         }
5954         
5955         return { cn : [ cfg ] };
5956     },
5957     
5958     initEvents : function()
5959     {   
5960         if(!this.store || !this.cm){
5961             return;
5962         }
5963         
5964         //Roo.log('initEvents with ds!!!!');
5965         
5966         this.mainBody = this.el.select('tbody', true).first();
5967         this.mainHead = this.el.select('thead', true).first();
5968         
5969         
5970         
5971         var _this = this;
5972         
5973         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5974             e.on('click', _this.sort, _this);
5975         });
5976         
5977         this.el.on("click", this.onClick, this);
5978         this.el.on("dblclick", this.onDblClick, this);
5979         
5980         // why is this done????? = it breaks dialogs??
5981         //this.parent().el.setStyle('position', 'relative');
5982         
5983         
5984         if (this.footer) {
5985             this.footer.parentId = this.id;
5986             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5987         }
5988         
5989         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5990         
5991         this.store.on('load', this.onLoad, this);
5992         this.store.on('beforeload', this.onBeforeLoad, this);
5993         this.store.on('update', this.onUpdate, this);
5994         this.store.on('add', this.onAdd, this);
5995         
5996         this.el.on("contextmenu", this.onContextMenu, this);
5997         
5998         this.mainBody.on('scroll', this.onBodyScroll, this);
5999         
6000         
6001     },
6002     
6003     onContextMenu : function(e, t)
6004     {
6005         this.processEvent("contextmenu", e);
6006     },
6007     
6008     processEvent : function(name, e)
6009     {
6010         if (name != 'touchstart' ) {
6011             this.fireEvent(name, e);    
6012         }
6013         
6014         var t = e.getTarget();
6015         
6016         var cell = Roo.get(t);
6017         
6018         if(!cell){
6019             return;
6020         }
6021         
6022         if(cell.findParent('tfoot', false, true)){
6023             return;
6024         }
6025         
6026         if(cell.findParent('thead', false, true)){
6027             
6028             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6029                 cell = Roo.get(t).findParent('th', false, true);
6030                 if (!cell) {
6031                     Roo.log("failed to find th in thead?");
6032                     Roo.log(e.getTarget());
6033                     return;
6034                 }
6035             }
6036             
6037             var cellIndex = cell.dom.cellIndex;
6038             
6039             var ename = name == 'touchstart' ? 'click' : name;
6040             this.fireEvent("header" + ename, this, cellIndex, e);
6041             
6042             return;
6043         }
6044         
6045         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6046             cell = Roo.get(t).findParent('td', false, true);
6047             if (!cell) {
6048                 Roo.log("failed to find th in tbody?");
6049                 Roo.log(e.getTarget());
6050                 return;
6051             }
6052         }
6053         
6054         var row = cell.findParent('tr', false, true);
6055         var cellIndex = cell.dom.cellIndex;
6056         var rowIndex = row.dom.rowIndex - 1;
6057         
6058         if(row !== false){
6059             
6060             this.fireEvent("row" + name, this, rowIndex, e);
6061             
6062             if(cell !== false){
6063             
6064                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6065             }
6066         }
6067         
6068     },
6069     
6070     onMouseover : function(e, el)
6071     {
6072         var cell = Roo.get(el);
6073         
6074         if(!cell){
6075             return;
6076         }
6077         
6078         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6079             cell = cell.findParent('td', false, true);
6080         }
6081         
6082         var row = cell.findParent('tr', false, true);
6083         var cellIndex = cell.dom.cellIndex;
6084         var rowIndex = row.dom.rowIndex - 1; // start from 0
6085         
6086         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6087         
6088     },
6089     
6090     onMouseout : function(e, el)
6091     {
6092         var cell = Roo.get(el);
6093         
6094         if(!cell){
6095             return;
6096         }
6097         
6098         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6099             cell = cell.findParent('td', false, true);
6100         }
6101         
6102         var row = cell.findParent('tr', false, true);
6103         var cellIndex = cell.dom.cellIndex;
6104         var rowIndex = row.dom.rowIndex - 1; // start from 0
6105         
6106         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6107         
6108     },
6109     
6110     onClick : function(e, el)
6111     {
6112         var cell = Roo.get(el);
6113         
6114         if(!cell || (!this.cellSelection && !this.rowSelection)){
6115             return;
6116         }
6117         
6118         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6119             cell = cell.findParent('td', false, true);
6120         }
6121         
6122         if(!cell || typeof(cell) == 'undefined'){
6123             return;
6124         }
6125         
6126         var row = cell.findParent('tr', false, true);
6127         
6128         if(!row || typeof(row) == 'undefined'){
6129             return;
6130         }
6131         
6132         var cellIndex = cell.dom.cellIndex;
6133         var rowIndex = this.getRowIndex(row);
6134         
6135         // why??? - should these not be based on SelectionModel?
6136         if(this.cellSelection){
6137             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6138         }
6139         
6140         if(this.rowSelection){
6141             this.fireEvent('rowclick', this, row, rowIndex, e);
6142         }
6143         
6144         
6145     },
6146     
6147     onDblClick : function(e,el)
6148     {
6149         var cell = Roo.get(el);
6150         
6151         if(!cell || (!this.CellSelection && !this.RowSelection)){
6152             return;
6153         }
6154         
6155         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6156             cell = cell.findParent('td', false, true);
6157         }
6158         
6159         if(!cell || typeof(cell) == 'undefined'){
6160             return;
6161         }
6162         
6163         var row = cell.findParent('tr', false, true);
6164         
6165         if(!row || typeof(row) == 'undefined'){
6166             return;
6167         }
6168         
6169         var cellIndex = cell.dom.cellIndex;
6170         var rowIndex = this.getRowIndex(row);
6171         
6172         if(this.CellSelection){
6173             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6174         }
6175         
6176         if(this.RowSelection){
6177             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6178         }
6179     },
6180     
6181     sort : function(e,el)
6182     {
6183         var col = Roo.get(el);
6184         
6185         if(!col.hasClass('sortable')){
6186             return;
6187         }
6188         
6189         var sort = col.attr('sort');
6190         var dir = 'ASC';
6191         
6192         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6193             dir = 'DESC';
6194         }
6195         
6196         this.store.sortInfo = {field : sort, direction : dir};
6197         
6198         if (this.footer) {
6199             Roo.log("calling footer first");
6200             this.footer.onClick('first');
6201         } else {
6202         
6203             this.store.load({ params : { start : 0 } });
6204         }
6205     },
6206     
6207     renderHeader : function()
6208     {
6209         var header = {
6210             tag: 'thead',
6211             cn : []
6212         };
6213         
6214         var cm = this.cm;
6215         this.totalWidth = 0;
6216         
6217         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6218             
6219             var config = cm.config[i];
6220             
6221             var c = {
6222                 tag: 'th',
6223                 style : '',
6224                 html: cm.getColumnHeader(i)
6225             };
6226             
6227             var hh = '';
6228             
6229             if(typeof(config.sortable) != 'undefined' && config.sortable){
6230                 c.cls = 'sortable';
6231                 c.html = '<i class="glyphicon"></i>' + c.html;
6232             }
6233             
6234             if(typeof(config.lgHeader) != 'undefined'){
6235                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6236             }
6237             
6238             if(typeof(config.mdHeader) != 'undefined'){
6239                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6240             }
6241             
6242             if(typeof(config.smHeader) != 'undefined'){
6243                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6244             }
6245             
6246             if(typeof(config.xsHeader) != 'undefined'){
6247                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6248             }
6249             
6250             if(hh.length){
6251                 c.html = hh;
6252             }
6253             
6254             if(typeof(config.tooltip) != 'undefined'){
6255                 c.tooltip = config.tooltip;
6256             }
6257             
6258             if(typeof(config.colspan) != 'undefined'){
6259                 c.colspan = config.colspan;
6260             }
6261             
6262             if(typeof(config.hidden) != 'undefined' && config.hidden){
6263                 c.style += ' display:none;';
6264             }
6265             
6266             if(typeof(config.dataIndex) != 'undefined'){
6267                 c.sort = config.dataIndex;
6268             }
6269             
6270            
6271             
6272             if(typeof(config.align) != 'undefined' && config.align.length){
6273                 c.style += ' text-align:' + config.align + ';';
6274             }
6275             
6276             if(typeof(config.width) != 'undefined'){
6277                 c.style += ' width:' + config.width + 'px;';
6278                 this.totalWidth += config.width;
6279             } else {
6280                 this.totalWidth += 100; // assume minimum of 100 per column?
6281             }
6282             
6283             if(typeof(config.cls) != 'undefined'){
6284                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6285             }
6286             
6287             ['xs','sm','md','lg'].map(function(size){
6288                 
6289                 if(typeof(config[size]) == 'undefined'){
6290                     return;
6291                 }
6292                 
6293                 if (!config[size]) { // 0 = hidden
6294                     c.cls += ' hidden-' + size;
6295                     return;
6296                 }
6297                 
6298                 c.cls += ' col-' + size + '-' + config[size];
6299
6300             });
6301             
6302             header.cn.push(c)
6303         }
6304         
6305         return header;
6306     },
6307     
6308     renderBody : function()
6309     {
6310         var body = {
6311             tag: 'tbody',
6312             cn : [
6313                 {
6314                     tag: 'tr',
6315                     cn : [
6316                         {
6317                             tag : 'td',
6318                             colspan :  this.cm.getColumnCount()
6319                         }
6320                     ]
6321                 }
6322             ]
6323         };
6324         
6325         return body;
6326     },
6327     
6328     renderFooter : function()
6329     {
6330         var footer = {
6331             tag: 'tfoot',
6332             cn : [
6333                 {
6334                     tag: 'tr',
6335                     cn : [
6336                         {
6337                             tag : 'td',
6338                             colspan :  this.cm.getColumnCount()
6339                         }
6340                     ]
6341                 }
6342             ]
6343         };
6344         
6345         return footer;
6346     },
6347     
6348     
6349     
6350     onLoad : function()
6351     {
6352 //        Roo.log('ds onload');
6353         this.clear();
6354         
6355         var _this = this;
6356         var cm = this.cm;
6357         var ds = this.store;
6358         
6359         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6360             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6361             if (_this.store.sortInfo) {
6362                     
6363                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6364                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6365                 }
6366                 
6367                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6368                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6369                 }
6370             }
6371         });
6372         
6373         var tbody =  this.mainBody;
6374               
6375         if(ds.getCount() > 0){
6376             ds.data.each(function(d,rowIndex){
6377                 var row =  this.renderRow(cm, ds, rowIndex);
6378                 
6379                 tbody.createChild(row);
6380                 
6381                 var _this = this;
6382                 
6383                 if(row.cellObjects.length){
6384                     Roo.each(row.cellObjects, function(r){
6385                         _this.renderCellObject(r);
6386                     })
6387                 }
6388                 
6389             }, this);
6390         }
6391         
6392         Roo.each(this.el.select('tbody td', true).elements, function(e){
6393             e.on('mouseover', _this.onMouseover, _this);
6394         });
6395         
6396         Roo.each(this.el.select('tbody td', true).elements, function(e){
6397             e.on('mouseout', _this.onMouseout, _this);
6398         });
6399         this.fireEvent('rowsrendered', this);
6400         //if(this.loadMask){
6401         //    this.maskEl.hide();
6402         //}
6403         
6404         this.autoSize();
6405     },
6406     
6407     
6408     onUpdate : function(ds,record)
6409     {
6410         this.refreshRow(record);
6411     },
6412     
6413     onRemove : function(ds, record, index, isUpdate){
6414         if(isUpdate !== true){
6415             this.fireEvent("beforerowremoved", this, index, record);
6416         }
6417         var bt = this.mainBody.dom;
6418         
6419         var rows = this.el.select('tbody > tr', true).elements;
6420         
6421         if(typeof(rows[index]) != 'undefined'){
6422             bt.removeChild(rows[index].dom);
6423         }
6424         
6425 //        if(bt.rows[index]){
6426 //            bt.removeChild(bt.rows[index]);
6427 //        }
6428         
6429         if(isUpdate !== true){
6430             //this.stripeRows(index);
6431             //this.syncRowHeights(index, index);
6432             //this.layout();
6433             this.fireEvent("rowremoved", this, index, record);
6434         }
6435     },
6436     
6437     onAdd : function(ds, records, rowIndex)
6438     {
6439         //Roo.log('on Add called');
6440         // - note this does not handle multiple adding very well..
6441         var bt = this.mainBody.dom;
6442         for (var i =0 ; i < records.length;i++) {
6443             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6444             //Roo.log(records[i]);
6445             //Roo.log(this.store.getAt(rowIndex+i));
6446             this.insertRow(this.store, rowIndex + i, false);
6447             return;
6448         }
6449         
6450     },
6451     
6452     
6453     refreshRow : function(record){
6454         var ds = this.store, index;
6455         if(typeof record == 'number'){
6456             index = record;
6457             record = ds.getAt(index);
6458         }else{
6459             index = ds.indexOf(record);
6460         }
6461         this.insertRow(ds, index, true);
6462         this.onRemove(ds, record, index+1, true);
6463         //this.syncRowHeights(index, index);
6464         //this.layout();
6465         this.fireEvent("rowupdated", this, index, record);
6466     },
6467     
6468     insertRow : function(dm, rowIndex, isUpdate){
6469         
6470         if(!isUpdate){
6471             this.fireEvent("beforerowsinserted", this, rowIndex);
6472         }
6473             //var s = this.getScrollState();
6474         var row = this.renderRow(this.cm, this.store, rowIndex);
6475         // insert before rowIndex..
6476         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6477         
6478         var _this = this;
6479                 
6480         if(row.cellObjects.length){
6481             Roo.each(row.cellObjects, function(r){
6482                 _this.renderCellObject(r);
6483             })
6484         }
6485             
6486         if(!isUpdate){
6487             this.fireEvent("rowsinserted", this, rowIndex);
6488             //this.syncRowHeights(firstRow, lastRow);
6489             //this.stripeRows(firstRow);
6490             //this.layout();
6491         }
6492         
6493     },
6494     
6495     
6496     getRowDom : function(rowIndex)
6497     {
6498         var rows = this.el.select('tbody > tr', true).elements;
6499         
6500         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6501         
6502     },
6503     // returns the object tree for a tr..
6504   
6505     
6506     renderRow : function(cm, ds, rowIndex) 
6507     {
6508         
6509         var d = ds.getAt(rowIndex);
6510         
6511         var row = {
6512             tag : 'tr',
6513             cn : []
6514         };
6515             
6516         var cellObjects = [];
6517         
6518         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6519             var config = cm.config[i];
6520             
6521             var renderer = cm.getRenderer(i);
6522             var value = '';
6523             var id = false;
6524             
6525             if(typeof(renderer) !== 'undefined'){
6526                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6527             }
6528             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6529             // and are rendered into the cells after the row is rendered - using the id for the element.
6530             
6531             if(typeof(value) === 'object'){
6532                 id = Roo.id();
6533                 cellObjects.push({
6534                     container : id,
6535                     cfg : value 
6536                 })
6537             }
6538             
6539             var rowcfg = {
6540                 record: d,
6541                 rowIndex : rowIndex,
6542                 colIndex : i,
6543                 rowClass : ''
6544             };
6545
6546             this.fireEvent('rowclass', this, rowcfg);
6547             
6548             var td = {
6549                 tag: 'td',
6550                 cls : rowcfg.rowClass,
6551                 style: '',
6552                 html: (typeof(value) === 'object') ? '' : value
6553             };
6554             
6555             if (id) {
6556                 td.id = id;
6557             }
6558             
6559             if(typeof(config.colspan) != 'undefined'){
6560                 td.colspan = config.colspan;
6561             }
6562             
6563             if(typeof(config.hidden) != 'undefined' && config.hidden){
6564                 td.style += ' display:none;';
6565             }
6566             
6567             if(typeof(config.align) != 'undefined' && config.align.length){
6568                 td.style += ' text-align:' + config.align + ';';
6569             }
6570             
6571             if(typeof(config.width) != 'undefined'){
6572                 td.style += ' width:' +  config.width + 'px;';
6573             }
6574             
6575             if(typeof(config.cursor) != 'undefined'){
6576                 td.style += ' cursor:' +  config.cursor + ';';
6577             }
6578             
6579             if(typeof(config.cls) != 'undefined'){
6580                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6581             }
6582             
6583             ['xs','sm','md','lg'].map(function(size){
6584                 
6585                 if(typeof(config[size]) == 'undefined'){
6586                     return;
6587                 }
6588                 
6589                 if (!config[size]) { // 0 = hidden
6590                     td.cls += ' hidden-' + size;
6591                     return;
6592                 }
6593                 
6594                 td.cls += ' col-' + size + '-' + config[size];
6595
6596             });
6597              
6598             row.cn.push(td);
6599            
6600         }
6601         
6602         row.cellObjects = cellObjects;
6603         
6604         return row;
6605           
6606     },
6607     
6608     
6609     
6610     onBeforeLoad : function()
6611     {
6612         //Roo.log('ds onBeforeLoad');
6613         
6614         //this.clear();
6615         
6616         //if(this.loadMask){
6617         //    this.maskEl.show();
6618         //}
6619     },
6620      /**
6621      * Remove all rows
6622      */
6623     clear : function()
6624     {
6625         this.el.select('tbody', true).first().dom.innerHTML = '';
6626     },
6627     /**
6628      * Show or hide a row.
6629      * @param {Number} rowIndex to show or hide
6630      * @param {Boolean} state hide
6631      */
6632     setRowVisibility : function(rowIndex, state)
6633     {
6634         var bt = this.mainBody.dom;
6635         
6636         var rows = this.el.select('tbody > tr', true).elements;
6637         
6638         if(typeof(rows[rowIndex]) == 'undefined'){
6639             return;
6640         }
6641         rows[rowIndex].dom.style.display = state ? '' : 'none';
6642     },
6643     
6644     
6645     getSelectionModel : function(){
6646         if(!this.selModel){
6647             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6648         }
6649         return this.selModel;
6650     },
6651     /*
6652      * Render the Roo.bootstrap object from renderder
6653      */
6654     renderCellObject : function(r)
6655     {
6656         var _this = this;
6657         
6658         var t = r.cfg.render(r.container);
6659         
6660         if(r.cfg.cn){
6661             Roo.each(r.cfg.cn, function(c){
6662                 var child = {
6663                     container: t.getChildContainer(),
6664                     cfg: c
6665                 };
6666                 _this.renderCellObject(child);
6667             })
6668         }
6669     },
6670     
6671     getRowIndex : function(row)
6672     {
6673         var rowIndex = -1;
6674         
6675         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6676             if(el != row){
6677                 return;
6678             }
6679             
6680             rowIndex = index;
6681         });
6682         
6683         return rowIndex;
6684     },
6685      /**
6686      * Returns the grid's underlying element = used by panel.Grid
6687      * @return {Element} The element
6688      */
6689     getGridEl : function(){
6690         return this.el;
6691     },
6692      /**
6693      * Forces a resize - used by panel.Grid
6694      * @return {Element} The element
6695      */
6696     autoSize : function(){
6697         //var ctr = Roo.get(this.container.dom.parentElement);
6698         var ctr = Roo.get(this.el.dom);
6699         
6700         var thd = this.getGridEl().select('thead',true).first();
6701         var tbd = this.getGridEl().select('tbody', true).first();
6702         
6703         
6704         var cw = ctr.getWidth();
6705         
6706         if (tbd) {
6707             
6708             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6709             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6710             cw -= barsize;
6711         }
6712         cw = Math.max(cw, this.totalWidth);
6713         this.getGridEl().select('tr',true).setWidth(cw);
6714         
6715         return; // we doe not have a view in this design..
6716         
6717     },
6718     onBodyScroll: function()
6719     {
6720         
6721         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6722         this.mainHead.setStyle({
6723                     'position' : 'relative',
6724                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6725         });
6726         
6727         
6728     }
6729 });
6730
6731  
6732
6733  /*
6734  * - LGPL
6735  *
6736  * table cell
6737  * 
6738  */
6739
6740 /**
6741  * @class Roo.bootstrap.TableCell
6742  * @extends Roo.bootstrap.Component
6743  * Bootstrap TableCell class
6744  * @cfg {String} html cell contain text
6745  * @cfg {String} cls cell class
6746  * @cfg {String} tag cell tag (td|th) default td
6747  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6748  * @cfg {String} align Aligns the content in a cell
6749  * @cfg {String} axis Categorizes cells
6750  * @cfg {String} bgcolor Specifies the background color of a cell
6751  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6752  * @cfg {Number} colspan Specifies the number of columns a cell should span
6753  * @cfg {String} headers Specifies one or more header cells a cell is related to
6754  * @cfg {Number} height Sets the height of a cell
6755  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6756  * @cfg {Number} rowspan Sets the number of rows a cell should span
6757  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6758  * @cfg {String} valign Vertical aligns the content in a cell
6759  * @cfg {Number} width Specifies the width of a cell
6760  * 
6761  * @constructor
6762  * Create a new TableCell
6763  * @param {Object} config The config object
6764  */
6765
6766 Roo.bootstrap.TableCell = function(config){
6767     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6768 };
6769
6770 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6771     
6772     html: false,
6773     cls: false,
6774     tag: false,
6775     abbr: false,
6776     align: false,
6777     axis: false,
6778     bgcolor: false,
6779     charoff: false,
6780     colspan: false,
6781     headers: false,
6782     height: false,
6783     nowrap: false,
6784     rowspan: false,
6785     scope: false,
6786     valign: false,
6787     width: false,
6788     
6789     
6790     getAutoCreate : function(){
6791         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6792         
6793         cfg = {
6794             tag: 'td'
6795         };
6796         
6797         if(this.tag){
6798             cfg.tag = this.tag;
6799         }
6800         
6801         if (this.html) {
6802             cfg.html=this.html
6803         }
6804         if (this.cls) {
6805             cfg.cls=this.cls
6806         }
6807         if (this.abbr) {
6808             cfg.abbr=this.abbr
6809         }
6810         if (this.align) {
6811             cfg.align=this.align
6812         }
6813         if (this.axis) {
6814             cfg.axis=this.axis
6815         }
6816         if (this.bgcolor) {
6817             cfg.bgcolor=this.bgcolor
6818         }
6819         if (this.charoff) {
6820             cfg.charoff=this.charoff
6821         }
6822         if (this.colspan) {
6823             cfg.colspan=this.colspan
6824         }
6825         if (this.headers) {
6826             cfg.headers=this.headers
6827         }
6828         if (this.height) {
6829             cfg.height=this.height
6830         }
6831         if (this.nowrap) {
6832             cfg.nowrap=this.nowrap
6833         }
6834         if (this.rowspan) {
6835             cfg.rowspan=this.rowspan
6836         }
6837         if (this.scope) {
6838             cfg.scope=this.scope
6839         }
6840         if (this.valign) {
6841             cfg.valign=this.valign
6842         }
6843         if (this.width) {
6844             cfg.width=this.width
6845         }
6846         
6847         
6848         return cfg;
6849     }
6850    
6851 });
6852
6853  
6854
6855  /*
6856  * - LGPL
6857  *
6858  * table row
6859  * 
6860  */
6861
6862 /**
6863  * @class Roo.bootstrap.TableRow
6864  * @extends Roo.bootstrap.Component
6865  * Bootstrap TableRow class
6866  * @cfg {String} cls row class
6867  * @cfg {String} align Aligns the content in a table row
6868  * @cfg {String} bgcolor Specifies a background color for a table row
6869  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6870  * @cfg {String} valign Vertical aligns the content in a table row
6871  * 
6872  * @constructor
6873  * Create a new TableRow
6874  * @param {Object} config The config object
6875  */
6876
6877 Roo.bootstrap.TableRow = function(config){
6878     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6879 };
6880
6881 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6882     
6883     cls: false,
6884     align: false,
6885     bgcolor: false,
6886     charoff: false,
6887     valign: false,
6888     
6889     getAutoCreate : function(){
6890         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6891         
6892         cfg = {
6893             tag: 'tr'
6894         };
6895             
6896         if(this.cls){
6897             cfg.cls = this.cls;
6898         }
6899         if(this.align){
6900             cfg.align = this.align;
6901         }
6902         if(this.bgcolor){
6903             cfg.bgcolor = this.bgcolor;
6904         }
6905         if(this.charoff){
6906             cfg.charoff = this.charoff;
6907         }
6908         if(this.valign){
6909             cfg.valign = this.valign;
6910         }
6911         
6912         return cfg;
6913     }
6914    
6915 });
6916
6917  
6918
6919  /*
6920  * - LGPL
6921  *
6922  * table body
6923  * 
6924  */
6925
6926 /**
6927  * @class Roo.bootstrap.TableBody
6928  * @extends Roo.bootstrap.Component
6929  * Bootstrap TableBody class
6930  * @cfg {String} cls element class
6931  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6932  * @cfg {String} align Aligns the content inside the element
6933  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6934  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6935  * 
6936  * @constructor
6937  * Create a new TableBody
6938  * @param {Object} config The config object
6939  */
6940
6941 Roo.bootstrap.TableBody = function(config){
6942     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6943 };
6944
6945 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6946     
6947     cls: false,
6948     tag: false,
6949     align: false,
6950     charoff: false,
6951     valign: false,
6952     
6953     getAutoCreate : function(){
6954         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6955         
6956         cfg = {
6957             tag: 'tbody'
6958         };
6959             
6960         if (this.cls) {
6961             cfg.cls=this.cls
6962         }
6963         if(this.tag){
6964             cfg.tag = this.tag;
6965         }
6966         
6967         if(this.align){
6968             cfg.align = this.align;
6969         }
6970         if(this.charoff){
6971             cfg.charoff = this.charoff;
6972         }
6973         if(this.valign){
6974             cfg.valign = this.valign;
6975         }
6976         
6977         return cfg;
6978     }
6979     
6980     
6981 //    initEvents : function()
6982 //    {
6983 //        
6984 //        if(!this.store){
6985 //            return;
6986 //        }
6987 //        
6988 //        this.store = Roo.factory(this.store, Roo.data);
6989 //        this.store.on('load', this.onLoad, this);
6990 //        
6991 //        this.store.load();
6992 //        
6993 //    },
6994 //    
6995 //    onLoad: function () 
6996 //    {   
6997 //        this.fireEvent('load', this);
6998 //    }
6999 //    
7000 //   
7001 });
7002
7003  
7004
7005  /*
7006  * Based on:
7007  * Ext JS Library 1.1.1
7008  * Copyright(c) 2006-2007, Ext JS, LLC.
7009  *
7010  * Originally Released Under LGPL - original licence link has changed is not relivant.
7011  *
7012  * Fork - LGPL
7013  * <script type="text/javascript">
7014  */
7015
7016 // as we use this in bootstrap.
7017 Roo.namespace('Roo.form');
7018  /**
7019  * @class Roo.form.Action
7020  * Internal Class used to handle form actions
7021  * @constructor
7022  * @param {Roo.form.BasicForm} el The form element or its id
7023  * @param {Object} config Configuration options
7024  */
7025
7026  
7027  
7028 // define the action interface
7029 Roo.form.Action = function(form, options){
7030     this.form = form;
7031     this.options = options || {};
7032 };
7033 /**
7034  * Client Validation Failed
7035  * @const 
7036  */
7037 Roo.form.Action.CLIENT_INVALID = 'client';
7038 /**
7039  * Server Validation Failed
7040  * @const 
7041  */
7042 Roo.form.Action.SERVER_INVALID = 'server';
7043  /**
7044  * Connect to Server Failed
7045  * @const 
7046  */
7047 Roo.form.Action.CONNECT_FAILURE = 'connect';
7048 /**
7049  * Reading Data from Server Failed
7050  * @const 
7051  */
7052 Roo.form.Action.LOAD_FAILURE = 'load';
7053
7054 Roo.form.Action.prototype = {
7055     type : 'default',
7056     failureType : undefined,
7057     response : undefined,
7058     result : undefined,
7059
7060     // interface method
7061     run : function(options){
7062
7063     },
7064
7065     // interface method
7066     success : function(response){
7067
7068     },
7069
7070     // interface method
7071     handleResponse : function(response){
7072
7073     },
7074
7075     // default connection failure
7076     failure : function(response){
7077         
7078         this.response = response;
7079         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7080         this.form.afterAction(this, false);
7081     },
7082
7083     processResponse : function(response){
7084         this.response = response;
7085         if(!response.responseText){
7086             return true;
7087         }
7088         this.result = this.handleResponse(response);
7089         return this.result;
7090     },
7091
7092     // utility functions used internally
7093     getUrl : function(appendParams){
7094         var url = this.options.url || this.form.url || this.form.el.dom.action;
7095         if(appendParams){
7096             var p = this.getParams();
7097             if(p){
7098                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7099             }
7100         }
7101         return url;
7102     },
7103
7104     getMethod : function(){
7105         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7106     },
7107
7108     getParams : function(){
7109         var bp = this.form.baseParams;
7110         var p = this.options.params;
7111         if(p){
7112             if(typeof p == "object"){
7113                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7114             }else if(typeof p == 'string' && bp){
7115                 p += '&' + Roo.urlEncode(bp);
7116             }
7117         }else if(bp){
7118             p = Roo.urlEncode(bp);
7119         }
7120         return p;
7121     },
7122
7123     createCallback : function(){
7124         return {
7125             success: this.success,
7126             failure: this.failure,
7127             scope: this,
7128             timeout: (this.form.timeout*1000),
7129             upload: this.form.fileUpload ? this.success : undefined
7130         };
7131     }
7132 };
7133
7134 Roo.form.Action.Submit = function(form, options){
7135     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7136 };
7137
7138 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7139     type : 'submit',
7140
7141     haveProgress : false,
7142     uploadComplete : false,
7143     
7144     // uploadProgress indicator.
7145     uploadProgress : function()
7146     {
7147         if (!this.form.progressUrl) {
7148             return;
7149         }
7150         
7151         if (!this.haveProgress) {
7152             Roo.MessageBox.progress("Uploading", "Uploading");
7153         }
7154         if (this.uploadComplete) {
7155            Roo.MessageBox.hide();
7156            return;
7157         }
7158         
7159         this.haveProgress = true;
7160    
7161         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7162         
7163         var c = new Roo.data.Connection();
7164         c.request({
7165             url : this.form.progressUrl,
7166             params: {
7167                 id : uid
7168             },
7169             method: 'GET',
7170             success : function(req){
7171                //console.log(data);
7172                 var rdata = false;
7173                 var edata;
7174                 try  {
7175                    rdata = Roo.decode(req.responseText)
7176                 } catch (e) {
7177                     Roo.log("Invalid data from server..");
7178                     Roo.log(edata);
7179                     return;
7180                 }
7181                 if (!rdata || !rdata.success) {
7182                     Roo.log(rdata);
7183                     Roo.MessageBox.alert(Roo.encode(rdata));
7184                     return;
7185                 }
7186                 var data = rdata.data;
7187                 
7188                 if (this.uploadComplete) {
7189                    Roo.MessageBox.hide();
7190                    return;
7191                 }
7192                    
7193                 if (data){
7194                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7195                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7196                     );
7197                 }
7198                 this.uploadProgress.defer(2000,this);
7199             },
7200        
7201             failure: function(data) {
7202                 Roo.log('progress url failed ');
7203                 Roo.log(data);
7204             },
7205             scope : this
7206         });
7207            
7208     },
7209     
7210     
7211     run : function()
7212     {
7213         // run get Values on the form, so it syncs any secondary forms.
7214         this.form.getValues();
7215         
7216         var o = this.options;
7217         var method = this.getMethod();
7218         var isPost = method == 'POST';
7219         if(o.clientValidation === false || this.form.isValid()){
7220             
7221             if (this.form.progressUrl) {
7222                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7223                     (new Date() * 1) + '' + Math.random());
7224                     
7225             } 
7226             
7227             
7228             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7229                 form:this.form.el.dom,
7230                 url:this.getUrl(!isPost),
7231                 method: method,
7232                 params:isPost ? this.getParams() : null,
7233                 isUpload: this.form.fileUpload
7234             }));
7235             
7236             this.uploadProgress();
7237
7238         }else if (o.clientValidation !== false){ // client validation failed
7239             this.failureType = Roo.form.Action.CLIENT_INVALID;
7240             this.form.afterAction(this, false);
7241         }
7242     },
7243
7244     success : function(response)
7245     {
7246         this.uploadComplete= true;
7247         if (this.haveProgress) {
7248             Roo.MessageBox.hide();
7249         }
7250         
7251         
7252         var result = this.processResponse(response);
7253         if(result === true || result.success){
7254             this.form.afterAction(this, true);
7255             return;
7256         }
7257         if(result.errors){
7258             this.form.markInvalid(result.errors);
7259             this.failureType = Roo.form.Action.SERVER_INVALID;
7260         }
7261         this.form.afterAction(this, false);
7262     },
7263     failure : function(response)
7264     {
7265         this.uploadComplete= true;
7266         if (this.haveProgress) {
7267             Roo.MessageBox.hide();
7268         }
7269         
7270         this.response = response;
7271         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7272         this.form.afterAction(this, false);
7273     },
7274     
7275     handleResponse : function(response){
7276         if(this.form.errorReader){
7277             var rs = this.form.errorReader.read(response);
7278             var errors = [];
7279             if(rs.records){
7280                 for(var i = 0, len = rs.records.length; i < len; i++) {
7281                     var r = rs.records[i];
7282                     errors[i] = r.data;
7283                 }
7284             }
7285             if(errors.length < 1){
7286                 errors = null;
7287             }
7288             return {
7289                 success : rs.success,
7290                 errors : errors
7291             };
7292         }
7293         var ret = false;
7294         try {
7295             ret = Roo.decode(response.responseText);
7296         } catch (e) {
7297             ret = {
7298                 success: false,
7299                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7300                 errors : []
7301             };
7302         }
7303         return ret;
7304         
7305     }
7306 });
7307
7308
7309 Roo.form.Action.Load = function(form, options){
7310     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7311     this.reader = this.form.reader;
7312 };
7313
7314 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7315     type : 'load',
7316
7317     run : function(){
7318         
7319         Roo.Ajax.request(Roo.apply(
7320                 this.createCallback(), {
7321                     method:this.getMethod(),
7322                     url:this.getUrl(false),
7323                     params:this.getParams()
7324         }));
7325     },
7326
7327     success : function(response){
7328         
7329         var result = this.processResponse(response);
7330         if(result === true || !result.success || !result.data){
7331             this.failureType = Roo.form.Action.LOAD_FAILURE;
7332             this.form.afterAction(this, false);
7333             return;
7334         }
7335         this.form.clearInvalid();
7336         this.form.setValues(result.data);
7337         this.form.afterAction(this, true);
7338     },
7339
7340     handleResponse : function(response){
7341         if(this.form.reader){
7342             var rs = this.form.reader.read(response);
7343             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7344             return {
7345                 success : rs.success,
7346                 data : data
7347             };
7348         }
7349         return Roo.decode(response.responseText);
7350     }
7351 });
7352
7353 Roo.form.Action.ACTION_TYPES = {
7354     'load' : Roo.form.Action.Load,
7355     'submit' : Roo.form.Action.Submit
7356 };/*
7357  * - LGPL
7358  *
7359  * form
7360  * 
7361  */
7362
7363 /**
7364  * @class Roo.bootstrap.Form
7365  * @extends Roo.bootstrap.Component
7366  * Bootstrap Form class
7367  * @cfg {String} method  GET | POST (default POST)
7368  * @cfg {String} labelAlign top | left (default top)
7369  * @cfg {String} align left  | right - for navbars
7370  * @cfg {Boolean} loadMask load mask when submit (default true)
7371
7372  * 
7373  * @constructor
7374  * Create a new Form
7375  * @param {Object} config The config object
7376  */
7377
7378
7379 Roo.bootstrap.Form = function(config){
7380     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7381     this.addEvents({
7382         /**
7383          * @event clientvalidation
7384          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7385          * @param {Form} this
7386          * @param {Boolean} valid true if the form has passed client-side validation
7387          */
7388         clientvalidation: true,
7389         /**
7390          * @event beforeaction
7391          * Fires before any action is performed. Return false to cancel the action.
7392          * @param {Form} this
7393          * @param {Action} action The action to be performed
7394          */
7395         beforeaction: true,
7396         /**
7397          * @event actionfailed
7398          * Fires when an action fails.
7399          * @param {Form} this
7400          * @param {Action} action The action that failed
7401          */
7402         actionfailed : true,
7403         /**
7404          * @event actioncomplete
7405          * Fires when an action is completed.
7406          * @param {Form} this
7407          * @param {Action} action The action that completed
7408          */
7409         actioncomplete : true
7410     });
7411     
7412 };
7413
7414 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7415       
7416      /**
7417      * @cfg {String} method
7418      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7419      */
7420     method : 'POST',
7421     /**
7422      * @cfg {String} url
7423      * The URL to use for form actions if one isn't supplied in the action options.
7424      */
7425     /**
7426      * @cfg {Boolean} fileUpload
7427      * Set to true if this form is a file upload.
7428      */
7429      
7430     /**
7431      * @cfg {Object} baseParams
7432      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7433      */
7434       
7435     /**
7436      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7437      */
7438     timeout: 30,
7439     /**
7440      * @cfg {Sting} align (left|right) for navbar forms
7441      */
7442     align : 'left',
7443
7444     // private
7445     activeAction : null,
7446  
7447     /**
7448      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7449      * element by passing it or its id or mask the form itself by passing in true.
7450      * @type Mixed
7451      */
7452     waitMsgTarget : false,
7453     
7454     loadMask : true,
7455     
7456     getAutoCreate : function(){
7457         
7458         var cfg = {
7459             tag: 'form',
7460             method : this.method || 'POST',
7461             id : this.id || Roo.id(),
7462             cls : ''
7463         };
7464         if (this.parent().xtype.match(/^Nav/)) {
7465             cfg.cls = 'navbar-form navbar-' + this.align;
7466             
7467         }
7468         
7469         if (this.labelAlign == 'left' ) {
7470             cfg.cls += ' form-horizontal';
7471         }
7472         
7473         
7474         return cfg;
7475     },
7476     initEvents : function()
7477     {
7478         this.el.on('submit', this.onSubmit, this);
7479         // this was added as random key presses on the form where triggering form submit.
7480         this.el.on('keypress', function(e) {
7481             if (e.getCharCode() != 13) {
7482                 return true;
7483             }
7484             // we might need to allow it for textareas.. and some other items.
7485             // check e.getTarget().
7486             
7487             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7488                 return true;
7489             }
7490         
7491             Roo.log("keypress blocked");
7492             
7493             e.preventDefault();
7494             return false;
7495         });
7496         
7497     },
7498     // private
7499     onSubmit : function(e){
7500         e.stopEvent();
7501     },
7502     
7503      /**
7504      * Returns true if client-side validation on the form is successful.
7505      * @return Boolean
7506      */
7507     isValid : function(){
7508         var items = this.getItems();
7509         var valid = true;
7510         items.each(function(f){
7511            if(!f.validate()){
7512                valid = false;
7513                
7514            }
7515         });
7516         return valid;
7517     },
7518     /**
7519      * Returns true if any fields in this form have changed since their original load.
7520      * @return Boolean
7521      */
7522     isDirty : function(){
7523         var dirty = false;
7524         var items = this.getItems();
7525         items.each(function(f){
7526            if(f.isDirty()){
7527                dirty = true;
7528                return false;
7529            }
7530            return true;
7531         });
7532         return dirty;
7533     },
7534      /**
7535      * Performs a predefined action (submit or load) or custom actions you define on this form.
7536      * @param {String} actionName The name of the action type
7537      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7538      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7539      * accept other config options):
7540      * <pre>
7541 Property          Type             Description
7542 ----------------  ---------------  ----------------------------------------------------------------------------------
7543 url               String           The url for the action (defaults to the form's url)
7544 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7545 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7546 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7547                                    validate the form on the client (defaults to false)
7548      * </pre>
7549      * @return {BasicForm} this
7550      */
7551     doAction : function(action, options){
7552         if(typeof action == 'string'){
7553             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7554         }
7555         if(this.fireEvent('beforeaction', this, action) !== false){
7556             this.beforeAction(action);
7557             action.run.defer(100, action);
7558         }
7559         return this;
7560     },
7561     
7562     // private
7563     beforeAction : function(action){
7564         var o = action.options;
7565         
7566         if(this.loadMask){
7567             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7568         }
7569         // not really supported yet.. ??
7570         
7571         //if(this.waitMsgTarget === true){
7572         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7573         //}else if(this.waitMsgTarget){
7574         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7575         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7576         //}else {
7577         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7578        // }
7579          
7580     },
7581
7582     // private
7583     afterAction : function(action, success){
7584         this.activeAction = null;
7585         var o = action.options;
7586         
7587         //if(this.waitMsgTarget === true){
7588             this.el.unmask();
7589         //}else if(this.waitMsgTarget){
7590         //    this.waitMsgTarget.unmask();
7591         //}else{
7592         //    Roo.MessageBox.updateProgress(1);
7593         //    Roo.MessageBox.hide();
7594        // }
7595         // 
7596         if(success){
7597             if(o.reset){
7598                 this.reset();
7599             }
7600             Roo.callback(o.success, o.scope, [this, action]);
7601             this.fireEvent('actioncomplete', this, action);
7602             
7603         }else{
7604             
7605             // failure condition..
7606             // we have a scenario where updates need confirming.
7607             // eg. if a locking scenario exists..
7608             // we look for { errors : { needs_confirm : true }} in the response.
7609             if (
7610                 (typeof(action.result) != 'undefined')  &&
7611                 (typeof(action.result.errors) != 'undefined')  &&
7612                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7613            ){
7614                 var _t = this;
7615                 Roo.log("not supported yet");
7616                  /*
7617                 
7618                 Roo.MessageBox.confirm(
7619                     "Change requires confirmation",
7620                     action.result.errorMsg,
7621                     function(r) {
7622                         if (r != 'yes') {
7623                             return;
7624                         }
7625                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7626                     }
7627                     
7628                 );
7629                 */
7630                 
7631                 
7632                 return;
7633             }
7634             
7635             Roo.callback(o.failure, o.scope, [this, action]);
7636             // show an error message if no failed handler is set..
7637             if (!this.hasListener('actionfailed')) {
7638                 Roo.log("need to add dialog support");
7639                 /*
7640                 Roo.MessageBox.alert("Error",
7641                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7642                         action.result.errorMsg :
7643                         "Saving Failed, please check your entries or try again"
7644                 );
7645                 */
7646             }
7647             
7648             this.fireEvent('actionfailed', this, action);
7649         }
7650         
7651     },
7652     /**
7653      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7654      * @param {String} id The value to search for
7655      * @return Field
7656      */
7657     findField : function(id){
7658         var items = this.getItems();
7659         var field = items.get(id);
7660         if(!field){
7661              items.each(function(f){
7662                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7663                     field = f;
7664                     return false;
7665                 }
7666                 return true;
7667             });
7668         }
7669         return field || null;
7670     },
7671      /**
7672      * Mark fields in this form invalid in bulk.
7673      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7674      * @return {BasicForm} this
7675      */
7676     markInvalid : function(errors){
7677         if(errors instanceof Array){
7678             for(var i = 0, len = errors.length; i < len; i++){
7679                 var fieldError = errors[i];
7680                 var f = this.findField(fieldError.id);
7681                 if(f){
7682                     f.markInvalid(fieldError.msg);
7683                 }
7684             }
7685         }else{
7686             var field, id;
7687             for(id in errors){
7688                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7689                     field.markInvalid(errors[id]);
7690                 }
7691             }
7692         }
7693         //Roo.each(this.childForms || [], function (f) {
7694         //    f.markInvalid(errors);
7695         //});
7696         
7697         return this;
7698     },
7699
7700     /**
7701      * Set values for fields in this form in bulk.
7702      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7703      * @return {BasicForm} this
7704      */
7705     setValues : function(values){
7706         if(values instanceof Array){ // array of objects
7707             for(var i = 0, len = values.length; i < len; i++){
7708                 var v = values[i];
7709                 var f = this.findField(v.id);
7710                 if(f){
7711                     f.setValue(v.value);
7712                     if(this.trackResetOnLoad){
7713                         f.originalValue = f.getValue();
7714                     }
7715                 }
7716             }
7717         }else{ // object hash
7718             var field, id;
7719             for(id in values){
7720                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7721                     
7722                     if (field.setFromData && 
7723                         field.valueField && 
7724                         field.displayField &&
7725                         // combos' with local stores can 
7726                         // be queried via setValue()
7727                         // to set their value..
7728                         (field.store && !field.store.isLocal)
7729                         ) {
7730                         // it's a combo
7731                         var sd = { };
7732                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7733                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7734                         field.setFromData(sd);
7735                         
7736                     } else {
7737                         field.setValue(values[id]);
7738                     }
7739                     
7740                     
7741                     if(this.trackResetOnLoad){
7742                         field.originalValue = field.getValue();
7743                     }
7744                 }
7745             }
7746         }
7747          
7748         //Roo.each(this.childForms || [], function (f) {
7749         //    f.setValues(values);
7750         //});
7751                 
7752         return this;
7753     },
7754
7755     /**
7756      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7757      * they are returned as an array.
7758      * @param {Boolean} asString
7759      * @return {Object}
7760      */
7761     getValues : function(asString){
7762         //if (this.childForms) {
7763             // copy values from the child forms
7764         //    Roo.each(this.childForms, function (f) {
7765         //        this.setValues(f.getValues());
7766         //    }, this);
7767         //}
7768         
7769         
7770         
7771         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7772         if(asString === true){
7773             return fs;
7774         }
7775         return Roo.urlDecode(fs);
7776     },
7777     
7778     /**
7779      * Returns the fields in this form as an object with key/value pairs. 
7780      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7781      * @return {Object}
7782      */
7783     getFieldValues : function(with_hidden)
7784     {
7785         var items = this.getItems();
7786         var ret = {};
7787         items.each(function(f){
7788             if (!f.getName()) {
7789                 return;
7790             }
7791             var v = f.getValue();
7792             if (f.inputType =='radio') {
7793                 if (typeof(ret[f.getName()]) == 'undefined') {
7794                     ret[f.getName()] = ''; // empty..
7795                 }
7796                 
7797                 if (!f.el.dom.checked) {
7798                     return;
7799                     
7800                 }
7801                 v = f.el.dom.value;
7802                 
7803             }
7804             
7805             // not sure if this supported any more..
7806             if ((typeof(v) == 'object') && f.getRawValue) {
7807                 v = f.getRawValue() ; // dates..
7808             }
7809             // combo boxes where name != hiddenName...
7810             if (f.name != f.getName()) {
7811                 ret[f.name] = f.getRawValue();
7812             }
7813             ret[f.getName()] = v;
7814         });
7815         
7816         return ret;
7817     },
7818
7819     /**
7820      * Clears all invalid messages in this form.
7821      * @return {BasicForm} this
7822      */
7823     clearInvalid : function(){
7824         var items = this.getItems();
7825         
7826         items.each(function(f){
7827            f.clearInvalid();
7828         });
7829         
7830         
7831         
7832         return this;
7833     },
7834
7835     /**
7836      * Resets this form.
7837      * @return {BasicForm} this
7838      */
7839     reset : function(){
7840         var items = this.getItems();
7841         items.each(function(f){
7842             f.reset();
7843         });
7844         
7845         Roo.each(this.childForms || [], function (f) {
7846             f.reset();
7847         });
7848        
7849         
7850         return this;
7851     },
7852     getItems : function()
7853     {
7854         var r=new Roo.util.MixedCollection(false, function(o){
7855             return o.id || (o.id = Roo.id());
7856         });
7857         var iter = function(el) {
7858             if (el.inputEl) {
7859                 r.add(el);
7860             }
7861             if (!el.items) {
7862                 return;
7863             }
7864             Roo.each(el.items,function(e) {
7865                 iter(e);
7866             });
7867             
7868             
7869         };
7870         
7871         iter(this);
7872         return r;
7873         
7874         
7875         
7876         
7877     }
7878     
7879 });
7880
7881  
7882 /*
7883  * Based on:
7884  * Ext JS Library 1.1.1
7885  * Copyright(c) 2006-2007, Ext JS, LLC.
7886  *
7887  * Originally Released Under LGPL - original licence link has changed is not relivant.
7888  *
7889  * Fork - LGPL
7890  * <script type="text/javascript">
7891  */
7892 /**
7893  * @class Roo.form.VTypes
7894  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7895  * @singleton
7896  */
7897 Roo.form.VTypes = function(){
7898     // closure these in so they are only created once.
7899     var alpha = /^[a-zA-Z_]+$/;
7900     var alphanum = /^[a-zA-Z0-9_]+$/;
7901     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7902     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7903
7904     // All these messages and functions are configurable
7905     return {
7906         /**
7907          * The function used to validate email addresses
7908          * @param {String} value The email address
7909          */
7910         'email' : function(v){
7911             return email.test(v);
7912         },
7913         /**
7914          * The error text to display when the email validation function returns false
7915          * @type String
7916          */
7917         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7918         /**
7919          * The keystroke filter mask to be applied on email input
7920          * @type RegExp
7921          */
7922         'emailMask' : /[a-z0-9_\.\-@]/i,
7923
7924         /**
7925          * The function used to validate URLs
7926          * @param {String} value The URL
7927          */
7928         'url' : function(v){
7929             return url.test(v);
7930         },
7931         /**
7932          * The error text to display when the url validation function returns false
7933          * @type String
7934          */
7935         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7936         
7937         /**
7938          * The function used to validate alpha values
7939          * @param {String} value The value
7940          */
7941         'alpha' : function(v){
7942             return alpha.test(v);
7943         },
7944         /**
7945          * The error text to display when the alpha validation function returns false
7946          * @type String
7947          */
7948         'alphaText' : 'This field should only contain letters and _',
7949         /**
7950          * The keystroke filter mask to be applied on alpha input
7951          * @type RegExp
7952          */
7953         'alphaMask' : /[a-z_]/i,
7954
7955         /**
7956          * The function used to validate alphanumeric values
7957          * @param {String} value The value
7958          */
7959         'alphanum' : function(v){
7960             return alphanum.test(v);
7961         },
7962         /**
7963          * The error text to display when the alphanumeric validation function returns false
7964          * @type String
7965          */
7966         'alphanumText' : 'This field should only contain letters, numbers and _',
7967         /**
7968          * The keystroke filter mask to be applied on alphanumeric input
7969          * @type RegExp
7970          */
7971         'alphanumMask' : /[a-z0-9_]/i
7972     };
7973 }();/*
7974  * - LGPL
7975  *
7976  * Input
7977  * 
7978  */
7979
7980 /**
7981  * @class Roo.bootstrap.Input
7982  * @extends Roo.bootstrap.Component
7983  * Bootstrap Input class
7984  * @cfg {Boolean} disabled is it disabled
7985  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7986  * @cfg {String} name name of the input
7987  * @cfg {string} fieldLabel - the label associated
7988  * @cfg {string} placeholder - placeholder to put in text.
7989  * @cfg {string}  before - input group add on before
7990  * @cfg {string} after - input group add on after
7991  * @cfg {string} size - (lg|sm) or leave empty..
7992  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7993  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7994  * @cfg {Number} md colspan out of 12 for computer-sized screens
7995  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7996  * @cfg {string} value default value of the input
7997  * @cfg {Number} labelWidth set the width of label (0-12)
7998  * @cfg {String} labelAlign (top|left)
7999  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8000  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8001
8002  * @cfg {String} align (left|center|right) Default left
8003  * @cfg {Boolean} forceFeedback (true|false) Default false
8004  * 
8005  * 
8006  * 
8007  * 
8008  * @constructor
8009  * Create a new Input
8010  * @param {Object} config The config object
8011  */
8012
8013 Roo.bootstrap.Input = function(config){
8014     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8015    
8016         this.addEvents({
8017             /**
8018              * @event focus
8019              * Fires when this field receives input focus.
8020              * @param {Roo.form.Field} this
8021              */
8022             focus : true,
8023             /**
8024              * @event blur
8025              * Fires when this field loses input focus.
8026              * @param {Roo.form.Field} this
8027              */
8028             blur : true,
8029             /**
8030              * @event specialkey
8031              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8032              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8033              * @param {Roo.form.Field} this
8034              * @param {Roo.EventObject} e The event object
8035              */
8036             specialkey : true,
8037             /**
8038              * @event change
8039              * Fires just before the field blurs if the field value has changed.
8040              * @param {Roo.form.Field} this
8041              * @param {Mixed} newValue The new value
8042              * @param {Mixed} oldValue The original value
8043              */
8044             change : true,
8045             /**
8046              * @event invalid
8047              * Fires after the field has been marked as invalid.
8048              * @param {Roo.form.Field} this
8049              * @param {String} msg The validation message
8050              */
8051             invalid : true,
8052             /**
8053              * @event valid
8054              * Fires after the field has been validated with no errors.
8055              * @param {Roo.form.Field} this
8056              */
8057             valid : true,
8058              /**
8059              * @event keyup
8060              * Fires after the key up
8061              * @param {Roo.form.Field} this
8062              * @param {Roo.EventObject}  e The event Object
8063              */
8064             keyup : true
8065         });
8066 };
8067
8068 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8069      /**
8070      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8071       automatic validation (defaults to "keyup").
8072      */
8073     validationEvent : "keyup",
8074      /**
8075      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8076      */
8077     validateOnBlur : true,
8078     /**
8079      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8080      */
8081     validationDelay : 250,
8082      /**
8083      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8084      */
8085     focusClass : "x-form-focus",  // not needed???
8086     
8087        
8088     /**
8089      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8090      */
8091     invalidClass : "has-warning",
8092     
8093     /**
8094      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8095      */
8096     validClass : "has-success",
8097     
8098     /**
8099      * @cfg {Boolean} hasFeedback (true|false) default true
8100      */
8101     hasFeedback : true,
8102     
8103     /**
8104      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8105      */
8106     invalidFeedbackClass : "glyphicon-warning-sign",
8107     
8108     /**
8109      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8110      */
8111     validFeedbackClass : "glyphicon-ok",
8112     
8113     /**
8114      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8115      */
8116     selectOnFocus : false,
8117     
8118      /**
8119      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8120      */
8121     maskRe : null,
8122        /**
8123      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8124      */
8125     vtype : null,
8126     
8127       /**
8128      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8129      */
8130     disableKeyFilter : false,
8131     
8132        /**
8133      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8134      */
8135     disabled : false,
8136      /**
8137      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8138      */
8139     allowBlank : true,
8140     /**
8141      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8142      */
8143     blankText : "This field is required",
8144     
8145      /**
8146      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8147      */
8148     minLength : 0,
8149     /**
8150      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8151      */
8152     maxLength : Number.MAX_VALUE,
8153     /**
8154      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8155      */
8156     minLengthText : "The minimum length for this field is {0}",
8157     /**
8158      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8159      */
8160     maxLengthText : "The maximum length for this field is {0}",
8161   
8162     
8163     /**
8164      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8165      * If available, this function will be called only after the basic validators all return true, and will be passed the
8166      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8167      */
8168     validator : null,
8169     /**
8170      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8171      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8172      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8173      */
8174     regex : null,
8175     /**
8176      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8177      */
8178     regexText : "",
8179     
8180     autocomplete: false,
8181     
8182     
8183     fieldLabel : '',
8184     inputType : 'text',
8185     
8186     name : false,
8187     placeholder: false,
8188     before : false,
8189     after : false,
8190     size : false,
8191     hasFocus : false,
8192     preventMark: false,
8193     isFormField : true,
8194     value : '',
8195     labelWidth : 2,
8196     labelAlign : false,
8197     readOnly : false,
8198     align : false,
8199     formatedValue : false,
8200     forceFeedback : false,
8201     
8202     parentLabelAlign : function()
8203     {
8204         var parent = this;
8205         while (parent.parent()) {
8206             parent = parent.parent();
8207             if (typeof(parent.labelAlign) !='undefined') {
8208                 return parent.labelAlign;
8209             }
8210         }
8211         return 'left';
8212         
8213     },
8214     
8215     getAutoCreate : function(){
8216         
8217         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8218         
8219         var id = Roo.id();
8220         
8221         var cfg = {};
8222         
8223        
8224         
8225         if(this.inputType != 'hidden'){
8226             cfg.cls = 'form-group' //input-group
8227         }
8228         
8229         var input =  {
8230             tag: 'input',
8231             id : id,
8232             type : this.inputType,
8233             value : this.value,
8234             cls : 'form-control',
8235             placeholder : this.placeholder || '',
8236             autocomplete : this.autocomplete || 'new-password'
8237         };
8238         
8239         
8240         if(this.align){
8241             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8242         }
8243         
8244         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8245             input.maxLength = this.maxLength;
8246         }
8247         
8248         if (this.disabled) {
8249             input.disabled=true;
8250         }
8251         
8252         if (this.readOnly) {
8253             input.readonly=true;
8254         }
8255         
8256         if (this.name) {
8257             input.name = this.name;
8258         }
8259         if (this.size) {
8260             input.cls += ' input-' + this.size;
8261         }
8262         var settings=this;
8263         ['xs','sm','md','lg'].map(function(size){
8264             if (settings[size]) {
8265                 cfg.cls += ' col-' + size + '-' + settings[size];
8266             }
8267         });
8268         
8269         var inputblock = input;
8270         
8271         var feedback = {
8272             tag: 'span',
8273             cls: 'glyphicon form-control-feedback'
8274         };
8275             
8276         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8277             
8278             inputblock = {
8279                 cls : 'has-feedback',
8280                 cn :  [
8281                     input,
8282                     feedback
8283                 ] 
8284             };  
8285         }
8286         
8287         if (this.before || this.after) {
8288             
8289             inputblock = {
8290                 cls : 'input-group',
8291                 cn :  [] 
8292             };
8293             
8294             if (this.before && typeof(this.before) == 'string') {
8295                 
8296                 inputblock.cn.push({
8297                     tag :'span',
8298                     cls : 'roo-input-before input-group-addon',
8299                     html : this.before
8300                 });
8301             }
8302             if (this.before && typeof(this.before) == 'object') {
8303                 this.before = Roo.factory(this.before);
8304                 
8305                 inputblock.cn.push({
8306                     tag :'span',
8307                     cls : 'roo-input-before input-group-' +
8308                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8309                 });
8310             }
8311             
8312             inputblock.cn.push(input);
8313             
8314             if (this.after && typeof(this.after) == 'string') {
8315                 inputblock.cn.push({
8316                     tag :'span',
8317                     cls : 'roo-input-after input-group-addon',
8318                     html : this.after
8319                 });
8320             }
8321             if (this.after && typeof(this.after) == 'object') {
8322                 this.after = Roo.factory(this.after);
8323                 
8324                 inputblock.cn.push({
8325                     tag :'span',
8326                     cls : 'roo-input-after input-group-' +
8327                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8328                 });
8329             }
8330             
8331             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8332                 inputblock.cls += ' has-feedback';
8333                 inputblock.cn.push(feedback);
8334             }
8335         };
8336         
8337         if (align ==='left' && this.fieldLabel.length) {
8338                 
8339                 cfg.cn = [
8340                     
8341                     {
8342                         tag: 'label',
8343                         'for' :  id,
8344                         cls : 'control-label col-sm-' + this.labelWidth,
8345                         html : this.fieldLabel
8346                         
8347                     },
8348                     {
8349                         cls : "col-sm-" + (12 - this.labelWidth), 
8350                         cn: [
8351                             inputblock
8352                         ]
8353                     }
8354                     
8355                 ];
8356         } else if ( this.fieldLabel.length) {
8357                 
8358                  cfg.cn = [
8359                    
8360                     {
8361                         tag: 'label',
8362                         //cls : 'input-group-addon',
8363                         html : this.fieldLabel
8364                         
8365                     },
8366                     
8367                     inputblock
8368                     
8369                 ];
8370
8371         } else {
8372             
8373                 cfg.cn = [
8374                     
8375                         inputblock
8376                     
8377                 ];
8378                 
8379                 
8380         };
8381         
8382         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8383            cfg.cls += ' navbar-form';
8384         }
8385         if (this.parentType === 'NavGroup') {
8386            cfg.cls += ' navbar-form';
8387            cfg.tag = 'li';
8388         }
8389         return cfg;
8390         
8391     },
8392     /**
8393      * return the real input element.
8394      */
8395     inputEl: function ()
8396     {
8397         return this.el.select('input.form-control',true).first();
8398     },
8399     
8400     tooltipEl : function()
8401     {
8402         return this.inputEl();
8403     },
8404     
8405     setDisabled : function(v)
8406     {
8407         var i  = this.inputEl().dom;
8408         if (!v) {
8409             i.removeAttribute('disabled');
8410             return;
8411             
8412         }
8413         i.setAttribute('disabled','true');
8414     },
8415     initEvents : function()
8416     {
8417           
8418         this.inputEl().on("keydown" , this.fireKey,  this);
8419         this.inputEl().on("focus", this.onFocus,  this);
8420         this.inputEl().on("blur", this.onBlur,  this);
8421         
8422         this.inputEl().relayEvent('keyup', this);
8423  
8424         // reference to original value for reset
8425         this.originalValue = this.getValue();
8426         //Roo.form.TextField.superclass.initEvents.call(this);
8427         if(this.validationEvent == 'keyup'){
8428             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8429             this.inputEl().on('keyup', this.filterValidation, this);
8430         }
8431         else if(this.validationEvent !== false){
8432             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8433         }
8434         
8435         if(this.selectOnFocus){
8436             this.on("focus", this.preFocus, this);
8437             
8438         }
8439         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8440             this.inputEl().on("keypress", this.filterKeys, this);
8441         }
8442        /* if(this.grow){
8443             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8444             this.el.on("click", this.autoSize,  this);
8445         }
8446         */
8447         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8448             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8449         }
8450         
8451         if (typeof(this.before) == 'object') {
8452             this.before.render(this.el.select('.roo-input-before',true).first());
8453         }
8454         if (typeof(this.after) == 'object') {
8455             this.after.render(this.el.select('.roo-input-after',true).first());
8456         }
8457         
8458         
8459     },
8460     filterValidation : function(e){
8461         if(!e.isNavKeyPress()){
8462             this.validationTask.delay(this.validationDelay);
8463         }
8464     },
8465      /**
8466      * Validates the field value
8467      * @return {Boolean} True if the value is valid, else false
8468      */
8469     validate : function(){
8470         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8471         if(this.disabled || this.validateValue(this.getRawValue())){
8472             this.markValid();
8473             return true;
8474         }
8475         
8476         this.markInvalid();
8477         return false;
8478     },
8479     
8480     
8481     /**
8482      * Validates a value according to the field's validation rules and marks the field as invalid
8483      * if the validation fails
8484      * @param {Mixed} value The value to validate
8485      * @return {Boolean} True if the value is valid, else false
8486      */
8487     validateValue : function(value){
8488         if(value.length < 1)  { // if it's blank
8489             if(this.allowBlank){
8490                 return true;
8491             }
8492             return false;
8493         }
8494         
8495         if(value.length < this.minLength){
8496             return false;
8497         }
8498         if(value.length > this.maxLength){
8499             return false;
8500         }
8501         if(this.vtype){
8502             var vt = Roo.form.VTypes;
8503             if(!vt[this.vtype](value, this)){
8504                 return false;
8505             }
8506         }
8507         if(typeof this.validator == "function"){
8508             var msg = this.validator(value);
8509             if(msg !== true){
8510                 return false;
8511             }
8512         }
8513         
8514         if(this.regex && !this.regex.test(value)){
8515             return false;
8516         }
8517         
8518         return true;
8519     },
8520
8521     
8522     
8523      // private
8524     fireKey : function(e){
8525         //Roo.log('field ' + e.getKey());
8526         if(e.isNavKeyPress()){
8527             this.fireEvent("specialkey", this, e);
8528         }
8529     },
8530     focus : function (selectText){
8531         if(this.rendered){
8532             this.inputEl().focus();
8533             if(selectText === true){
8534                 this.inputEl().dom.select();
8535             }
8536         }
8537         return this;
8538     } ,
8539     
8540     onFocus : function(){
8541         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8542            // this.el.addClass(this.focusClass);
8543         }
8544         if(!this.hasFocus){
8545             this.hasFocus = true;
8546             this.startValue = this.getValue();
8547             this.fireEvent("focus", this);
8548         }
8549     },
8550     
8551     beforeBlur : Roo.emptyFn,
8552
8553     
8554     // private
8555     onBlur : function(){
8556         this.beforeBlur();
8557         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8558             //this.el.removeClass(this.focusClass);
8559         }
8560         this.hasFocus = false;
8561         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8562             this.validate();
8563         }
8564         var v = this.getValue();
8565         if(String(v) !== String(this.startValue)){
8566             this.fireEvent('change', this, v, this.startValue);
8567         }
8568         this.fireEvent("blur", this);
8569     },
8570     
8571     /**
8572      * Resets the current field value to the originally loaded value and clears any validation messages
8573      */
8574     reset : function(){
8575         this.setValue(this.originalValue);
8576         this.validate();
8577     },
8578      /**
8579      * Returns the name of the field
8580      * @return {Mixed} name The name field
8581      */
8582     getName: function(){
8583         return this.name;
8584     },
8585      /**
8586      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8587      * @return {Mixed} value The field value
8588      */
8589     getValue : function(){
8590         
8591         var v = this.inputEl().getValue();
8592         
8593         return v;
8594     },
8595     /**
8596      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8597      * @return {Mixed} value The field value
8598      */
8599     getRawValue : function(){
8600         var v = this.inputEl().getValue();
8601         
8602         return v;
8603     },
8604     
8605     /**
8606      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8607      * @param {Mixed} value The value to set
8608      */
8609     setRawValue : function(v){
8610         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8611     },
8612     
8613     selectText : function(start, end){
8614         var v = this.getRawValue();
8615         if(v.length > 0){
8616             start = start === undefined ? 0 : start;
8617             end = end === undefined ? v.length : end;
8618             var d = this.inputEl().dom;
8619             if(d.setSelectionRange){
8620                 d.setSelectionRange(start, end);
8621             }else if(d.createTextRange){
8622                 var range = d.createTextRange();
8623                 range.moveStart("character", start);
8624                 range.moveEnd("character", v.length-end);
8625                 range.select();
8626             }
8627         }
8628     },
8629     
8630     /**
8631      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8632      * @param {Mixed} value The value to set
8633      */
8634     setValue : function(v){
8635         this.value = v;
8636         if(this.rendered){
8637             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8638             this.validate();
8639         }
8640     },
8641     
8642     /*
8643     processValue : function(value){
8644         if(this.stripCharsRe){
8645             var newValue = value.replace(this.stripCharsRe, '');
8646             if(newValue !== value){
8647                 this.setRawValue(newValue);
8648                 return newValue;
8649             }
8650         }
8651         return value;
8652     },
8653   */
8654     preFocus : function(){
8655         
8656         if(this.selectOnFocus){
8657             this.inputEl().dom.select();
8658         }
8659     },
8660     filterKeys : function(e){
8661         var k = e.getKey();
8662         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8663             return;
8664         }
8665         var c = e.getCharCode(), cc = String.fromCharCode(c);
8666         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8667             return;
8668         }
8669         if(!this.maskRe.test(cc)){
8670             e.stopEvent();
8671         }
8672     },
8673      /**
8674      * Clear any invalid styles/messages for this field
8675      */
8676     clearInvalid : function(){
8677         
8678         if(!this.el || this.preventMark){ // not rendered
8679             return;
8680         }
8681         
8682         var label = this.el.select('label', true).first();
8683         var icon = this.el.select('i.fa-star', true).first();
8684         
8685         if(label && icon){
8686             icon.remove();
8687         }
8688         
8689         this.el.removeClass(this.invalidClass);
8690         
8691         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8692             
8693             var feedback = this.el.select('.form-control-feedback', true).first();
8694             
8695             if(feedback){
8696                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8697             }
8698             
8699         }
8700         
8701         this.fireEvent('valid', this);
8702     },
8703     
8704      /**
8705      * Mark this field as valid
8706      */
8707     markValid : function()
8708     {
8709         if(!this.el  || this.preventMark){ // not rendered
8710             return;
8711         }
8712         
8713         this.el.removeClass([this.invalidClass, this.validClass]);
8714         
8715         var feedback = this.el.select('.form-control-feedback', true).first();
8716             
8717         if(feedback){
8718             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8719         }
8720
8721         if(this.disabled || this.allowBlank){
8722             return;
8723         }
8724         
8725         var formGroup = this.el.findParent('.form-group', false, true);
8726         
8727         if(formGroup){
8728             
8729             var label = formGroup.select('label', true).first();
8730             var icon = formGroup.select('i.fa-star', true).first();
8731             
8732             if(label && icon){
8733                 icon.remove();
8734             }
8735         }
8736         
8737         this.el.addClass(this.validClass);
8738         
8739         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8740             
8741             var feedback = this.el.select('.form-control-feedback', true).first();
8742             
8743             if(feedback){
8744                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8745                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8746             }
8747             
8748         }
8749         
8750         this.fireEvent('valid', this);
8751     },
8752     
8753      /**
8754      * Mark this field as invalid
8755      * @param {String} msg The validation message
8756      */
8757     markInvalid : function(msg)
8758     {
8759         if(!this.el  || this.preventMark){ // not rendered
8760             return;
8761         }
8762         
8763         this.el.removeClass([this.invalidClass, this.validClass]);
8764         
8765         var feedback = this.el.select('.form-control-feedback', true).first();
8766             
8767         if(feedback){
8768             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8769         }
8770
8771         if(this.disabled || this.allowBlank){
8772             return;
8773         }
8774         
8775         var formGroup = this.el.findParent('.form-group', false, true);
8776         
8777         if(formGroup){
8778             var label = formGroup.select('label', true).first();
8779             var icon = formGroup.select('i.fa-star', true).first();
8780
8781             if(!this.getValue().length && label && !icon){
8782                 this.el.findParent('.form-group', false, true).createChild({
8783                     tag : 'i',
8784                     cls : 'text-danger fa fa-lg fa-star',
8785                     tooltip : 'This field is required',
8786                     style : 'margin-right:5px;'
8787                 }, label, true);
8788             }
8789         }
8790         
8791         
8792         this.el.addClass(this.invalidClass);
8793         
8794         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8795             
8796             var feedback = this.el.select('.form-control-feedback', true).first();
8797             
8798             if(feedback){
8799                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8800                 
8801                 if(this.getValue().length || this.forceFeedback){
8802                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8803                 }
8804                 
8805             }
8806             
8807         }
8808         
8809         this.fireEvent('invalid', this, msg);
8810     },
8811     // private
8812     SafariOnKeyDown : function(event)
8813     {
8814         // this is a workaround for a password hang bug on chrome/ webkit.
8815         
8816         var isSelectAll = false;
8817         
8818         if(this.inputEl().dom.selectionEnd > 0){
8819             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8820         }
8821         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8822             event.preventDefault();
8823             this.setValue('');
8824             return;
8825         }
8826         
8827         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8828             
8829             event.preventDefault();
8830             // this is very hacky as keydown always get's upper case.
8831             //
8832             var cc = String.fromCharCode(event.getCharCode());
8833             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8834             
8835         }
8836     },
8837     adjustWidth : function(tag, w){
8838         tag = tag.toLowerCase();
8839         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8840             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8841                 if(tag == 'input'){
8842                     return w + 2;
8843                 }
8844                 if(tag == 'textarea'){
8845                     return w-2;
8846                 }
8847             }else if(Roo.isOpera){
8848                 if(tag == 'input'){
8849                     return w + 2;
8850                 }
8851                 if(tag == 'textarea'){
8852                     return w-2;
8853                 }
8854             }
8855         }
8856         return w;
8857     }
8858     
8859 });
8860
8861  
8862 /*
8863  * - LGPL
8864  *
8865  * Input
8866  * 
8867  */
8868
8869 /**
8870  * @class Roo.bootstrap.TextArea
8871  * @extends Roo.bootstrap.Input
8872  * Bootstrap TextArea class
8873  * @cfg {Number} cols Specifies the visible width of a text area
8874  * @cfg {Number} rows Specifies the visible number of lines in a text area
8875  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8876  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8877  * @cfg {string} html text
8878  * 
8879  * @constructor
8880  * Create a new TextArea
8881  * @param {Object} config The config object
8882  */
8883
8884 Roo.bootstrap.TextArea = function(config){
8885     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8886    
8887 };
8888
8889 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8890      
8891     cols : false,
8892     rows : 5,
8893     readOnly : false,
8894     warp : 'soft',
8895     resize : false,
8896     value: false,
8897     html: false,
8898     
8899     getAutoCreate : function(){
8900         
8901         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8902         
8903         var id = Roo.id();
8904         
8905         var cfg = {};
8906         
8907         var input =  {
8908             tag: 'textarea',
8909             id : id,
8910             warp : this.warp,
8911             rows : this.rows,
8912             value : this.value || '',
8913             html: this.html || '',
8914             cls : 'form-control',
8915             placeholder : this.placeholder || '' 
8916             
8917         };
8918         
8919         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8920             input.maxLength = this.maxLength;
8921         }
8922         
8923         if(this.resize){
8924             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8925         }
8926         
8927         if(this.cols){
8928             input.cols = this.cols;
8929         }
8930         
8931         if (this.readOnly) {
8932             input.readonly = true;
8933         }
8934         
8935         if (this.name) {
8936             input.name = this.name;
8937         }
8938         
8939         if (this.size) {
8940             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8941         }
8942         
8943         var settings=this;
8944         ['xs','sm','md','lg'].map(function(size){
8945             if (settings[size]) {
8946                 cfg.cls += ' col-' + size + '-' + settings[size];
8947             }
8948         });
8949         
8950         var inputblock = input;
8951         
8952         if(this.hasFeedback && !this.allowBlank){
8953             
8954             var feedback = {
8955                 tag: 'span',
8956                 cls: 'glyphicon form-control-feedback'
8957             };
8958
8959             inputblock = {
8960                 cls : 'has-feedback',
8961                 cn :  [
8962                     input,
8963                     feedback
8964                 ] 
8965             };  
8966         }
8967         
8968         
8969         if (this.before || this.after) {
8970             
8971             inputblock = {
8972                 cls : 'input-group',
8973                 cn :  [] 
8974             };
8975             if (this.before) {
8976                 inputblock.cn.push({
8977                     tag :'span',
8978                     cls : 'input-group-addon',
8979                     html : this.before
8980                 });
8981             }
8982             
8983             inputblock.cn.push(input);
8984             
8985             if(this.hasFeedback && !this.allowBlank){
8986                 inputblock.cls += ' has-feedback';
8987                 inputblock.cn.push(feedback);
8988             }
8989             
8990             if (this.after) {
8991                 inputblock.cn.push({
8992                     tag :'span',
8993                     cls : 'input-group-addon',
8994                     html : this.after
8995                 });
8996             }
8997             
8998         }
8999         
9000         if (align ==='left' && this.fieldLabel.length) {
9001 //                Roo.log("left and has label");
9002                 cfg.cn = [
9003                     
9004                     {
9005                         tag: 'label',
9006                         'for' :  id,
9007                         cls : 'control-label col-sm-' + this.labelWidth,
9008                         html : this.fieldLabel
9009                         
9010                     },
9011                     {
9012                         cls : "col-sm-" + (12 - this.labelWidth), 
9013                         cn: [
9014                             inputblock
9015                         ]
9016                     }
9017                     
9018                 ];
9019         } else if ( this.fieldLabel.length) {
9020 //                Roo.log(" label");
9021                  cfg.cn = [
9022                    
9023                     {
9024                         tag: 'label',
9025                         //cls : 'input-group-addon',
9026                         html : this.fieldLabel
9027                         
9028                     },
9029                     
9030                     inputblock
9031                     
9032                 ];
9033
9034         } else {
9035             
9036 //                   Roo.log(" no label && no align");
9037                 cfg.cn = [
9038                     
9039                         inputblock
9040                     
9041                 ];
9042                 
9043                 
9044         }
9045         
9046         if (this.disabled) {
9047             input.disabled=true;
9048         }
9049         
9050         return cfg;
9051         
9052     },
9053     /**
9054      * return the real textarea element.
9055      */
9056     inputEl: function ()
9057     {
9058         return this.el.select('textarea.form-control',true).first();
9059     },
9060     
9061     /**
9062      * Clear any invalid styles/messages for this field
9063      */
9064     clearInvalid : function()
9065     {
9066         
9067         if(!this.el || this.preventMark){ // not rendered
9068             return;
9069         }
9070         
9071         var label = this.el.select('label', true).first();
9072         var icon = this.el.select('i.fa-star', true).first();
9073         
9074         if(label && icon){
9075             icon.remove();
9076         }
9077         
9078         this.el.removeClass(this.invalidClass);
9079         
9080         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9081             
9082             var feedback = this.el.select('.form-control-feedback', true).first();
9083             
9084             if(feedback){
9085                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9086             }
9087             
9088         }
9089         
9090         this.fireEvent('valid', this);
9091     },
9092     
9093      /**
9094      * Mark this field as valid
9095      */
9096     markValid : function()
9097     {
9098         if(!this.el  || this.preventMark){ // not rendered
9099             return;
9100         }
9101         
9102         this.el.removeClass([this.invalidClass, this.validClass]);
9103         
9104         var feedback = this.el.select('.form-control-feedback', true).first();
9105             
9106         if(feedback){
9107             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9108         }
9109
9110         if(this.disabled || this.allowBlank){
9111             return;
9112         }
9113         
9114         var label = this.el.select('label', true).first();
9115         var icon = this.el.select('i.fa-star', true).first();
9116         
9117         if(label && icon){
9118             icon.remove();
9119         }
9120         
9121         this.el.addClass(this.validClass);
9122         
9123         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9124             
9125             var feedback = this.el.select('.form-control-feedback', true).first();
9126             
9127             if(feedback){
9128                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9129                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9130             }
9131             
9132         }
9133         
9134         this.fireEvent('valid', this);
9135     },
9136     
9137      /**
9138      * Mark this field as invalid
9139      * @param {String} msg The validation message
9140      */
9141     markInvalid : function(msg)
9142     {
9143         if(!this.el  || this.preventMark){ // not rendered
9144             return;
9145         }
9146         
9147         this.el.removeClass([this.invalidClass, this.validClass]);
9148         
9149         var feedback = this.el.select('.form-control-feedback', true).first();
9150             
9151         if(feedback){
9152             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9153         }
9154
9155         if(this.disabled || this.allowBlank){
9156             return;
9157         }
9158         
9159         var label = this.el.select('label', true).first();
9160         var icon = this.el.select('i.fa-star', true).first();
9161         
9162         if(!this.getValue().length && label && !icon){
9163             this.el.createChild({
9164                 tag : 'i',
9165                 cls : 'text-danger fa fa-lg fa-star',
9166                 tooltip : 'This field is required',
9167                 style : 'margin-right:5px;'
9168             }, label, true);
9169         }
9170
9171         this.el.addClass(this.invalidClass);
9172         
9173         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9174             
9175             var feedback = this.el.select('.form-control-feedback', true).first();
9176             
9177             if(feedback){
9178                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9179                 
9180                 if(this.getValue().length || this.forceFeedback){
9181                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9182                 }
9183                 
9184             }
9185             
9186         }
9187         
9188         this.fireEvent('invalid', this, msg);
9189     }
9190 });
9191
9192  
9193 /*
9194  * - LGPL
9195  *
9196  * trigger field - base class for combo..
9197  * 
9198  */
9199  
9200 /**
9201  * @class Roo.bootstrap.TriggerField
9202  * @extends Roo.bootstrap.Input
9203  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9204  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9205  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9206  * for which you can provide a custom implementation.  For example:
9207  * <pre><code>
9208 var trigger = new Roo.bootstrap.TriggerField();
9209 trigger.onTriggerClick = myTriggerFn;
9210 trigger.applyTo('my-field');
9211 </code></pre>
9212  *
9213  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9214  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9215  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9216  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9217  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9218
9219  * @constructor
9220  * Create a new TriggerField.
9221  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9222  * to the base TextField)
9223  */
9224 Roo.bootstrap.TriggerField = function(config){
9225     this.mimicing = false;
9226     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9227 };
9228
9229 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9230     /**
9231      * @cfg {String} triggerClass A CSS class to apply to the trigger
9232      */
9233      /**
9234      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9235      */
9236     hideTrigger:false,
9237
9238     /**
9239      * @cfg {Boolean} removable (true|false) special filter default false
9240      */
9241     removable : false,
9242     
9243     /** @cfg {Boolean} grow @hide */
9244     /** @cfg {Number} growMin @hide */
9245     /** @cfg {Number} growMax @hide */
9246
9247     /**
9248      * @hide 
9249      * @method
9250      */
9251     autoSize: Roo.emptyFn,
9252     // private
9253     monitorTab : true,
9254     // private
9255     deferHeight : true,
9256
9257     
9258     actionMode : 'wrap',
9259     
9260     caret : false,
9261     
9262     
9263     getAutoCreate : function(){
9264        
9265         var align = this.labelAlign || this.parentLabelAlign();
9266         
9267         var id = Roo.id();
9268         
9269         var cfg = {
9270             cls: 'form-group' //input-group
9271         };
9272         
9273         
9274         var input =  {
9275             tag: 'input',
9276             id : id,
9277             type : this.inputType,
9278             cls : 'form-control',
9279             autocomplete: 'new-password',
9280             placeholder : this.placeholder || '' 
9281             
9282         };
9283         if (this.name) {
9284             input.name = this.name;
9285         }
9286         if (this.size) {
9287             input.cls += ' input-' + this.size;
9288         }
9289         
9290         if (this.disabled) {
9291             input.disabled=true;
9292         }
9293         
9294         var inputblock = input;
9295         
9296         if(this.hasFeedback && !this.allowBlank){
9297             
9298             var feedback = {
9299                 tag: 'span',
9300                 cls: 'glyphicon form-control-feedback'
9301             };
9302             
9303             if(this.removable && !this.editable && !this.tickable){
9304                 inputblock = {
9305                     cls : 'has-feedback',
9306                     cn :  [
9307                         inputblock,
9308                         {
9309                             tag: 'button',
9310                             html : 'x',
9311                             cls : 'roo-combo-removable-btn close'
9312                         },
9313                         feedback
9314                     ] 
9315                 };
9316             } else {
9317                 inputblock = {
9318                     cls : 'has-feedback',
9319                     cn :  [
9320                         inputblock,
9321                         feedback
9322                     ] 
9323                 };
9324             }
9325
9326         } else {
9327             if(this.removable && !this.editable && !this.tickable){
9328                 inputblock = {
9329                     cls : 'roo-removable',
9330                     cn :  [
9331                         inputblock,
9332                         {
9333                             tag: 'button',
9334                             html : 'x',
9335                             cls : 'roo-combo-removable-btn close'
9336                         }
9337                     ] 
9338                 };
9339             }
9340         }
9341         
9342         if (this.before || this.after) {
9343             
9344             inputblock = {
9345                 cls : 'input-group',
9346                 cn :  [] 
9347             };
9348             if (this.before) {
9349                 inputblock.cn.push({
9350                     tag :'span',
9351                     cls : 'input-group-addon',
9352                     html : this.before
9353                 });
9354             }
9355             
9356             inputblock.cn.push(input);
9357             
9358             if(this.hasFeedback && !this.allowBlank){
9359                 inputblock.cls += ' has-feedback';
9360                 inputblock.cn.push(feedback);
9361             }
9362             
9363             if (this.after) {
9364                 inputblock.cn.push({
9365                     tag :'span',
9366                     cls : 'input-group-addon',
9367                     html : this.after
9368                 });
9369             }
9370             
9371         };
9372         
9373         var box = {
9374             tag: 'div',
9375             cn: [
9376                 {
9377                     tag: 'input',
9378                     type : 'hidden',
9379                     cls: 'form-hidden-field'
9380                 },
9381                 inputblock
9382             ]
9383             
9384         };
9385         
9386         if(this.multiple){
9387             box = {
9388                 tag: 'div',
9389                 cn: [
9390                     {
9391                         tag: 'input',
9392                         type : 'hidden',
9393                         cls: 'form-hidden-field'
9394                     },
9395                     {
9396                         tag: 'ul',
9397                         cls: 'roo-select2-choices',
9398                         cn:[
9399                             {
9400                                 tag: 'li',
9401                                 cls: 'roo-select2-search-field',
9402                                 cn: [
9403
9404                                     inputblock
9405                                 ]
9406                             }
9407                         ]
9408                     }
9409                 ]
9410             }
9411         };
9412         
9413         var combobox = {
9414             cls: 'roo-select2-container input-group',
9415             cn: [
9416                 box
9417 //                {
9418 //                    tag: 'ul',
9419 //                    cls: 'typeahead typeahead-long dropdown-menu',
9420 //                    style: 'display:none'
9421 //                }
9422             ]
9423         };
9424         
9425         if(!this.multiple && this.showToggleBtn){
9426             
9427             var caret = {
9428                         tag: 'span',
9429                         cls: 'caret'
9430              };
9431             if (this.caret != false) {
9432                 caret = {
9433                      tag: 'i',
9434                      cls: 'fa fa-' + this.caret
9435                 };
9436                 
9437             }
9438             
9439             combobox.cn.push({
9440                 tag :'span',
9441                 cls : 'input-group-addon btn dropdown-toggle',
9442                 cn : [
9443                     caret,
9444                     {
9445                         tag: 'span',
9446                         cls: 'combobox-clear',
9447                         cn  : [
9448                             {
9449                                 tag : 'i',
9450                                 cls: 'icon-remove'
9451                             }
9452                         ]
9453                     }
9454                 ]
9455
9456             })
9457         }
9458         
9459         if(this.multiple){
9460             combobox.cls += ' roo-select2-container-multi';
9461         }
9462         
9463         if (align ==='left' && this.fieldLabel.length) {
9464             
9465 //                Roo.log("left and has label");
9466                 cfg.cn = [
9467                     
9468                     {
9469                         tag: 'label',
9470                         'for' :  id,
9471                         cls : 'control-label col-sm-' + this.labelWidth,
9472                         html : this.fieldLabel
9473                         
9474                     },
9475                     {
9476                         cls : "col-sm-" + (12 - this.labelWidth), 
9477                         cn: [
9478                             combobox
9479                         ]
9480                     }
9481                     
9482                 ];
9483         } else if ( this.fieldLabel.length) {
9484 //                Roo.log(" label");
9485                  cfg.cn = [
9486                    
9487                     {
9488                         tag: 'label',
9489                         //cls : 'input-group-addon',
9490                         html : this.fieldLabel
9491                         
9492                     },
9493                     
9494                     combobox
9495                     
9496                 ];
9497
9498         } else {
9499             
9500 //                Roo.log(" no label && no align");
9501                 cfg = combobox
9502                      
9503                 
9504         }
9505          
9506         var settings=this;
9507         ['xs','sm','md','lg'].map(function(size){
9508             if (settings[size]) {
9509                 cfg.cls += ' col-' + size + '-' + settings[size];
9510             }
9511         });
9512         
9513         return cfg;
9514         
9515     },
9516     
9517     
9518     
9519     // private
9520     onResize : function(w, h){
9521 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9522 //        if(typeof w == 'number'){
9523 //            var x = w - this.trigger.getWidth();
9524 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9525 //            this.trigger.setStyle('left', x+'px');
9526 //        }
9527     },
9528
9529     // private
9530     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9531
9532     // private
9533     getResizeEl : function(){
9534         return this.inputEl();
9535     },
9536
9537     // private
9538     getPositionEl : function(){
9539         return this.inputEl();
9540     },
9541
9542     // private
9543     alignErrorIcon : function(){
9544         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9545     },
9546
9547     // private
9548     initEvents : function(){
9549         
9550         this.createList();
9551         
9552         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9553         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9554         if(!this.multiple && this.showToggleBtn){
9555             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9556             if(this.hideTrigger){
9557                 this.trigger.setDisplayed(false);
9558             }
9559             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9560         }
9561         
9562         if(this.multiple){
9563             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9564         }
9565         
9566         if(this.removable && !this.editable && !this.tickable){
9567             var close = this.closeTriggerEl();
9568             
9569             if(close){
9570                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9571                 close.on('click', this.removeBtnClick, this, close);
9572             }
9573         }
9574         
9575         //this.trigger.addClassOnOver('x-form-trigger-over');
9576         //this.trigger.addClassOnClick('x-form-trigger-click');
9577         
9578         //if(!this.width){
9579         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9580         //}
9581     },
9582     
9583     closeTriggerEl : function()
9584     {
9585         var close = this.el.select('.roo-combo-removable-btn', true).first();
9586         return close ? close : false;
9587     },
9588     
9589     removeBtnClick : function(e, h, el)
9590     {
9591         e.preventDefault();
9592         
9593         if(this.fireEvent("remove", this) !== false){
9594             this.reset();
9595             this.fireEvent("afterremove", this)
9596         }
9597     },
9598     
9599     createList : function()
9600     {
9601         this.list = Roo.get(document.body).createChild({
9602             tag: 'ul',
9603             cls: 'typeahead typeahead-long dropdown-menu',
9604             style: 'display:none'
9605         });
9606         
9607         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9608         
9609     },
9610
9611     // private
9612     initTrigger : function(){
9613        
9614     },
9615
9616     // private
9617     onDestroy : function(){
9618         if(this.trigger){
9619             this.trigger.removeAllListeners();
9620           //  this.trigger.remove();
9621         }
9622         //if(this.wrap){
9623         //    this.wrap.remove();
9624         //}
9625         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9626     },
9627
9628     // private
9629     onFocus : function(){
9630         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9631         /*
9632         if(!this.mimicing){
9633             this.wrap.addClass('x-trigger-wrap-focus');
9634             this.mimicing = true;
9635             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9636             if(this.monitorTab){
9637                 this.el.on("keydown", this.checkTab, this);
9638             }
9639         }
9640         */
9641     },
9642
9643     // private
9644     checkTab : function(e){
9645         if(e.getKey() == e.TAB){
9646             this.triggerBlur();
9647         }
9648     },
9649
9650     // private
9651     onBlur : function(){
9652         // do nothing
9653     },
9654
9655     // private
9656     mimicBlur : function(e, t){
9657         /*
9658         if(!this.wrap.contains(t) && this.validateBlur()){
9659             this.triggerBlur();
9660         }
9661         */
9662     },
9663
9664     // private
9665     triggerBlur : function(){
9666         this.mimicing = false;
9667         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9668         if(this.monitorTab){
9669             this.el.un("keydown", this.checkTab, this);
9670         }
9671         //this.wrap.removeClass('x-trigger-wrap-focus');
9672         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9673     },
9674
9675     // private
9676     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9677     validateBlur : function(e, t){
9678         return true;
9679     },
9680
9681     // private
9682     onDisable : function(){
9683         this.inputEl().dom.disabled = true;
9684         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9685         //if(this.wrap){
9686         //    this.wrap.addClass('x-item-disabled');
9687         //}
9688     },
9689
9690     // private
9691     onEnable : function(){
9692         this.inputEl().dom.disabled = false;
9693         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9694         //if(this.wrap){
9695         //    this.el.removeClass('x-item-disabled');
9696         //}
9697     },
9698
9699     // private
9700     onShow : function(){
9701         var ae = this.getActionEl();
9702         
9703         if(ae){
9704             ae.dom.style.display = '';
9705             ae.dom.style.visibility = 'visible';
9706         }
9707     },
9708
9709     // private
9710     
9711     onHide : function(){
9712         var ae = this.getActionEl();
9713         ae.dom.style.display = 'none';
9714     },
9715
9716     /**
9717      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9718      * by an implementing function.
9719      * @method
9720      * @param {EventObject} e
9721      */
9722     onTriggerClick : Roo.emptyFn
9723 });
9724  /*
9725  * Based on:
9726  * Ext JS Library 1.1.1
9727  * Copyright(c) 2006-2007, Ext JS, LLC.
9728  *
9729  * Originally Released Under LGPL - original licence link has changed is not relivant.
9730  *
9731  * Fork - LGPL
9732  * <script type="text/javascript">
9733  */
9734
9735
9736 /**
9737  * @class Roo.data.SortTypes
9738  * @singleton
9739  * Defines the default sorting (casting?) comparison functions used when sorting data.
9740  */
9741 Roo.data.SortTypes = {
9742     /**
9743      * Default sort that does nothing
9744      * @param {Mixed} s The value being converted
9745      * @return {Mixed} The comparison value
9746      */
9747     none : function(s){
9748         return s;
9749     },
9750     
9751     /**
9752      * The regular expression used to strip tags
9753      * @type {RegExp}
9754      * @property
9755      */
9756     stripTagsRE : /<\/?[^>]+>/gi,
9757     
9758     /**
9759      * Strips all HTML tags to sort on text only
9760      * @param {Mixed} s The value being converted
9761      * @return {String} The comparison value
9762      */
9763     asText : function(s){
9764         return String(s).replace(this.stripTagsRE, "");
9765     },
9766     
9767     /**
9768      * Strips all HTML tags to sort on text only - Case insensitive
9769      * @param {Mixed} s The value being converted
9770      * @return {String} The comparison value
9771      */
9772     asUCText : function(s){
9773         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9774     },
9775     
9776     /**
9777      * Case insensitive string
9778      * @param {Mixed} s The value being converted
9779      * @return {String} The comparison value
9780      */
9781     asUCString : function(s) {
9782         return String(s).toUpperCase();
9783     },
9784     
9785     /**
9786      * Date sorting
9787      * @param {Mixed} s The value being converted
9788      * @return {Number} The comparison value
9789      */
9790     asDate : function(s) {
9791         if(!s){
9792             return 0;
9793         }
9794         if(s instanceof Date){
9795             return s.getTime();
9796         }
9797         return Date.parse(String(s));
9798     },
9799     
9800     /**
9801      * Float sorting
9802      * @param {Mixed} s The value being converted
9803      * @return {Float} The comparison value
9804      */
9805     asFloat : function(s) {
9806         var val = parseFloat(String(s).replace(/,/g, ""));
9807         if(isNaN(val)) {
9808             val = 0;
9809         }
9810         return val;
9811     },
9812     
9813     /**
9814      * Integer sorting
9815      * @param {Mixed} s The value being converted
9816      * @return {Number} The comparison value
9817      */
9818     asInt : function(s) {
9819         var val = parseInt(String(s).replace(/,/g, ""));
9820         if(isNaN(val)) {
9821             val = 0;
9822         }
9823         return val;
9824     }
9825 };/*
9826  * Based on:
9827  * Ext JS Library 1.1.1
9828  * Copyright(c) 2006-2007, Ext JS, LLC.
9829  *
9830  * Originally Released Under LGPL - original licence link has changed is not relivant.
9831  *
9832  * Fork - LGPL
9833  * <script type="text/javascript">
9834  */
9835
9836 /**
9837 * @class Roo.data.Record
9838  * Instances of this class encapsulate both record <em>definition</em> information, and record
9839  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9840  * to access Records cached in an {@link Roo.data.Store} object.<br>
9841  * <p>
9842  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9843  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9844  * objects.<br>
9845  * <p>
9846  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9847  * @constructor
9848  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9849  * {@link #create}. The parameters are the same.
9850  * @param {Array} data An associative Array of data values keyed by the field name.
9851  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9852  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9853  * not specified an integer id is generated.
9854  */
9855 Roo.data.Record = function(data, id){
9856     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9857     this.data = data;
9858 };
9859
9860 /**
9861  * Generate a constructor for a specific record layout.
9862  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9863  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9864  * Each field definition object may contain the following properties: <ul>
9865  * <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,
9866  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9867  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9868  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9869  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9870  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9871  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9872  * this may be omitted.</p></li>
9873  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9874  * <ul><li>auto (Default, implies no conversion)</li>
9875  * <li>string</li>
9876  * <li>int</li>
9877  * <li>float</li>
9878  * <li>boolean</li>
9879  * <li>date</li></ul></p></li>
9880  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9881  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9882  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9883  * by the Reader into an object that will be stored in the Record. It is passed the
9884  * following parameters:<ul>
9885  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9886  * </ul></p></li>
9887  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9888  * </ul>
9889  * <br>usage:<br><pre><code>
9890 var TopicRecord = Roo.data.Record.create(
9891     {name: 'title', mapping: 'topic_title'},
9892     {name: 'author', mapping: 'username'},
9893     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9894     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9895     {name: 'lastPoster', mapping: 'user2'},
9896     {name: 'excerpt', mapping: 'post_text'}
9897 );
9898
9899 var myNewRecord = new TopicRecord({
9900     title: 'Do my job please',
9901     author: 'noobie',
9902     totalPosts: 1,
9903     lastPost: new Date(),
9904     lastPoster: 'Animal',
9905     excerpt: 'No way dude!'
9906 });
9907 myStore.add(myNewRecord);
9908 </code></pre>
9909  * @method create
9910  * @static
9911  */
9912 Roo.data.Record.create = function(o){
9913     var f = function(){
9914         f.superclass.constructor.apply(this, arguments);
9915     };
9916     Roo.extend(f, Roo.data.Record);
9917     var p = f.prototype;
9918     p.fields = new Roo.util.MixedCollection(false, function(field){
9919         return field.name;
9920     });
9921     for(var i = 0, len = o.length; i < len; i++){
9922         p.fields.add(new Roo.data.Field(o[i]));
9923     }
9924     f.getField = function(name){
9925         return p.fields.get(name);  
9926     };
9927     return f;
9928 };
9929
9930 Roo.data.Record.AUTO_ID = 1000;
9931 Roo.data.Record.EDIT = 'edit';
9932 Roo.data.Record.REJECT = 'reject';
9933 Roo.data.Record.COMMIT = 'commit';
9934
9935 Roo.data.Record.prototype = {
9936     /**
9937      * Readonly flag - true if this record has been modified.
9938      * @type Boolean
9939      */
9940     dirty : false,
9941     editing : false,
9942     error: null,
9943     modified: null,
9944
9945     // private
9946     join : function(store){
9947         this.store = store;
9948     },
9949
9950     /**
9951      * Set the named field to the specified value.
9952      * @param {String} name The name of the field to set.
9953      * @param {Object} value The value to set the field to.
9954      */
9955     set : function(name, value){
9956         if(this.data[name] == value){
9957             return;
9958         }
9959         this.dirty = true;
9960         if(!this.modified){
9961             this.modified = {};
9962         }
9963         if(typeof this.modified[name] == 'undefined'){
9964             this.modified[name] = this.data[name];
9965         }
9966         this.data[name] = value;
9967         if(!this.editing && this.store){
9968             this.store.afterEdit(this);
9969         }       
9970     },
9971
9972     /**
9973      * Get the value of the named field.
9974      * @param {String} name The name of the field to get the value of.
9975      * @return {Object} The value of the field.
9976      */
9977     get : function(name){
9978         return this.data[name]; 
9979     },
9980
9981     // private
9982     beginEdit : function(){
9983         this.editing = true;
9984         this.modified = {}; 
9985     },
9986
9987     // private
9988     cancelEdit : function(){
9989         this.editing = false;
9990         delete this.modified;
9991     },
9992
9993     // private
9994     endEdit : function(){
9995         this.editing = false;
9996         if(this.dirty && this.store){
9997             this.store.afterEdit(this);
9998         }
9999     },
10000
10001     /**
10002      * Usually called by the {@link Roo.data.Store} which owns the Record.
10003      * Rejects all changes made to the Record since either creation, or the last commit operation.
10004      * Modified fields are reverted to their original values.
10005      * <p>
10006      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10007      * of reject operations.
10008      */
10009     reject : function(){
10010         var m = this.modified;
10011         for(var n in m){
10012             if(typeof m[n] != "function"){
10013                 this.data[n] = m[n];
10014             }
10015         }
10016         this.dirty = false;
10017         delete this.modified;
10018         this.editing = false;
10019         if(this.store){
10020             this.store.afterReject(this);
10021         }
10022     },
10023
10024     /**
10025      * Usually called by the {@link Roo.data.Store} which owns the Record.
10026      * Commits all changes made to the Record since either creation, or the last commit operation.
10027      * <p>
10028      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10029      * of commit operations.
10030      */
10031     commit : function(){
10032         this.dirty = false;
10033         delete this.modified;
10034         this.editing = false;
10035         if(this.store){
10036             this.store.afterCommit(this);
10037         }
10038     },
10039
10040     // private
10041     hasError : function(){
10042         return this.error != null;
10043     },
10044
10045     // private
10046     clearError : function(){
10047         this.error = null;
10048     },
10049
10050     /**
10051      * Creates a copy of this record.
10052      * @param {String} id (optional) A new record id if you don't want to use this record's id
10053      * @return {Record}
10054      */
10055     copy : function(newId) {
10056         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10057     }
10058 };/*
10059  * Based on:
10060  * Ext JS Library 1.1.1
10061  * Copyright(c) 2006-2007, Ext JS, LLC.
10062  *
10063  * Originally Released Under LGPL - original licence link has changed is not relivant.
10064  *
10065  * Fork - LGPL
10066  * <script type="text/javascript">
10067  */
10068
10069
10070
10071 /**
10072  * @class Roo.data.Store
10073  * @extends Roo.util.Observable
10074  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10075  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10076  * <p>
10077  * 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
10078  * has no knowledge of the format of the data returned by the Proxy.<br>
10079  * <p>
10080  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10081  * instances from the data object. These records are cached and made available through accessor functions.
10082  * @constructor
10083  * Creates a new Store.
10084  * @param {Object} config A config object containing the objects needed for the Store to access data,
10085  * and read the data into Records.
10086  */
10087 Roo.data.Store = function(config){
10088     this.data = new Roo.util.MixedCollection(false);
10089     this.data.getKey = function(o){
10090         return o.id;
10091     };
10092     this.baseParams = {};
10093     // private
10094     this.paramNames = {
10095         "start" : "start",
10096         "limit" : "limit",
10097         "sort" : "sort",
10098         "dir" : "dir",
10099         "multisort" : "_multisort"
10100     };
10101
10102     if(config && config.data){
10103         this.inlineData = config.data;
10104         delete config.data;
10105     }
10106
10107     Roo.apply(this, config);
10108     
10109     if(this.reader){ // reader passed
10110         this.reader = Roo.factory(this.reader, Roo.data);
10111         this.reader.xmodule = this.xmodule || false;
10112         if(!this.recordType){
10113             this.recordType = this.reader.recordType;
10114         }
10115         if(this.reader.onMetaChange){
10116             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10117         }
10118     }
10119
10120     if(this.recordType){
10121         this.fields = this.recordType.prototype.fields;
10122     }
10123     this.modified = [];
10124
10125     this.addEvents({
10126         /**
10127          * @event datachanged
10128          * Fires when the data cache has changed, and a widget which is using this Store
10129          * as a Record cache should refresh its view.
10130          * @param {Store} this
10131          */
10132         datachanged : true,
10133         /**
10134          * @event metachange
10135          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10136          * @param {Store} this
10137          * @param {Object} meta The JSON metadata
10138          */
10139         metachange : true,
10140         /**
10141          * @event add
10142          * Fires when Records have been added to the Store
10143          * @param {Store} this
10144          * @param {Roo.data.Record[]} records The array of Records added
10145          * @param {Number} index The index at which the record(s) were added
10146          */
10147         add : true,
10148         /**
10149          * @event remove
10150          * Fires when a Record has been removed from the Store
10151          * @param {Store} this
10152          * @param {Roo.data.Record} record The Record that was removed
10153          * @param {Number} index The index at which the record was removed
10154          */
10155         remove : true,
10156         /**
10157          * @event update
10158          * Fires when a Record has been updated
10159          * @param {Store} this
10160          * @param {Roo.data.Record} record The Record that was updated
10161          * @param {String} operation The update operation being performed.  Value may be one of:
10162          * <pre><code>
10163  Roo.data.Record.EDIT
10164  Roo.data.Record.REJECT
10165  Roo.data.Record.COMMIT
10166          * </code></pre>
10167          */
10168         update : true,
10169         /**
10170          * @event clear
10171          * Fires when the data cache has been cleared.
10172          * @param {Store} this
10173          */
10174         clear : true,
10175         /**
10176          * @event beforeload
10177          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10178          * the load action will be canceled.
10179          * @param {Store} this
10180          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10181          */
10182         beforeload : true,
10183         /**
10184          * @event beforeloadadd
10185          * Fires after a new set of Records has been loaded.
10186          * @param {Store} this
10187          * @param {Roo.data.Record[]} records The Records that were loaded
10188          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10189          */
10190         beforeloadadd : true,
10191         /**
10192          * @event load
10193          * Fires after a new set of Records has been loaded, before they are added to the store.
10194          * @param {Store} this
10195          * @param {Roo.data.Record[]} records The Records that were loaded
10196          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10197          * @params {Object} return from reader
10198          */
10199         load : true,
10200         /**
10201          * @event loadexception
10202          * Fires if an exception occurs in the Proxy during loading.
10203          * Called with the signature of the Proxy's "loadexception" event.
10204          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10205          * 
10206          * @param {Proxy} 
10207          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10208          * @param {Object} load options 
10209          * @param {Object} jsonData from your request (normally this contains the Exception)
10210          */
10211         loadexception : true
10212     });
10213     
10214     if(this.proxy){
10215         this.proxy = Roo.factory(this.proxy, Roo.data);
10216         this.proxy.xmodule = this.xmodule || false;
10217         this.relayEvents(this.proxy,  ["loadexception"]);
10218     }
10219     this.sortToggle = {};
10220     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10221
10222     Roo.data.Store.superclass.constructor.call(this);
10223
10224     if(this.inlineData){
10225         this.loadData(this.inlineData);
10226         delete this.inlineData;
10227     }
10228 };
10229
10230 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10231      /**
10232     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10233     * without a remote query - used by combo/forms at present.
10234     */
10235     
10236     /**
10237     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10238     */
10239     /**
10240     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10241     */
10242     /**
10243     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10244     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10245     */
10246     /**
10247     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10248     * on any HTTP request
10249     */
10250     /**
10251     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10252     */
10253     /**
10254     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10255     */
10256     multiSort: false,
10257     /**
10258     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10259     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10260     */
10261     remoteSort : false,
10262
10263     /**
10264     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10265      * loaded or when a record is removed. (defaults to false).
10266     */
10267     pruneModifiedRecords : false,
10268
10269     // private
10270     lastOptions : null,
10271
10272     /**
10273      * Add Records to the Store and fires the add event.
10274      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10275      */
10276     add : function(records){
10277         records = [].concat(records);
10278         for(var i = 0, len = records.length; i < len; i++){
10279             records[i].join(this);
10280         }
10281         var index = this.data.length;
10282         this.data.addAll(records);
10283         this.fireEvent("add", this, records, index);
10284     },
10285
10286     /**
10287      * Remove a Record from the Store and fires the remove event.
10288      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10289      */
10290     remove : function(record){
10291         var index = this.data.indexOf(record);
10292         this.data.removeAt(index);
10293         if(this.pruneModifiedRecords){
10294             this.modified.remove(record);
10295         }
10296         this.fireEvent("remove", this, record, index);
10297     },
10298
10299     /**
10300      * Remove all Records from the Store and fires the clear event.
10301      */
10302     removeAll : function(){
10303         this.data.clear();
10304         if(this.pruneModifiedRecords){
10305             this.modified = [];
10306         }
10307         this.fireEvent("clear", this);
10308     },
10309
10310     /**
10311      * Inserts Records to the Store at the given index and fires the add event.
10312      * @param {Number} index The start index at which to insert the passed Records.
10313      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10314      */
10315     insert : function(index, records){
10316         records = [].concat(records);
10317         for(var i = 0, len = records.length; i < len; i++){
10318             this.data.insert(index, records[i]);
10319             records[i].join(this);
10320         }
10321         this.fireEvent("add", this, records, index);
10322     },
10323
10324     /**
10325      * Get the index within the cache of the passed Record.
10326      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10327      * @return {Number} The index of the passed Record. Returns -1 if not found.
10328      */
10329     indexOf : function(record){
10330         return this.data.indexOf(record);
10331     },
10332
10333     /**
10334      * Get the index within the cache of the Record with the passed id.
10335      * @param {String} id The id of the Record to find.
10336      * @return {Number} The index of the Record. Returns -1 if not found.
10337      */
10338     indexOfId : function(id){
10339         return this.data.indexOfKey(id);
10340     },
10341
10342     /**
10343      * Get the Record with the specified id.
10344      * @param {String} id The id of the Record to find.
10345      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10346      */
10347     getById : function(id){
10348         return this.data.key(id);
10349     },
10350
10351     /**
10352      * Get the Record at the specified index.
10353      * @param {Number} index The index of the Record to find.
10354      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10355      */
10356     getAt : function(index){
10357         return this.data.itemAt(index);
10358     },
10359
10360     /**
10361      * Returns a range of Records between specified indices.
10362      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10363      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10364      * @return {Roo.data.Record[]} An array of Records
10365      */
10366     getRange : function(start, end){
10367         return this.data.getRange(start, end);
10368     },
10369
10370     // private
10371     storeOptions : function(o){
10372         o = Roo.apply({}, o);
10373         delete o.callback;
10374         delete o.scope;
10375         this.lastOptions = o;
10376     },
10377
10378     /**
10379      * Loads the Record cache from the configured Proxy using the configured Reader.
10380      * <p>
10381      * If using remote paging, then the first load call must specify the <em>start</em>
10382      * and <em>limit</em> properties in the options.params property to establish the initial
10383      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10384      * <p>
10385      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10386      * and this call will return before the new data has been loaded. Perform any post-processing
10387      * in a callback function, or in a "load" event handler.</strong>
10388      * <p>
10389      * @param {Object} options An object containing properties which control loading options:<ul>
10390      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10391      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10392      * passed the following arguments:<ul>
10393      * <li>r : Roo.data.Record[]</li>
10394      * <li>options: Options object from the load call</li>
10395      * <li>success: Boolean success indicator</li></ul></li>
10396      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10397      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10398      * </ul>
10399      */
10400     load : function(options){
10401         options = options || {};
10402         if(this.fireEvent("beforeload", this, options) !== false){
10403             this.storeOptions(options);
10404             var p = Roo.apply(options.params || {}, this.baseParams);
10405             // if meta was not loaded from remote source.. try requesting it.
10406             if (!this.reader.metaFromRemote) {
10407                 p._requestMeta = 1;
10408             }
10409             if(this.sortInfo && this.remoteSort){
10410                 var pn = this.paramNames;
10411                 p[pn["sort"]] = this.sortInfo.field;
10412                 p[pn["dir"]] = this.sortInfo.direction;
10413             }
10414             if (this.multiSort) {
10415                 var pn = this.paramNames;
10416                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10417             }
10418             
10419             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10420         }
10421     },
10422
10423     /**
10424      * Reloads the Record cache from the configured Proxy using the configured Reader and
10425      * the options from the last load operation performed.
10426      * @param {Object} options (optional) An object containing properties which may override the options
10427      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10428      * the most recently used options are reused).
10429      */
10430     reload : function(options){
10431         this.load(Roo.applyIf(options||{}, this.lastOptions));
10432     },
10433
10434     // private
10435     // Called as a callback by the Reader during a load operation.
10436     loadRecords : function(o, options, success){
10437         if(!o || success === false){
10438             if(success !== false){
10439                 this.fireEvent("load", this, [], options, o);
10440             }
10441             if(options.callback){
10442                 options.callback.call(options.scope || this, [], options, false);
10443             }
10444             return;
10445         }
10446         // if data returned failure - throw an exception.
10447         if (o.success === false) {
10448             // show a message if no listener is registered.
10449             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10450                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10451             }
10452             // loadmask wil be hooked into this..
10453             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10454             return;
10455         }
10456         var r = o.records, t = o.totalRecords || r.length;
10457         
10458         this.fireEvent("beforeloadadd", this, r, options, o);
10459         
10460         if(!options || options.add !== true){
10461             if(this.pruneModifiedRecords){
10462                 this.modified = [];
10463             }
10464             for(var i = 0, len = r.length; i < len; i++){
10465                 r[i].join(this);
10466             }
10467             if(this.snapshot){
10468                 this.data = this.snapshot;
10469                 delete this.snapshot;
10470             }
10471             this.data.clear();
10472             this.data.addAll(r);
10473             this.totalLength = t;
10474             this.applySort();
10475             this.fireEvent("datachanged", this);
10476         }else{
10477             this.totalLength = Math.max(t, this.data.length+r.length);
10478             this.add(r);
10479         }
10480         this.fireEvent("load", this, r, options, o);
10481         if(options.callback){
10482             options.callback.call(options.scope || this, r, options, true);
10483         }
10484     },
10485
10486
10487     /**
10488      * Loads data from a passed data block. A Reader which understands the format of the data
10489      * must have been configured in the constructor.
10490      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10491      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10492      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10493      */
10494     loadData : function(o, append){
10495         var r = this.reader.readRecords(o);
10496         this.loadRecords(r, {add: append}, true);
10497     },
10498
10499     /**
10500      * Gets the number of cached records.
10501      * <p>
10502      * <em>If using paging, this may not be the total size of the dataset. If the data object
10503      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10504      * the data set size</em>
10505      */
10506     getCount : function(){
10507         return this.data.length || 0;
10508     },
10509
10510     /**
10511      * Gets the total number of records in the dataset as returned by the server.
10512      * <p>
10513      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10514      * the dataset size</em>
10515      */
10516     getTotalCount : function(){
10517         return this.totalLength || 0;
10518     },
10519
10520     /**
10521      * Returns the sort state of the Store as an object with two properties:
10522      * <pre><code>
10523  field {String} The name of the field by which the Records are sorted
10524  direction {String} The sort order, "ASC" or "DESC"
10525      * </code></pre>
10526      */
10527     getSortState : function(){
10528         return this.sortInfo;
10529     },
10530
10531     // private
10532     applySort : function(){
10533         if(this.sortInfo && !this.remoteSort){
10534             var s = this.sortInfo, f = s.field;
10535             var st = this.fields.get(f).sortType;
10536             var fn = function(r1, r2){
10537                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10538                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10539             };
10540             this.data.sort(s.direction, fn);
10541             if(this.snapshot && this.snapshot != this.data){
10542                 this.snapshot.sort(s.direction, fn);
10543             }
10544         }
10545     },
10546
10547     /**
10548      * Sets the default sort column and order to be used by the next load operation.
10549      * @param {String} fieldName The name of the field to sort by.
10550      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10551      */
10552     setDefaultSort : function(field, dir){
10553         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10554     },
10555
10556     /**
10557      * Sort the Records.
10558      * If remote sorting is used, the sort is performed on the server, and the cache is
10559      * reloaded. If local sorting is used, the cache is sorted internally.
10560      * @param {String} fieldName The name of the field to sort by.
10561      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10562      */
10563     sort : function(fieldName, dir){
10564         var f = this.fields.get(fieldName);
10565         if(!dir){
10566             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10567             
10568             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10569                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10570             }else{
10571                 dir = f.sortDir;
10572             }
10573         }
10574         this.sortToggle[f.name] = dir;
10575         this.sortInfo = {field: f.name, direction: dir};
10576         if(!this.remoteSort){
10577             this.applySort();
10578             this.fireEvent("datachanged", this);
10579         }else{
10580             this.load(this.lastOptions);
10581         }
10582     },
10583
10584     /**
10585      * Calls the specified function for each of the Records in the cache.
10586      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10587      * Returning <em>false</em> aborts and exits the iteration.
10588      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10589      */
10590     each : function(fn, scope){
10591         this.data.each(fn, scope);
10592     },
10593
10594     /**
10595      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10596      * (e.g., during paging).
10597      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10598      */
10599     getModifiedRecords : function(){
10600         return this.modified;
10601     },
10602
10603     // private
10604     createFilterFn : function(property, value, anyMatch){
10605         if(!value.exec){ // not a regex
10606             value = String(value);
10607             if(value.length == 0){
10608                 return false;
10609             }
10610             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10611         }
10612         return function(r){
10613             return value.test(r.data[property]);
10614         };
10615     },
10616
10617     /**
10618      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10619      * @param {String} property A field on your records
10620      * @param {Number} start The record index to start at (defaults to 0)
10621      * @param {Number} end The last record index to include (defaults to length - 1)
10622      * @return {Number} The sum
10623      */
10624     sum : function(property, start, end){
10625         var rs = this.data.items, v = 0;
10626         start = start || 0;
10627         end = (end || end === 0) ? end : rs.length-1;
10628
10629         for(var i = start; i <= end; i++){
10630             v += (rs[i].data[property] || 0);
10631         }
10632         return v;
10633     },
10634
10635     /**
10636      * Filter the records by a specified property.
10637      * @param {String} field A field on your records
10638      * @param {String/RegExp} value Either a string that the field
10639      * should start with or a RegExp to test against the field
10640      * @param {Boolean} anyMatch True to match any part not just the beginning
10641      */
10642     filter : function(property, value, anyMatch){
10643         var fn = this.createFilterFn(property, value, anyMatch);
10644         return fn ? this.filterBy(fn) : this.clearFilter();
10645     },
10646
10647     /**
10648      * Filter by a function. The specified function will be called with each
10649      * record in this data source. If the function returns true the record is included,
10650      * otherwise it is filtered.
10651      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10652      * @param {Object} scope (optional) The scope of the function (defaults to this)
10653      */
10654     filterBy : function(fn, scope){
10655         this.snapshot = this.snapshot || this.data;
10656         this.data = this.queryBy(fn, scope||this);
10657         this.fireEvent("datachanged", this);
10658     },
10659
10660     /**
10661      * Query the records by a specified property.
10662      * @param {String} field A field on your records
10663      * @param {String/RegExp} value Either a string that the field
10664      * should start with or a RegExp to test against the field
10665      * @param {Boolean} anyMatch True to match any part not just the beginning
10666      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10667      */
10668     query : function(property, value, anyMatch){
10669         var fn = this.createFilterFn(property, value, anyMatch);
10670         return fn ? this.queryBy(fn) : this.data.clone();
10671     },
10672
10673     /**
10674      * Query by a function. The specified function will be called with each
10675      * record in this data source. If the function returns true the record is included
10676      * in the results.
10677      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10678      * @param {Object} scope (optional) The scope of the function (defaults to this)
10679       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10680      **/
10681     queryBy : function(fn, scope){
10682         var data = this.snapshot || this.data;
10683         return data.filterBy(fn, scope||this);
10684     },
10685
10686     /**
10687      * Collects unique values for a particular dataIndex from this store.
10688      * @param {String} dataIndex The property to collect
10689      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10690      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10691      * @return {Array} An array of the unique values
10692      **/
10693     collect : function(dataIndex, allowNull, bypassFilter){
10694         var d = (bypassFilter === true && this.snapshot) ?
10695                 this.snapshot.items : this.data.items;
10696         var v, sv, r = [], l = {};
10697         for(var i = 0, len = d.length; i < len; i++){
10698             v = d[i].data[dataIndex];
10699             sv = String(v);
10700             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10701                 l[sv] = true;
10702                 r[r.length] = v;
10703             }
10704         }
10705         return r;
10706     },
10707
10708     /**
10709      * Revert to a view of the Record cache with no filtering applied.
10710      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10711      */
10712     clearFilter : function(suppressEvent){
10713         if(this.snapshot && this.snapshot != this.data){
10714             this.data = this.snapshot;
10715             delete this.snapshot;
10716             if(suppressEvent !== true){
10717                 this.fireEvent("datachanged", this);
10718             }
10719         }
10720     },
10721
10722     // private
10723     afterEdit : function(record){
10724         if(this.modified.indexOf(record) == -1){
10725             this.modified.push(record);
10726         }
10727         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10728     },
10729     
10730     // private
10731     afterReject : function(record){
10732         this.modified.remove(record);
10733         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10734     },
10735
10736     // private
10737     afterCommit : function(record){
10738         this.modified.remove(record);
10739         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10740     },
10741
10742     /**
10743      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10744      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10745      */
10746     commitChanges : function(){
10747         var m = this.modified.slice(0);
10748         this.modified = [];
10749         for(var i = 0, len = m.length; i < len; i++){
10750             m[i].commit();
10751         }
10752     },
10753
10754     /**
10755      * Cancel outstanding changes on all changed records.
10756      */
10757     rejectChanges : function(){
10758         var m = this.modified.slice(0);
10759         this.modified = [];
10760         for(var i = 0, len = m.length; i < len; i++){
10761             m[i].reject();
10762         }
10763     },
10764
10765     onMetaChange : function(meta, rtype, o){
10766         this.recordType = rtype;
10767         this.fields = rtype.prototype.fields;
10768         delete this.snapshot;
10769         this.sortInfo = meta.sortInfo || this.sortInfo;
10770         this.modified = [];
10771         this.fireEvent('metachange', this, this.reader.meta);
10772     },
10773     
10774     moveIndex : function(data, type)
10775     {
10776         var index = this.indexOf(data);
10777         
10778         var newIndex = index + type;
10779         
10780         this.remove(data);
10781         
10782         this.insert(newIndex, data);
10783         
10784     }
10785 });/*
10786  * Based on:
10787  * Ext JS Library 1.1.1
10788  * Copyright(c) 2006-2007, Ext JS, LLC.
10789  *
10790  * Originally Released Under LGPL - original licence link has changed is not relivant.
10791  *
10792  * Fork - LGPL
10793  * <script type="text/javascript">
10794  */
10795
10796 /**
10797  * @class Roo.data.SimpleStore
10798  * @extends Roo.data.Store
10799  * Small helper class to make creating Stores from Array data easier.
10800  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10801  * @cfg {Array} fields An array of field definition objects, or field name strings.
10802  * @cfg {Array} data The multi-dimensional array of data
10803  * @constructor
10804  * @param {Object} config
10805  */
10806 Roo.data.SimpleStore = function(config){
10807     Roo.data.SimpleStore.superclass.constructor.call(this, {
10808         isLocal : true,
10809         reader: new Roo.data.ArrayReader({
10810                 id: config.id
10811             },
10812             Roo.data.Record.create(config.fields)
10813         ),
10814         proxy : new Roo.data.MemoryProxy(config.data)
10815     });
10816     this.load();
10817 };
10818 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10819  * Based on:
10820  * Ext JS Library 1.1.1
10821  * Copyright(c) 2006-2007, Ext JS, LLC.
10822  *
10823  * Originally Released Under LGPL - original licence link has changed is not relivant.
10824  *
10825  * Fork - LGPL
10826  * <script type="text/javascript">
10827  */
10828
10829 /**
10830 /**
10831  * @extends Roo.data.Store
10832  * @class Roo.data.JsonStore
10833  * Small helper class to make creating Stores for JSON data easier. <br/>
10834 <pre><code>
10835 var store = new Roo.data.JsonStore({
10836     url: 'get-images.php',
10837     root: 'images',
10838     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10839 });
10840 </code></pre>
10841  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10842  * JsonReader and HttpProxy (unless inline data is provided).</b>
10843  * @cfg {Array} fields An array of field definition objects, or field name strings.
10844  * @constructor
10845  * @param {Object} config
10846  */
10847 Roo.data.JsonStore = function(c){
10848     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10849         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10850         reader: new Roo.data.JsonReader(c, c.fields)
10851     }));
10852 };
10853 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10854  * Based on:
10855  * Ext JS Library 1.1.1
10856  * Copyright(c) 2006-2007, Ext JS, LLC.
10857  *
10858  * Originally Released Under LGPL - original licence link has changed is not relivant.
10859  *
10860  * Fork - LGPL
10861  * <script type="text/javascript">
10862  */
10863
10864  
10865 Roo.data.Field = function(config){
10866     if(typeof config == "string"){
10867         config = {name: config};
10868     }
10869     Roo.apply(this, config);
10870     
10871     if(!this.type){
10872         this.type = "auto";
10873     }
10874     
10875     var st = Roo.data.SortTypes;
10876     // named sortTypes are supported, here we look them up
10877     if(typeof this.sortType == "string"){
10878         this.sortType = st[this.sortType];
10879     }
10880     
10881     // set default sortType for strings and dates
10882     if(!this.sortType){
10883         switch(this.type){
10884             case "string":
10885                 this.sortType = st.asUCString;
10886                 break;
10887             case "date":
10888                 this.sortType = st.asDate;
10889                 break;
10890             default:
10891                 this.sortType = st.none;
10892         }
10893     }
10894
10895     // define once
10896     var stripRe = /[\$,%]/g;
10897
10898     // prebuilt conversion function for this field, instead of
10899     // switching every time we're reading a value
10900     if(!this.convert){
10901         var cv, dateFormat = this.dateFormat;
10902         switch(this.type){
10903             case "":
10904             case "auto":
10905             case undefined:
10906                 cv = function(v){ return v; };
10907                 break;
10908             case "string":
10909                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10910                 break;
10911             case "int":
10912                 cv = function(v){
10913                     return v !== undefined && v !== null && v !== '' ?
10914                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10915                     };
10916                 break;
10917             case "float":
10918                 cv = function(v){
10919                     return v !== undefined && v !== null && v !== '' ?
10920                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10921                     };
10922                 break;
10923             case "bool":
10924             case "boolean":
10925                 cv = function(v){ return v === true || v === "true" || v == 1; };
10926                 break;
10927             case "date":
10928                 cv = function(v){
10929                     if(!v){
10930                         return '';
10931                     }
10932                     if(v instanceof Date){
10933                         return v;
10934                     }
10935                     if(dateFormat){
10936                         if(dateFormat == "timestamp"){
10937                             return new Date(v*1000);
10938                         }
10939                         return Date.parseDate(v, dateFormat);
10940                     }
10941                     var parsed = Date.parse(v);
10942                     return parsed ? new Date(parsed) : null;
10943                 };
10944              break;
10945             
10946         }
10947         this.convert = cv;
10948     }
10949 };
10950
10951 Roo.data.Field.prototype = {
10952     dateFormat: null,
10953     defaultValue: "",
10954     mapping: null,
10955     sortType : null,
10956     sortDir : "ASC"
10957 };/*
10958  * Based on:
10959  * Ext JS Library 1.1.1
10960  * Copyright(c) 2006-2007, Ext JS, LLC.
10961  *
10962  * Originally Released Under LGPL - original licence link has changed is not relivant.
10963  *
10964  * Fork - LGPL
10965  * <script type="text/javascript">
10966  */
10967  
10968 // Base class for reading structured data from a data source.  This class is intended to be
10969 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10970
10971 /**
10972  * @class Roo.data.DataReader
10973  * Base class for reading structured data from a data source.  This class is intended to be
10974  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10975  */
10976
10977 Roo.data.DataReader = function(meta, recordType){
10978     
10979     this.meta = meta;
10980     
10981     this.recordType = recordType instanceof Array ? 
10982         Roo.data.Record.create(recordType) : recordType;
10983 };
10984
10985 Roo.data.DataReader.prototype = {
10986      /**
10987      * Create an empty record
10988      * @param {Object} data (optional) - overlay some values
10989      * @return {Roo.data.Record} record created.
10990      */
10991     newRow :  function(d) {
10992         var da =  {};
10993         this.recordType.prototype.fields.each(function(c) {
10994             switch( c.type) {
10995                 case 'int' : da[c.name] = 0; break;
10996                 case 'date' : da[c.name] = new Date(); break;
10997                 case 'float' : da[c.name] = 0.0; break;
10998                 case 'boolean' : da[c.name] = false; break;
10999                 default : da[c.name] = ""; break;
11000             }
11001             
11002         });
11003         return new this.recordType(Roo.apply(da, d));
11004     }
11005     
11006 };/*
11007  * Based on:
11008  * Ext JS Library 1.1.1
11009  * Copyright(c) 2006-2007, Ext JS, LLC.
11010  *
11011  * Originally Released Under LGPL - original licence link has changed is not relivant.
11012  *
11013  * Fork - LGPL
11014  * <script type="text/javascript">
11015  */
11016
11017 /**
11018  * @class Roo.data.DataProxy
11019  * @extends Roo.data.Observable
11020  * This class is an abstract base class for implementations which provide retrieval of
11021  * unformatted data objects.<br>
11022  * <p>
11023  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11024  * (of the appropriate type which knows how to parse the data object) to provide a block of
11025  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11026  * <p>
11027  * Custom implementations must implement the load method as described in
11028  * {@link Roo.data.HttpProxy#load}.
11029  */
11030 Roo.data.DataProxy = function(){
11031     this.addEvents({
11032         /**
11033          * @event beforeload
11034          * Fires before a network request is made to retrieve a data object.
11035          * @param {Object} This DataProxy object.
11036          * @param {Object} params The params parameter to the load function.
11037          */
11038         beforeload : true,
11039         /**
11040          * @event load
11041          * Fires before the load method's callback is called.
11042          * @param {Object} This DataProxy object.
11043          * @param {Object} o The data object.
11044          * @param {Object} arg The callback argument object passed to the load function.
11045          */
11046         load : true,
11047         /**
11048          * @event loadexception
11049          * Fires if an Exception occurs during data retrieval.
11050          * @param {Object} This DataProxy object.
11051          * @param {Object} o The data object.
11052          * @param {Object} arg The callback argument object passed to the load function.
11053          * @param {Object} e The Exception.
11054          */
11055         loadexception : true
11056     });
11057     Roo.data.DataProxy.superclass.constructor.call(this);
11058 };
11059
11060 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11061
11062     /**
11063      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11064      */
11065 /*
11066  * Based on:
11067  * Ext JS Library 1.1.1
11068  * Copyright(c) 2006-2007, Ext JS, LLC.
11069  *
11070  * Originally Released Under LGPL - original licence link has changed is not relivant.
11071  *
11072  * Fork - LGPL
11073  * <script type="text/javascript">
11074  */
11075 /**
11076  * @class Roo.data.MemoryProxy
11077  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11078  * to the Reader when its load method is called.
11079  * @constructor
11080  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11081  */
11082 Roo.data.MemoryProxy = function(data){
11083     if (data.data) {
11084         data = data.data;
11085     }
11086     Roo.data.MemoryProxy.superclass.constructor.call(this);
11087     this.data = data;
11088 };
11089
11090 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11091     
11092     /**
11093      * Load data from the requested source (in this case an in-memory
11094      * data object passed to the constructor), read the data object into
11095      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11096      * process that block using the passed callback.
11097      * @param {Object} params This parameter is not used by the MemoryProxy class.
11098      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11099      * object into a block of Roo.data.Records.
11100      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11101      * The function must be passed <ul>
11102      * <li>The Record block object</li>
11103      * <li>The "arg" argument from the load function</li>
11104      * <li>A boolean success indicator</li>
11105      * </ul>
11106      * @param {Object} scope The scope in which to call the callback
11107      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11108      */
11109     load : function(params, reader, callback, scope, arg){
11110         params = params || {};
11111         var result;
11112         try {
11113             result = reader.readRecords(this.data);
11114         }catch(e){
11115             this.fireEvent("loadexception", this, arg, null, e);
11116             callback.call(scope, null, arg, false);
11117             return;
11118         }
11119         callback.call(scope, result, arg, true);
11120     },
11121     
11122     // private
11123     update : function(params, records){
11124         
11125     }
11126 });/*
11127  * Based on:
11128  * Ext JS Library 1.1.1
11129  * Copyright(c) 2006-2007, Ext JS, LLC.
11130  *
11131  * Originally Released Under LGPL - original licence link has changed is not relivant.
11132  *
11133  * Fork - LGPL
11134  * <script type="text/javascript">
11135  */
11136 /**
11137  * @class Roo.data.HttpProxy
11138  * @extends Roo.data.DataProxy
11139  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11140  * configured to reference a certain URL.<br><br>
11141  * <p>
11142  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11143  * from which the running page was served.<br><br>
11144  * <p>
11145  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11146  * <p>
11147  * Be aware that to enable the browser to parse an XML document, the server must set
11148  * the Content-Type header in the HTTP response to "text/xml".
11149  * @constructor
11150  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11151  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11152  * will be used to make the request.
11153  */
11154 Roo.data.HttpProxy = function(conn){
11155     Roo.data.HttpProxy.superclass.constructor.call(this);
11156     // is conn a conn config or a real conn?
11157     this.conn = conn;
11158     this.useAjax = !conn || !conn.events;
11159   
11160 };
11161
11162 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11163     // thse are take from connection...
11164     
11165     /**
11166      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11167      */
11168     /**
11169      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11170      * extra parameters to each request made by this object. (defaults to undefined)
11171      */
11172     /**
11173      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11174      *  to each request made by this object. (defaults to undefined)
11175      */
11176     /**
11177      * @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)
11178      */
11179     /**
11180      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11181      */
11182      /**
11183      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11184      * @type Boolean
11185      */
11186   
11187
11188     /**
11189      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11190      * @type Boolean
11191      */
11192     /**
11193      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11194      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11195      * a finer-grained basis than the DataProxy events.
11196      */
11197     getConnection : function(){
11198         return this.useAjax ? Roo.Ajax : this.conn;
11199     },
11200
11201     /**
11202      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11203      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11204      * process that block using the passed callback.
11205      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11206      * for the request to the remote server.
11207      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11208      * object into a block of Roo.data.Records.
11209      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11210      * The function must be passed <ul>
11211      * <li>The Record block object</li>
11212      * <li>The "arg" argument from the load function</li>
11213      * <li>A boolean success indicator</li>
11214      * </ul>
11215      * @param {Object} scope The scope in which to call the callback
11216      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11217      */
11218     load : function(params, reader, callback, scope, arg){
11219         if(this.fireEvent("beforeload", this, params) !== false){
11220             var  o = {
11221                 params : params || {},
11222                 request: {
11223                     callback : callback,
11224                     scope : scope,
11225                     arg : arg
11226                 },
11227                 reader: reader,
11228                 callback : this.loadResponse,
11229                 scope: this
11230             };
11231             if(this.useAjax){
11232                 Roo.applyIf(o, this.conn);
11233                 if(this.activeRequest){
11234                     Roo.Ajax.abort(this.activeRequest);
11235                 }
11236                 this.activeRequest = Roo.Ajax.request(o);
11237             }else{
11238                 this.conn.request(o);
11239             }
11240         }else{
11241             callback.call(scope||this, null, arg, false);
11242         }
11243     },
11244
11245     // private
11246     loadResponse : function(o, success, response){
11247         delete this.activeRequest;
11248         if(!success){
11249             this.fireEvent("loadexception", this, o, response);
11250             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11251             return;
11252         }
11253         var result;
11254         try {
11255             result = o.reader.read(response);
11256         }catch(e){
11257             this.fireEvent("loadexception", this, o, response, e);
11258             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11259             return;
11260         }
11261         
11262         this.fireEvent("load", this, o, o.request.arg);
11263         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11264     },
11265
11266     // private
11267     update : function(dataSet){
11268
11269     },
11270
11271     // private
11272     updateResponse : function(dataSet){
11273
11274     }
11275 });/*
11276  * Based on:
11277  * Ext JS Library 1.1.1
11278  * Copyright(c) 2006-2007, Ext JS, LLC.
11279  *
11280  * Originally Released Under LGPL - original licence link has changed is not relivant.
11281  *
11282  * Fork - LGPL
11283  * <script type="text/javascript">
11284  */
11285
11286 /**
11287  * @class Roo.data.ScriptTagProxy
11288  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11289  * other than the originating domain of the running page.<br><br>
11290  * <p>
11291  * <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
11292  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11293  * <p>
11294  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11295  * source code that is used as the source inside a &lt;script> tag.<br><br>
11296  * <p>
11297  * In order for the browser to process the returned data, the server must wrap the data object
11298  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11299  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11300  * depending on whether the callback name was passed:
11301  * <p>
11302  * <pre><code>
11303 boolean scriptTag = false;
11304 String cb = request.getParameter("callback");
11305 if (cb != null) {
11306     scriptTag = true;
11307     response.setContentType("text/javascript");
11308 } else {
11309     response.setContentType("application/x-json");
11310 }
11311 Writer out = response.getWriter();
11312 if (scriptTag) {
11313     out.write(cb + "(");
11314 }
11315 out.print(dataBlock.toJsonString());
11316 if (scriptTag) {
11317     out.write(");");
11318 }
11319 </pre></code>
11320  *
11321  * @constructor
11322  * @param {Object} config A configuration object.
11323  */
11324 Roo.data.ScriptTagProxy = function(config){
11325     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11326     Roo.apply(this, config);
11327     this.head = document.getElementsByTagName("head")[0];
11328 };
11329
11330 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11331
11332 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11333     /**
11334      * @cfg {String} url The URL from which to request the data object.
11335      */
11336     /**
11337      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11338      */
11339     timeout : 30000,
11340     /**
11341      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11342      * the server the name of the callback function set up by the load call to process the returned data object.
11343      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11344      * javascript output which calls this named function passing the data object as its only parameter.
11345      */
11346     callbackParam : "callback",
11347     /**
11348      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11349      * name to the request.
11350      */
11351     nocache : true,
11352
11353     /**
11354      * Load data from the configured URL, read the data object into
11355      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11356      * process that block using the passed callback.
11357      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11358      * for the request to the remote server.
11359      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11360      * object into a block of Roo.data.Records.
11361      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11362      * The function must be passed <ul>
11363      * <li>The Record block object</li>
11364      * <li>The "arg" argument from the load function</li>
11365      * <li>A boolean success indicator</li>
11366      * </ul>
11367      * @param {Object} scope The scope in which to call the callback
11368      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11369      */
11370     load : function(params, reader, callback, scope, arg){
11371         if(this.fireEvent("beforeload", this, params) !== false){
11372
11373             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11374
11375             var url = this.url;
11376             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11377             if(this.nocache){
11378                 url += "&_dc=" + (new Date().getTime());
11379             }
11380             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11381             var trans = {
11382                 id : transId,
11383                 cb : "stcCallback"+transId,
11384                 scriptId : "stcScript"+transId,
11385                 params : params,
11386                 arg : arg,
11387                 url : url,
11388                 callback : callback,
11389                 scope : scope,
11390                 reader : reader
11391             };
11392             var conn = this;
11393
11394             window[trans.cb] = function(o){
11395                 conn.handleResponse(o, trans);
11396             };
11397
11398             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11399
11400             if(this.autoAbort !== false){
11401                 this.abort();
11402             }
11403
11404             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11405
11406             var script = document.createElement("script");
11407             script.setAttribute("src", url);
11408             script.setAttribute("type", "text/javascript");
11409             script.setAttribute("id", trans.scriptId);
11410             this.head.appendChild(script);
11411
11412             this.trans = trans;
11413         }else{
11414             callback.call(scope||this, null, arg, false);
11415         }
11416     },
11417
11418     // private
11419     isLoading : function(){
11420         return this.trans ? true : false;
11421     },
11422
11423     /**
11424      * Abort the current server request.
11425      */
11426     abort : function(){
11427         if(this.isLoading()){
11428             this.destroyTrans(this.trans);
11429         }
11430     },
11431
11432     // private
11433     destroyTrans : function(trans, isLoaded){
11434         this.head.removeChild(document.getElementById(trans.scriptId));
11435         clearTimeout(trans.timeoutId);
11436         if(isLoaded){
11437             window[trans.cb] = undefined;
11438             try{
11439                 delete window[trans.cb];
11440             }catch(e){}
11441         }else{
11442             // if hasn't been loaded, wait for load to remove it to prevent script error
11443             window[trans.cb] = function(){
11444                 window[trans.cb] = undefined;
11445                 try{
11446                     delete window[trans.cb];
11447                 }catch(e){}
11448             };
11449         }
11450     },
11451
11452     // private
11453     handleResponse : function(o, trans){
11454         this.trans = false;
11455         this.destroyTrans(trans, true);
11456         var result;
11457         try {
11458             result = trans.reader.readRecords(o);
11459         }catch(e){
11460             this.fireEvent("loadexception", this, o, trans.arg, e);
11461             trans.callback.call(trans.scope||window, null, trans.arg, false);
11462             return;
11463         }
11464         this.fireEvent("load", this, o, trans.arg);
11465         trans.callback.call(trans.scope||window, result, trans.arg, true);
11466     },
11467
11468     // private
11469     handleFailure : function(trans){
11470         this.trans = false;
11471         this.destroyTrans(trans, false);
11472         this.fireEvent("loadexception", this, null, trans.arg);
11473         trans.callback.call(trans.scope||window, null, trans.arg, false);
11474     }
11475 });/*
11476  * Based on:
11477  * Ext JS Library 1.1.1
11478  * Copyright(c) 2006-2007, Ext JS, LLC.
11479  *
11480  * Originally Released Under LGPL - original licence link has changed is not relivant.
11481  *
11482  * Fork - LGPL
11483  * <script type="text/javascript">
11484  */
11485
11486 /**
11487  * @class Roo.data.JsonReader
11488  * @extends Roo.data.DataReader
11489  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11490  * based on mappings in a provided Roo.data.Record constructor.
11491  * 
11492  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11493  * in the reply previously. 
11494  * 
11495  * <p>
11496  * Example code:
11497  * <pre><code>
11498 var RecordDef = Roo.data.Record.create([
11499     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11500     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11501 ]);
11502 var myReader = new Roo.data.JsonReader({
11503     totalProperty: "results",    // The property which contains the total dataset size (optional)
11504     root: "rows",                // The property which contains an Array of row objects
11505     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11506 }, RecordDef);
11507 </code></pre>
11508  * <p>
11509  * This would consume a JSON file like this:
11510  * <pre><code>
11511 { 'results': 2, 'rows': [
11512     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11513     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11514 }
11515 </code></pre>
11516  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11517  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11518  * paged from the remote server.
11519  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11520  * @cfg {String} root name of the property which contains the Array of row objects.
11521  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11522  * @cfg {Array} fields Array of field definition objects
11523  * @constructor
11524  * Create a new JsonReader
11525  * @param {Object} meta Metadata configuration options
11526  * @param {Object} recordType Either an Array of field definition objects,
11527  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11528  */
11529 Roo.data.JsonReader = function(meta, recordType){
11530     
11531     meta = meta || {};
11532     // set some defaults:
11533     Roo.applyIf(meta, {
11534         totalProperty: 'total',
11535         successProperty : 'success',
11536         root : 'data',
11537         id : 'id'
11538     });
11539     
11540     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11541 };
11542 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11543     
11544     /**
11545      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11546      * Used by Store query builder to append _requestMeta to params.
11547      * 
11548      */
11549     metaFromRemote : false,
11550     /**
11551      * This method is only used by a DataProxy which has retrieved data from a remote server.
11552      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11553      * @return {Object} data A data block which is used by an Roo.data.Store object as
11554      * a cache of Roo.data.Records.
11555      */
11556     read : function(response){
11557         var json = response.responseText;
11558        
11559         var o = /* eval:var:o */ eval("("+json+")");
11560         if(!o) {
11561             throw {message: "JsonReader.read: Json object not found"};
11562         }
11563         
11564         if(o.metaData){
11565             
11566             delete this.ef;
11567             this.metaFromRemote = true;
11568             this.meta = o.metaData;
11569             this.recordType = Roo.data.Record.create(o.metaData.fields);
11570             this.onMetaChange(this.meta, this.recordType, o);
11571         }
11572         return this.readRecords(o);
11573     },
11574
11575     // private function a store will implement
11576     onMetaChange : function(meta, recordType, o){
11577
11578     },
11579
11580     /**
11581          * @ignore
11582          */
11583     simpleAccess: function(obj, subsc) {
11584         return obj[subsc];
11585     },
11586
11587         /**
11588          * @ignore
11589          */
11590     getJsonAccessor: function(){
11591         var re = /[\[\.]/;
11592         return function(expr) {
11593             try {
11594                 return(re.test(expr))
11595                     ? new Function("obj", "return obj." + expr)
11596                     : function(obj){
11597                         return obj[expr];
11598                     };
11599             } catch(e){}
11600             return Roo.emptyFn;
11601         };
11602     }(),
11603
11604     /**
11605      * Create a data block containing Roo.data.Records from an XML document.
11606      * @param {Object} o An object which contains an Array of row objects in the property specified
11607      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11608      * which contains the total size of the dataset.
11609      * @return {Object} data A data block which is used by an Roo.data.Store object as
11610      * a cache of Roo.data.Records.
11611      */
11612     readRecords : function(o){
11613         /**
11614          * After any data loads, the raw JSON data is available for further custom processing.
11615          * @type Object
11616          */
11617         this.o = o;
11618         var s = this.meta, Record = this.recordType,
11619             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11620
11621 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11622         if (!this.ef) {
11623             if(s.totalProperty) {
11624                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11625                 }
11626                 if(s.successProperty) {
11627                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11628                 }
11629                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11630                 if (s.id) {
11631                         var g = this.getJsonAccessor(s.id);
11632                         this.getId = function(rec) {
11633                                 var r = g(rec);  
11634                                 return (r === undefined || r === "") ? null : r;
11635                         };
11636                 } else {
11637                         this.getId = function(){return null;};
11638                 }
11639             this.ef = [];
11640             for(var jj = 0; jj < fl; jj++){
11641                 f = fi[jj];
11642                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11643                 this.ef[jj] = this.getJsonAccessor(map);
11644             }
11645         }
11646
11647         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11648         if(s.totalProperty){
11649             var vt = parseInt(this.getTotal(o), 10);
11650             if(!isNaN(vt)){
11651                 totalRecords = vt;
11652             }
11653         }
11654         if(s.successProperty){
11655             var vs = this.getSuccess(o);
11656             if(vs === false || vs === 'false'){
11657                 success = false;
11658             }
11659         }
11660         var records = [];
11661         for(var i = 0; i < c; i++){
11662                 var n = root[i];
11663             var values = {};
11664             var id = this.getId(n);
11665             for(var j = 0; j < fl; j++){
11666                 f = fi[j];
11667             var v = this.ef[j](n);
11668             if (!f.convert) {
11669                 Roo.log('missing convert for ' + f.name);
11670                 Roo.log(f);
11671                 continue;
11672             }
11673             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11674             }
11675             var record = new Record(values, id);
11676             record.json = n;
11677             records[i] = record;
11678         }
11679         return {
11680             raw : o,
11681             success : success,
11682             records : records,
11683             totalRecords : totalRecords
11684         };
11685     }
11686 });/*
11687  * Based on:
11688  * Ext JS Library 1.1.1
11689  * Copyright(c) 2006-2007, Ext JS, LLC.
11690  *
11691  * Originally Released Under LGPL - original licence link has changed is not relivant.
11692  *
11693  * Fork - LGPL
11694  * <script type="text/javascript">
11695  */
11696
11697 /**
11698  * @class Roo.data.ArrayReader
11699  * @extends Roo.data.DataReader
11700  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11701  * Each element of that Array represents a row of data fields. The
11702  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11703  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11704  * <p>
11705  * Example code:.
11706  * <pre><code>
11707 var RecordDef = Roo.data.Record.create([
11708     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11709     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11710 ]);
11711 var myReader = new Roo.data.ArrayReader({
11712     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11713 }, RecordDef);
11714 </code></pre>
11715  * <p>
11716  * This would consume an Array like this:
11717  * <pre><code>
11718 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11719   </code></pre>
11720  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11721  * @constructor
11722  * Create a new JsonReader
11723  * @param {Object} meta Metadata configuration options.
11724  * @param {Object} recordType Either an Array of field definition objects
11725  * as specified to {@link Roo.data.Record#create},
11726  * or an {@link Roo.data.Record} object
11727  * created using {@link Roo.data.Record#create}.
11728  */
11729 Roo.data.ArrayReader = function(meta, recordType){
11730     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11731 };
11732
11733 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11734     /**
11735      * Create a data block containing Roo.data.Records from an XML document.
11736      * @param {Object} o An Array of row objects which represents the dataset.
11737      * @return {Object} data A data block which is used by an Roo.data.Store object as
11738      * a cache of Roo.data.Records.
11739      */
11740     readRecords : function(o){
11741         var sid = this.meta ? this.meta.id : null;
11742         var recordType = this.recordType, fields = recordType.prototype.fields;
11743         var records = [];
11744         var root = o;
11745             for(var i = 0; i < root.length; i++){
11746                     var n = root[i];
11747                 var values = {};
11748                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11749                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11750                 var f = fields.items[j];
11751                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11752                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11753                 v = f.convert(v);
11754                 values[f.name] = v;
11755             }
11756                 var record = new recordType(values, id);
11757                 record.json = n;
11758                 records[records.length] = record;
11759             }
11760             return {
11761                 records : records,
11762                 totalRecords : records.length
11763             };
11764     }
11765 });/*
11766  * - LGPL
11767  * * 
11768  */
11769
11770 /**
11771  * @class Roo.bootstrap.ComboBox
11772  * @extends Roo.bootstrap.TriggerField
11773  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11774  * @cfg {Boolean} append (true|false) default false
11775  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11776  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11777  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11778  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11779  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11780  * @cfg {Boolean} animate default true
11781  * @cfg {Boolean} emptyResultText only for touch device
11782  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11783  * @constructor
11784  * Create a new ComboBox.
11785  * @param {Object} config Configuration options
11786  */
11787 Roo.bootstrap.ComboBox = function(config){
11788     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11789     this.addEvents({
11790         /**
11791          * @event expand
11792          * Fires when the dropdown list is expanded
11793              * @param {Roo.bootstrap.ComboBox} combo This combo box
11794              */
11795         'expand' : true,
11796         /**
11797          * @event collapse
11798          * Fires when the dropdown list is collapsed
11799              * @param {Roo.bootstrap.ComboBox} combo This combo box
11800              */
11801         'collapse' : true,
11802         /**
11803          * @event beforeselect
11804          * Fires before a list item is selected. Return false to cancel the selection.
11805              * @param {Roo.bootstrap.ComboBox} combo This combo box
11806              * @param {Roo.data.Record} record The data record returned from the underlying store
11807              * @param {Number} index The index of the selected item in the dropdown list
11808              */
11809         'beforeselect' : true,
11810         /**
11811          * @event select
11812          * Fires when a list item is selected
11813              * @param {Roo.bootstrap.ComboBox} combo This combo box
11814              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11815              * @param {Number} index The index of the selected item in the dropdown list
11816              */
11817         'select' : true,
11818         /**
11819          * @event beforequery
11820          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11821          * The event object passed has these properties:
11822              * @param {Roo.bootstrap.ComboBox} combo This combo box
11823              * @param {String} query The query
11824              * @param {Boolean} forceAll true to force "all" query
11825              * @param {Boolean} cancel true to cancel the query
11826              * @param {Object} e The query event object
11827              */
11828         'beforequery': true,
11829          /**
11830          * @event add
11831          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11832              * @param {Roo.bootstrap.ComboBox} combo This combo box
11833              */
11834         'add' : true,
11835         /**
11836          * @event edit
11837          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11838              * @param {Roo.bootstrap.ComboBox} combo This combo box
11839              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11840              */
11841         'edit' : true,
11842         /**
11843          * @event remove
11844          * Fires when the remove value from the combobox array
11845              * @param {Roo.bootstrap.ComboBox} combo This combo box
11846              */
11847         'remove' : true,
11848         /**
11849          * @event afterremove
11850          * Fires when the remove value from the combobox array
11851              * @param {Roo.bootstrap.ComboBox} combo This combo box
11852              */
11853         'afterremove' : true,
11854         /**
11855          * @event specialfilter
11856          * Fires when specialfilter
11857             * @param {Roo.bootstrap.ComboBox} combo This combo box
11858             */
11859         'specialfilter' : true,
11860         /**
11861          * @event tick
11862          * Fires when tick the element
11863             * @param {Roo.bootstrap.ComboBox} combo This combo box
11864             */
11865         'tick' : true,
11866         /**
11867          * @event touchviewdisplay
11868          * Fires when touch view require special display (default is using displayField)
11869             * @param {Roo.bootstrap.ComboBox} combo This combo box
11870             * @param {Object} cfg set html .
11871             */
11872         'touchviewdisplay' : true
11873         
11874     });
11875     
11876     this.item = [];
11877     this.tickItems = [];
11878     
11879     this.selectedIndex = -1;
11880     if(this.mode == 'local'){
11881         if(config.queryDelay === undefined){
11882             this.queryDelay = 10;
11883         }
11884         if(config.minChars === undefined){
11885             this.minChars = 0;
11886         }
11887     }
11888 };
11889
11890 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11891      
11892     /**
11893      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11894      * rendering into an Roo.Editor, defaults to false)
11895      */
11896     /**
11897      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11898      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11899      */
11900     /**
11901      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11902      */
11903     /**
11904      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11905      * the dropdown list (defaults to undefined, with no header element)
11906      */
11907
11908      /**
11909      * @cfg {String/Roo.Template} tpl The template to use to render the output
11910      */
11911      
11912      /**
11913      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11914      */
11915     listWidth: undefined,
11916     /**
11917      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11918      * mode = 'remote' or 'text' if mode = 'local')
11919      */
11920     displayField: undefined,
11921     
11922     /**
11923      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11924      * mode = 'remote' or 'value' if mode = 'local'). 
11925      * Note: use of a valueField requires the user make a selection
11926      * in order for a value to be mapped.
11927      */
11928     valueField: undefined,
11929     
11930     
11931     /**
11932      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11933      * field's data value (defaults to the underlying DOM element's name)
11934      */
11935     hiddenName: undefined,
11936     /**
11937      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11938      */
11939     listClass: '',
11940     /**
11941      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11942      */
11943     selectedClass: 'active',
11944     
11945     /**
11946      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11947      */
11948     shadow:'sides',
11949     /**
11950      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11951      * anchor positions (defaults to 'tl-bl')
11952      */
11953     listAlign: 'tl-bl?',
11954     /**
11955      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11956      */
11957     maxHeight: 300,
11958     /**
11959      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11960      * query specified by the allQuery config option (defaults to 'query')
11961      */
11962     triggerAction: 'query',
11963     /**
11964      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11965      * (defaults to 4, does not apply if editable = false)
11966      */
11967     minChars : 4,
11968     /**
11969      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11970      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11971      */
11972     typeAhead: false,
11973     /**
11974      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11975      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11976      */
11977     queryDelay: 500,
11978     /**
11979      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11980      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11981      */
11982     pageSize: 0,
11983     /**
11984      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11985      * when editable = true (defaults to false)
11986      */
11987     selectOnFocus:false,
11988     /**
11989      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11990      */
11991     queryParam: 'query',
11992     /**
11993      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11994      * when mode = 'remote' (defaults to 'Loading...')
11995      */
11996     loadingText: 'Loading...',
11997     /**
11998      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11999      */
12000     resizable: false,
12001     /**
12002      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12003      */
12004     handleHeight : 8,
12005     /**
12006      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12007      * traditional select (defaults to true)
12008      */
12009     editable: true,
12010     /**
12011      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12012      */
12013     allQuery: '',
12014     /**
12015      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12016      */
12017     mode: 'remote',
12018     /**
12019      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12020      * listWidth has a higher value)
12021      */
12022     minListWidth : 70,
12023     /**
12024      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12025      * allow the user to set arbitrary text into the field (defaults to false)
12026      */
12027     forceSelection:false,
12028     /**
12029      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12030      * if typeAhead = true (defaults to 250)
12031      */
12032     typeAheadDelay : 250,
12033     /**
12034      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12035      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12036      */
12037     valueNotFoundText : undefined,
12038     /**
12039      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12040      */
12041     blockFocus : false,
12042     
12043     /**
12044      * @cfg {Boolean} disableClear Disable showing of clear button.
12045      */
12046     disableClear : false,
12047     /**
12048      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12049      */
12050     alwaysQuery : false,
12051     
12052     /**
12053      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12054      */
12055     multiple : false,
12056     
12057     /**
12058      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12059      */
12060     invalidClass : "has-warning",
12061     
12062     /**
12063      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12064      */
12065     validClass : "has-success",
12066     
12067     /**
12068      * @cfg {Boolean} specialFilter (true|false) special filter default false
12069      */
12070     specialFilter : false,
12071     
12072     /**
12073      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12074      */
12075     mobileTouchView : true,
12076     
12077     //private
12078     addicon : false,
12079     editicon: false,
12080     
12081     page: 0,
12082     hasQuery: false,
12083     append: false,
12084     loadNext: false,
12085     autoFocus : true,
12086     tickable : false,
12087     btnPosition : 'right',
12088     triggerList : true,
12089     showToggleBtn : true,
12090     animate : true,
12091     emptyResultText: 'Empty',
12092     triggerText : 'Select',
12093     
12094     // element that contains real text value.. (when hidden is used..)
12095     
12096     getAutoCreate : function()
12097     {
12098         var cfg = false;
12099         
12100         /*
12101          * Touch Devices
12102          */
12103         
12104         if(Roo.isTouch && this.mobileTouchView){
12105             cfg = this.getAutoCreateTouchView();
12106             return cfg;;
12107         }
12108         
12109         /*
12110          *  Normal ComboBox
12111          */
12112         if(!this.tickable){
12113             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12114             return cfg;
12115         }
12116         
12117         /*
12118          *  ComboBox with tickable selections
12119          */
12120              
12121         var align = this.labelAlign || this.parentLabelAlign();
12122         
12123         cfg = {
12124             cls : 'form-group roo-combobox-tickable' //input-group
12125         };
12126         
12127         var buttons = {
12128             tag : 'div',
12129             cls : 'tickable-buttons',
12130             cn : [
12131                 {
12132                     tag : 'button',
12133                     type : 'button',
12134                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12135                     html : this.triggerText
12136                 },
12137                 {
12138                     tag : 'button',
12139                     type : 'button',
12140                     name : 'ok',
12141                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12142                     html : 'Done'
12143                 },
12144                 {
12145                     tag : 'button',
12146                     type : 'button',
12147                     name : 'cancel',
12148                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12149                     html : 'Cancel'
12150                 }
12151             ]
12152         };
12153         
12154         if(this.editable){
12155             buttons.cn.unshift({
12156                 tag: 'input',
12157                 cls: 'roo-select2-search-field-input'
12158             });
12159         }
12160         
12161         var _this = this;
12162         
12163         Roo.each(buttons.cn, function(c){
12164             if (_this.size) {
12165                 c.cls += ' btn-' + _this.size;
12166             }
12167
12168             if (_this.disabled) {
12169                 c.disabled = true;
12170             }
12171         });
12172         
12173         var box = {
12174             tag: 'div',
12175             cn: [
12176                 {
12177                     tag: 'input',
12178                     type : 'hidden',
12179                     cls: 'form-hidden-field'
12180                 },
12181                 {
12182                     tag: 'ul',
12183                     cls: 'roo-select2-choices',
12184                     cn:[
12185                         {
12186                             tag: 'li',
12187                             cls: 'roo-select2-search-field',
12188                             cn: [
12189
12190                                 buttons
12191                             ]
12192                         }
12193                     ]
12194                 }
12195             ]
12196         };
12197         
12198         var combobox = {
12199             cls: 'roo-select2-container input-group roo-select2-container-multi',
12200             cn: [
12201                 box
12202 //                {
12203 //                    tag: 'ul',
12204 //                    cls: 'typeahead typeahead-long dropdown-menu',
12205 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12206 //                }
12207             ]
12208         };
12209         
12210         if(this.hasFeedback && !this.allowBlank){
12211             
12212             var feedback = {
12213                 tag: 'span',
12214                 cls: 'glyphicon form-control-feedback'
12215             };
12216
12217             combobox.cn.push(feedback);
12218         }
12219         
12220         if (align ==='left' && this.fieldLabel.length) {
12221             
12222 //                Roo.log("left and has label");
12223                 cfg.cn = [
12224                     
12225                     {
12226                         tag: 'label',
12227                         'for' :  id,
12228                         cls : 'control-label col-sm-' + this.labelWidth,
12229                         html : this.fieldLabel
12230                         
12231                     },
12232                     {
12233                         cls : "col-sm-" + (12 - this.labelWidth), 
12234                         cn: [
12235                             combobox
12236                         ]
12237                     }
12238                     
12239                 ];
12240         } else if ( this.fieldLabel.length) {
12241 //                Roo.log(" label");
12242                  cfg.cn = [
12243                    
12244                     {
12245                         tag: 'label',
12246                         //cls : 'input-group-addon',
12247                         html : this.fieldLabel
12248                         
12249                     },
12250                     
12251                     combobox
12252                     
12253                 ];
12254
12255         } else {
12256             
12257 //                Roo.log(" no label && no align");
12258                 cfg = combobox
12259                      
12260                 
12261         }
12262          
12263         var settings=this;
12264         ['xs','sm','md','lg'].map(function(size){
12265             if (settings[size]) {
12266                 cfg.cls += ' col-' + size + '-' + settings[size];
12267             }
12268         });
12269         
12270         return cfg;
12271         
12272     },
12273     
12274     _initEventsCalled : false,
12275     
12276     // private
12277     initEvents: function()
12278     {
12279         
12280         if (this._initEventsCalled) { // as we call render... prevent looping...
12281             return;
12282         }
12283         this._initEventsCalled = true;
12284         
12285         if (!this.store) {
12286             throw "can not find store for combo";
12287         }
12288         
12289         this.store = Roo.factory(this.store, Roo.data);
12290         
12291         // if we are building from html. then this element is so complex, that we can not really
12292         // use the rendered HTML.
12293         // so we have to trash and replace the previous code.
12294         if (Roo.XComponent.build_from_html) {
12295             
12296             // remove this element....
12297             var e = this.el.dom, k=0;
12298             while (e ) { e = e.previousSibling;  ++k;}
12299
12300             this.el.remove();
12301             
12302             this.el=false;
12303             this.rendered = false;
12304             
12305             this.render(this.parent().getChildContainer(true), k);
12306             
12307             
12308             
12309         }
12310         
12311         
12312         /*
12313          * Touch Devices
12314          */
12315         
12316         if(Roo.isTouch && this.mobileTouchView){
12317             this.initTouchView();
12318             return;
12319         }
12320         
12321         if(this.tickable){
12322             this.initTickableEvents();
12323             return;
12324         }
12325         
12326         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12327         
12328         if(this.hiddenName){
12329             
12330             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12331             
12332             this.hiddenField.dom.value =
12333                 this.hiddenValue !== undefined ? this.hiddenValue :
12334                 this.value !== undefined ? this.value : '';
12335
12336             // prevent input submission
12337             this.el.dom.removeAttribute('name');
12338             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12339              
12340              
12341         }
12342         //if(Roo.isGecko){
12343         //    this.el.dom.setAttribute('autocomplete', 'off');
12344         //}
12345         
12346         var cls = 'x-combo-list';
12347         
12348         //this.list = new Roo.Layer({
12349         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12350         //});
12351         
12352         var _this = this;
12353         
12354         (function(){
12355             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12356             _this.list.setWidth(lw);
12357         }).defer(100);
12358         
12359         this.list.on('mouseover', this.onViewOver, this);
12360         this.list.on('mousemove', this.onViewMove, this);
12361         
12362         this.list.on('scroll', this.onViewScroll, this);
12363         
12364         /*
12365         this.list.swallowEvent('mousewheel');
12366         this.assetHeight = 0;
12367
12368         if(this.title){
12369             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12370             this.assetHeight += this.header.getHeight();
12371         }
12372
12373         this.innerList = this.list.createChild({cls:cls+'-inner'});
12374         this.innerList.on('mouseover', this.onViewOver, this);
12375         this.innerList.on('mousemove', this.onViewMove, this);
12376         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12377         
12378         if(this.allowBlank && !this.pageSize && !this.disableClear){
12379             this.footer = this.list.createChild({cls:cls+'-ft'});
12380             this.pageTb = new Roo.Toolbar(this.footer);
12381            
12382         }
12383         if(this.pageSize){
12384             this.footer = this.list.createChild({cls:cls+'-ft'});
12385             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12386                     {pageSize: this.pageSize});
12387             
12388         }
12389         
12390         if (this.pageTb && this.allowBlank && !this.disableClear) {
12391             var _this = this;
12392             this.pageTb.add(new Roo.Toolbar.Fill(), {
12393                 cls: 'x-btn-icon x-btn-clear',
12394                 text: '&#160;',
12395                 handler: function()
12396                 {
12397                     _this.collapse();
12398                     _this.clearValue();
12399                     _this.onSelect(false, -1);
12400                 }
12401             });
12402         }
12403         if (this.footer) {
12404             this.assetHeight += this.footer.getHeight();
12405         }
12406         */
12407             
12408         if(!this.tpl){
12409             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12410         }
12411
12412         this.view = new Roo.View(this.list, this.tpl, {
12413             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12414         });
12415         //this.view.wrapEl.setDisplayed(false);
12416         this.view.on('click', this.onViewClick, this);
12417         
12418         
12419         
12420         this.store.on('beforeload', this.onBeforeLoad, this);
12421         this.store.on('load', this.onLoad, this);
12422         this.store.on('loadexception', this.onLoadException, this);
12423         /*
12424         if(this.resizable){
12425             this.resizer = new Roo.Resizable(this.list,  {
12426                pinned:true, handles:'se'
12427             });
12428             this.resizer.on('resize', function(r, w, h){
12429                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12430                 this.listWidth = w;
12431                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12432                 this.restrictHeight();
12433             }, this);
12434             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12435         }
12436         */
12437         if(!this.editable){
12438             this.editable = true;
12439             this.setEditable(false);
12440         }
12441         
12442         /*
12443         
12444         if (typeof(this.events.add.listeners) != 'undefined') {
12445             
12446             this.addicon = this.wrap.createChild(
12447                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12448        
12449             this.addicon.on('click', function(e) {
12450                 this.fireEvent('add', this);
12451             }, this);
12452         }
12453         if (typeof(this.events.edit.listeners) != 'undefined') {
12454             
12455             this.editicon = this.wrap.createChild(
12456                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12457             if (this.addicon) {
12458                 this.editicon.setStyle('margin-left', '40px');
12459             }
12460             this.editicon.on('click', function(e) {
12461                 
12462                 // we fire even  if inothing is selected..
12463                 this.fireEvent('edit', this, this.lastData );
12464                 
12465             }, this);
12466         }
12467         */
12468         
12469         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12470             "up" : function(e){
12471                 this.inKeyMode = true;
12472                 this.selectPrev();
12473             },
12474
12475             "down" : function(e){
12476                 if(!this.isExpanded()){
12477                     this.onTriggerClick();
12478                 }else{
12479                     this.inKeyMode = true;
12480                     this.selectNext();
12481                 }
12482             },
12483
12484             "enter" : function(e){
12485 //                this.onViewClick();
12486                 //return true;
12487                 this.collapse();
12488                 
12489                 if(this.fireEvent("specialkey", this, e)){
12490                     this.onViewClick(false);
12491                 }
12492                 
12493                 return true;
12494             },
12495
12496             "esc" : function(e){
12497                 this.collapse();
12498             },
12499
12500             "tab" : function(e){
12501                 this.collapse();
12502                 
12503                 if(this.fireEvent("specialkey", this, e)){
12504                     this.onViewClick(false);
12505                 }
12506                 
12507                 return true;
12508             },
12509
12510             scope : this,
12511
12512             doRelay : function(foo, bar, hname){
12513                 if(hname == 'down' || this.scope.isExpanded()){
12514                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12515                 }
12516                 return true;
12517             },
12518
12519             forceKeyDown: true
12520         });
12521         
12522         
12523         this.queryDelay = Math.max(this.queryDelay || 10,
12524                 this.mode == 'local' ? 10 : 250);
12525         
12526         
12527         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12528         
12529         if(this.typeAhead){
12530             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12531         }
12532         if(this.editable !== false){
12533             this.inputEl().on("keyup", this.onKeyUp, this);
12534         }
12535         if(this.forceSelection){
12536             this.inputEl().on('blur', this.doForce, this);
12537         }
12538         
12539         if(this.multiple){
12540             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12541             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12542         }
12543     },
12544     
12545     initTickableEvents: function()
12546     {   
12547         this.createList();
12548         
12549         if(this.hiddenName){
12550             
12551             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12552             
12553             this.hiddenField.dom.value =
12554                 this.hiddenValue !== undefined ? this.hiddenValue :
12555                 this.value !== undefined ? this.value : '';
12556
12557             // prevent input submission
12558             this.el.dom.removeAttribute('name');
12559             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12560              
12561              
12562         }
12563         
12564 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12565         
12566         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12567         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12568         if(this.triggerList){
12569             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12570         }
12571          
12572         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12573         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12574         
12575         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12576         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12577         
12578         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12579         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12580         
12581         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12582         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12583         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12584         
12585         this.okBtn.hide();
12586         this.cancelBtn.hide();
12587         
12588         var _this = this;
12589         
12590         (function(){
12591             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12592             _this.list.setWidth(lw);
12593         }).defer(100);
12594         
12595         this.list.on('mouseover', this.onViewOver, this);
12596         this.list.on('mousemove', this.onViewMove, this);
12597         
12598         this.list.on('scroll', this.onViewScroll, this);
12599         
12600         if(!this.tpl){
12601             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>';
12602         }
12603
12604         this.view = new Roo.View(this.list, this.tpl, {
12605             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12606         });
12607         
12608         //this.view.wrapEl.setDisplayed(false);
12609         this.view.on('click', this.onViewClick, this);
12610         
12611         
12612         
12613         this.store.on('beforeload', this.onBeforeLoad, this);
12614         this.store.on('load', this.onLoad, this);
12615         this.store.on('loadexception', this.onLoadException, this);
12616         
12617         if(this.editable){
12618             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12619                 "up" : function(e){
12620                     this.inKeyMode = true;
12621                     this.selectPrev();
12622                 },
12623
12624                 "down" : function(e){
12625                     this.inKeyMode = true;
12626                     this.selectNext();
12627                 },
12628
12629                 "enter" : function(e){
12630                     if(this.fireEvent("specialkey", this, e)){
12631                         this.onViewClick(false);
12632                     }
12633                     
12634                     return true;
12635                 },
12636
12637                 "esc" : function(e){
12638                     this.onTickableFooterButtonClick(e, false, false);
12639                 },
12640
12641                 "tab" : function(e){
12642                     this.fireEvent("specialkey", this, e);
12643                     
12644                     this.onTickableFooterButtonClick(e, false, false);
12645                     
12646                     return true;
12647                 },
12648
12649                 scope : this,
12650
12651                 doRelay : function(e, fn, key){
12652                     if(this.scope.isExpanded()){
12653                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12654                     }
12655                     return true;
12656                 },
12657
12658                 forceKeyDown: true
12659             });
12660         }
12661         
12662         this.queryDelay = Math.max(this.queryDelay || 10,
12663                 this.mode == 'local' ? 10 : 250);
12664         
12665         
12666         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12667         
12668         if(this.typeAhead){
12669             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12670         }
12671         
12672         if(this.editable !== false){
12673             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12674         }
12675         
12676     },
12677
12678     onDestroy : function(){
12679         if(this.view){
12680             this.view.setStore(null);
12681             this.view.el.removeAllListeners();
12682             this.view.el.remove();
12683             this.view.purgeListeners();
12684         }
12685         if(this.list){
12686             this.list.dom.innerHTML  = '';
12687         }
12688         
12689         if(this.store){
12690             this.store.un('beforeload', this.onBeforeLoad, this);
12691             this.store.un('load', this.onLoad, this);
12692             this.store.un('loadexception', this.onLoadException, this);
12693         }
12694         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12695     },
12696
12697     // private
12698     fireKey : function(e){
12699         if(e.isNavKeyPress() && !this.list.isVisible()){
12700             this.fireEvent("specialkey", this, e);
12701         }
12702     },
12703
12704     // private
12705     onResize: function(w, h){
12706 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12707 //        
12708 //        if(typeof w != 'number'){
12709 //            // we do not handle it!?!?
12710 //            return;
12711 //        }
12712 //        var tw = this.trigger.getWidth();
12713 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12714 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12715 //        var x = w - tw;
12716 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12717 //            
12718 //        //this.trigger.setStyle('left', x+'px');
12719 //        
12720 //        if(this.list && this.listWidth === undefined){
12721 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12722 //            this.list.setWidth(lw);
12723 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12724 //        }
12725         
12726     
12727         
12728     },
12729
12730     /**
12731      * Allow or prevent the user from directly editing the field text.  If false is passed,
12732      * the user will only be able to select from the items defined in the dropdown list.  This method
12733      * is the runtime equivalent of setting the 'editable' config option at config time.
12734      * @param {Boolean} value True to allow the user to directly edit the field text
12735      */
12736     setEditable : function(value){
12737         if(value == this.editable){
12738             return;
12739         }
12740         this.editable = value;
12741         if(!value){
12742             this.inputEl().dom.setAttribute('readOnly', true);
12743             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12744             this.inputEl().addClass('x-combo-noedit');
12745         }else{
12746             this.inputEl().dom.setAttribute('readOnly', false);
12747             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12748             this.inputEl().removeClass('x-combo-noedit');
12749         }
12750     },
12751
12752     // private
12753     
12754     onBeforeLoad : function(combo,opts){
12755         if(!this.hasFocus){
12756             return;
12757         }
12758          if (!opts.add) {
12759             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12760          }
12761         this.restrictHeight();
12762         this.selectedIndex = -1;
12763     },
12764
12765     // private
12766     onLoad : function(){
12767         
12768         this.hasQuery = false;
12769         
12770         if(!this.hasFocus){
12771             return;
12772         }
12773         
12774         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12775             this.loading.hide();
12776         }
12777              
12778         if(this.store.getCount() > 0){
12779             this.expand();
12780             this.restrictHeight();
12781             if(this.lastQuery == this.allQuery){
12782                 if(this.editable && !this.tickable){
12783                     this.inputEl().dom.select();
12784                 }
12785                 
12786                 if(
12787                     !this.selectByValue(this.value, true) &&
12788                     this.autoFocus && 
12789                     (
12790                         !this.store.lastOptions ||
12791                         typeof(this.store.lastOptions.add) == 'undefined' || 
12792                         this.store.lastOptions.add != true
12793                     )
12794                 ){
12795                     this.select(0, true);
12796                 }
12797             }else{
12798                 if(this.autoFocus){
12799                     this.selectNext();
12800                 }
12801                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12802                     this.taTask.delay(this.typeAheadDelay);
12803                 }
12804             }
12805         }else{
12806             this.onEmptyResults();
12807         }
12808         
12809         //this.el.focus();
12810     },
12811     // private
12812     onLoadException : function()
12813     {
12814         this.hasQuery = false;
12815         
12816         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12817             this.loading.hide();
12818         }
12819         
12820         if(this.tickable && this.editable){
12821             return;
12822         }
12823         
12824         this.collapse();
12825         // only causes errors at present
12826         //Roo.log(this.store.reader.jsonData);
12827         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12828             // fixme
12829             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12830         //}
12831         
12832         
12833     },
12834     // private
12835     onTypeAhead : function(){
12836         if(this.store.getCount() > 0){
12837             var r = this.store.getAt(0);
12838             var newValue = r.data[this.displayField];
12839             var len = newValue.length;
12840             var selStart = this.getRawValue().length;
12841             
12842             if(selStart != len){
12843                 this.setRawValue(newValue);
12844                 this.selectText(selStart, newValue.length);
12845             }
12846         }
12847     },
12848
12849     // private
12850     onSelect : function(record, index){
12851         
12852         if(this.fireEvent('beforeselect', this, record, index) !== false){
12853         
12854             this.setFromData(index > -1 ? record.data : false);
12855             
12856             this.collapse();
12857             this.fireEvent('select', this, record, index);
12858         }
12859     },
12860
12861     /**
12862      * Returns the currently selected field value or empty string if no value is set.
12863      * @return {String} value The selected value
12864      */
12865     getValue : function(){
12866         
12867         if(this.multiple){
12868             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12869         }
12870         
12871         if(this.valueField){
12872             return typeof this.value != 'undefined' ? this.value : '';
12873         }else{
12874             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12875         }
12876     },
12877
12878     /**
12879      * Clears any text/value currently set in the field
12880      */
12881     clearValue : function(){
12882         if(this.hiddenField){
12883             this.hiddenField.dom.value = '';
12884         }
12885         this.value = '';
12886         this.setRawValue('');
12887         this.lastSelectionText = '';
12888         this.lastData = false;
12889         
12890         var close = this.closeTriggerEl();
12891         
12892         if(close){
12893             close.hide();
12894         }
12895         
12896     },
12897
12898     /**
12899      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12900      * will be displayed in the field.  If the value does not match the data value of an existing item,
12901      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12902      * Otherwise the field will be blank (although the value will still be set).
12903      * @param {String} value The value to match
12904      */
12905     setValue : function(v){
12906         if(this.multiple){
12907             this.syncValue();
12908             return;
12909         }
12910         
12911         var text = v;
12912         if(this.valueField){
12913             var r = this.findRecord(this.valueField, v);
12914             if(r){
12915                 text = r.data[this.displayField];
12916             }else if(this.valueNotFoundText !== undefined){
12917                 text = this.valueNotFoundText;
12918             }
12919         }
12920         this.lastSelectionText = text;
12921         if(this.hiddenField){
12922             this.hiddenField.dom.value = v;
12923         }
12924         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12925         this.value = v;
12926         
12927         var close = this.closeTriggerEl();
12928         
12929         if(close){
12930             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12931         }
12932     },
12933     /**
12934      * @property {Object} the last set data for the element
12935      */
12936     
12937     lastData : false,
12938     /**
12939      * Sets the value of the field based on a object which is related to the record format for the store.
12940      * @param {Object} value the value to set as. or false on reset?
12941      */
12942     setFromData : function(o){
12943         
12944         if(this.multiple){
12945             this.addItem(o);
12946             return;
12947         }
12948             
12949         var dv = ''; // display value
12950         var vv = ''; // value value..
12951         this.lastData = o;
12952         if (this.displayField) {
12953             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12954         } else {
12955             // this is an error condition!!!
12956             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12957         }
12958         
12959         if(this.valueField){
12960             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12961         }
12962         
12963         var close = this.closeTriggerEl();
12964         
12965         if(close){
12966             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12967         }
12968         
12969         if(this.hiddenField){
12970             this.hiddenField.dom.value = vv;
12971             
12972             this.lastSelectionText = dv;
12973             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12974             this.value = vv;
12975             return;
12976         }
12977         // no hidden field.. - we store the value in 'value', but still display
12978         // display field!!!!
12979         this.lastSelectionText = dv;
12980         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12981         this.value = vv;
12982         
12983         
12984         
12985     },
12986     // private
12987     reset : function(){
12988         // overridden so that last data is reset..
12989         
12990         if(this.multiple){
12991             this.clearItem();
12992             return;
12993         }
12994         
12995         this.setValue(this.originalValue);
12996         this.clearInvalid();
12997         this.lastData = false;
12998         if (this.view) {
12999             this.view.clearSelections();
13000         }
13001     },
13002     // private
13003     findRecord : function(prop, value){
13004         var record;
13005         if(this.store.getCount() > 0){
13006             this.store.each(function(r){
13007                 if(r.data[prop] == value){
13008                     record = r;
13009                     return false;
13010                 }
13011                 return true;
13012             });
13013         }
13014         return record;
13015     },
13016     
13017     getName: function()
13018     {
13019         // returns hidden if it's set..
13020         if (!this.rendered) {return ''};
13021         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13022         
13023     },
13024     // private
13025     onViewMove : function(e, t){
13026         this.inKeyMode = false;
13027     },
13028
13029     // private
13030     onViewOver : function(e, t){
13031         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13032             return;
13033         }
13034         var item = this.view.findItemFromChild(t);
13035         
13036         if(item){
13037             var index = this.view.indexOf(item);
13038             this.select(index, false);
13039         }
13040     },
13041
13042     // private
13043     onViewClick : function(view, doFocus, el, e)
13044     {
13045         var index = this.view.getSelectedIndexes()[0];
13046         
13047         var r = this.store.getAt(index);
13048         
13049         if(this.tickable){
13050             
13051             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13052                 return;
13053             }
13054             
13055             var rm = false;
13056             var _this = this;
13057             
13058             Roo.each(this.tickItems, function(v,k){
13059                 
13060                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13061                     Roo.log(v);
13062                     _this.tickItems.splice(k, 1);
13063                     
13064                     if(typeof(e) == 'undefined' && view == false){
13065                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13066                     }
13067                     
13068                     rm = true;
13069                     return;
13070                 }
13071             });
13072             
13073             if(rm){
13074                 return;
13075             }
13076             
13077             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13078                 this.tickItems.push(r.data);
13079             }
13080             
13081             if(typeof(e) == 'undefined' && view == false){
13082                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13083             }
13084                     
13085             return;
13086         }
13087         
13088         if(r){
13089             this.onSelect(r, index);
13090         }
13091         if(doFocus !== false && !this.blockFocus){
13092             this.inputEl().focus();
13093         }
13094     },
13095
13096     // private
13097     restrictHeight : function(){
13098         //this.innerList.dom.style.height = '';
13099         //var inner = this.innerList.dom;
13100         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13101         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13102         //this.list.beginUpdate();
13103         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13104         this.list.alignTo(this.inputEl(), this.listAlign);
13105         this.list.alignTo(this.inputEl(), this.listAlign);
13106         //this.list.endUpdate();
13107     },
13108
13109     // private
13110     onEmptyResults : function(){
13111         
13112         if(this.tickable && this.editable){
13113             this.restrictHeight();
13114             return;
13115         }
13116         
13117         this.collapse();
13118     },
13119
13120     /**
13121      * Returns true if the dropdown list is expanded, else false.
13122      */
13123     isExpanded : function(){
13124         return this.list.isVisible();
13125     },
13126
13127     /**
13128      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13129      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13130      * @param {String} value The data value of the item to select
13131      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13132      * selected item if it is not currently in view (defaults to true)
13133      * @return {Boolean} True if the value matched an item in the list, else false
13134      */
13135     selectByValue : function(v, scrollIntoView){
13136         if(v !== undefined && v !== null){
13137             var r = this.findRecord(this.valueField || this.displayField, v);
13138             if(r){
13139                 this.select(this.store.indexOf(r), scrollIntoView);
13140                 return true;
13141             }
13142         }
13143         return false;
13144     },
13145
13146     /**
13147      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13148      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13149      * @param {Number} index The zero-based index of the list item to select
13150      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13151      * selected item if it is not currently in view (defaults to true)
13152      */
13153     select : function(index, scrollIntoView){
13154         this.selectedIndex = index;
13155         this.view.select(index);
13156         if(scrollIntoView !== false){
13157             var el = this.view.getNode(index);
13158             /*
13159              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13160              */
13161             if(el){
13162                 this.list.scrollChildIntoView(el, false);
13163             }
13164         }
13165     },
13166
13167     // private
13168     selectNext : function(){
13169         var ct = this.store.getCount();
13170         if(ct > 0){
13171             if(this.selectedIndex == -1){
13172                 this.select(0);
13173             }else if(this.selectedIndex < ct-1){
13174                 this.select(this.selectedIndex+1);
13175             }
13176         }
13177     },
13178
13179     // private
13180     selectPrev : function(){
13181         var ct = this.store.getCount();
13182         if(ct > 0){
13183             if(this.selectedIndex == -1){
13184                 this.select(0);
13185             }else if(this.selectedIndex != 0){
13186                 this.select(this.selectedIndex-1);
13187             }
13188         }
13189     },
13190
13191     // private
13192     onKeyUp : function(e){
13193         if(this.editable !== false && !e.isSpecialKey()){
13194             this.lastKey = e.getKey();
13195             this.dqTask.delay(this.queryDelay);
13196         }
13197     },
13198
13199     // private
13200     validateBlur : function(){
13201         return !this.list || !this.list.isVisible();   
13202     },
13203
13204     // private
13205     initQuery : function(){
13206         
13207         var v = this.getRawValue();
13208         
13209         if(this.tickable && this.editable){
13210             v = this.tickableInputEl().getValue();
13211         }
13212         
13213         this.doQuery(v);
13214     },
13215
13216     // private
13217     doForce : function(){
13218         if(this.inputEl().dom.value.length > 0){
13219             this.inputEl().dom.value =
13220                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13221              
13222         }
13223     },
13224
13225     /**
13226      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13227      * query allowing the query action to be canceled if needed.
13228      * @param {String} query The SQL query to execute
13229      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13230      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13231      * saved in the current store (defaults to false)
13232      */
13233     doQuery : function(q, forceAll){
13234         
13235         if(q === undefined || q === null){
13236             q = '';
13237         }
13238         var qe = {
13239             query: q,
13240             forceAll: forceAll,
13241             combo: this,
13242             cancel:false
13243         };
13244         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13245             return false;
13246         }
13247         q = qe.query;
13248         
13249         forceAll = qe.forceAll;
13250         if(forceAll === true || (q.length >= this.minChars)){
13251             
13252             this.hasQuery = true;
13253             
13254             if(this.lastQuery != q || this.alwaysQuery){
13255                 this.lastQuery = q;
13256                 if(this.mode == 'local'){
13257                     this.selectedIndex = -1;
13258                     if(forceAll){
13259                         this.store.clearFilter();
13260                     }else{
13261                         
13262                         if(this.specialFilter){
13263                             this.fireEvent('specialfilter', this);
13264                             this.onLoad();
13265                             return;
13266                         }
13267                         
13268                         this.store.filter(this.displayField, q);
13269                     }
13270                     
13271                     this.store.fireEvent("datachanged", this.store);
13272                     
13273                     this.onLoad();
13274                     
13275                     
13276                 }else{
13277                     
13278                     this.store.baseParams[this.queryParam] = q;
13279                     
13280                     var options = {params : this.getParams(q)};
13281                     
13282                     if(this.loadNext){
13283                         options.add = true;
13284                         options.params.start = this.page * this.pageSize;
13285                     }
13286                     
13287                     this.store.load(options);
13288                     
13289                     /*
13290                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13291                      *  we should expand the list on onLoad
13292                      *  so command out it
13293                      */
13294 //                    this.expand();
13295                 }
13296             }else{
13297                 this.selectedIndex = -1;
13298                 this.onLoad();   
13299             }
13300         }
13301         
13302         this.loadNext = false;
13303     },
13304     
13305     // private
13306     getParams : function(q){
13307         var p = {};
13308         //p[this.queryParam] = q;
13309         
13310         if(this.pageSize){
13311             p.start = 0;
13312             p.limit = this.pageSize;
13313         }
13314         return p;
13315     },
13316
13317     /**
13318      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13319      */
13320     collapse : function(){
13321         if(!this.isExpanded()){
13322             return;
13323         }
13324         
13325         this.list.hide();
13326         
13327         if(this.tickable){
13328             this.hasFocus = false;
13329             this.okBtn.hide();
13330             this.cancelBtn.hide();
13331             this.trigger.show();
13332             
13333             if(this.editable){
13334                 this.tickableInputEl().dom.value = '';
13335                 this.tickableInputEl().blur();
13336             }
13337             
13338         }
13339         
13340         Roo.get(document).un('mousedown', this.collapseIf, this);
13341         Roo.get(document).un('mousewheel', this.collapseIf, this);
13342         if (!this.editable) {
13343             Roo.get(document).un('keydown', this.listKeyPress, this);
13344         }
13345         this.fireEvent('collapse', this);
13346     },
13347
13348     // private
13349     collapseIf : function(e){
13350         var in_combo  = e.within(this.el);
13351         var in_list =  e.within(this.list);
13352         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13353         
13354         if (in_combo || in_list || is_list) {
13355             //e.stopPropagation();
13356             return;
13357         }
13358         
13359         if(this.tickable){
13360             this.onTickableFooterButtonClick(e, false, false);
13361         }
13362
13363         this.collapse();
13364         
13365     },
13366
13367     /**
13368      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13369      */
13370     expand : function(){
13371        
13372         if(this.isExpanded() || !this.hasFocus){
13373             return;
13374         }
13375         
13376         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13377         this.list.setWidth(lw);
13378         
13379         
13380          Roo.log('expand');
13381         
13382         this.list.show();
13383         
13384         this.restrictHeight();
13385         
13386         if(this.tickable){
13387             
13388             this.tickItems = Roo.apply([], this.item);
13389             
13390             this.okBtn.show();
13391             this.cancelBtn.show();
13392             this.trigger.hide();
13393             
13394             if(this.editable){
13395                 this.tickableInputEl().focus();
13396             }
13397             
13398         }
13399         
13400         Roo.get(document).on('mousedown', this.collapseIf, this);
13401         Roo.get(document).on('mousewheel', this.collapseIf, this);
13402         if (!this.editable) {
13403             Roo.get(document).on('keydown', this.listKeyPress, this);
13404         }
13405         
13406         this.fireEvent('expand', this);
13407     },
13408
13409     // private
13410     // Implements the default empty TriggerField.onTriggerClick function
13411     onTriggerClick : function(e)
13412     {
13413         Roo.log('trigger click');
13414         
13415         if(this.disabled || !this.triggerList){
13416             return;
13417         }
13418         
13419         this.page = 0;
13420         this.loadNext = false;
13421         
13422         if(this.isExpanded()){
13423             this.collapse();
13424             if (!this.blockFocus) {
13425                 this.inputEl().focus();
13426             }
13427             
13428         }else {
13429             this.hasFocus = true;
13430             if(this.triggerAction == 'all') {
13431                 this.doQuery(this.allQuery, true);
13432             } else {
13433                 this.doQuery(this.getRawValue());
13434             }
13435             if (!this.blockFocus) {
13436                 this.inputEl().focus();
13437             }
13438         }
13439     },
13440     
13441     onTickableTriggerClick : function(e)
13442     {
13443         if(this.disabled){
13444             return;
13445         }
13446         
13447         this.page = 0;
13448         this.loadNext = false;
13449         this.hasFocus = true;
13450         
13451         if(this.triggerAction == 'all') {
13452             this.doQuery(this.allQuery, true);
13453         } else {
13454             this.doQuery(this.getRawValue());
13455         }
13456     },
13457     
13458     onSearchFieldClick : function(e)
13459     {
13460         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13461             this.onTickableFooterButtonClick(e, false, false);
13462             return;
13463         }
13464         
13465         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13466             return;
13467         }
13468         
13469         this.page = 0;
13470         this.loadNext = false;
13471         this.hasFocus = true;
13472         
13473         if(this.triggerAction == 'all') {
13474             this.doQuery(this.allQuery, true);
13475         } else {
13476             this.doQuery(this.getRawValue());
13477         }
13478     },
13479     
13480     listKeyPress : function(e)
13481     {
13482         //Roo.log('listkeypress');
13483         // scroll to first matching element based on key pres..
13484         if (e.isSpecialKey()) {
13485             return false;
13486         }
13487         var k = String.fromCharCode(e.getKey()).toUpperCase();
13488         //Roo.log(k);
13489         var match  = false;
13490         var csel = this.view.getSelectedNodes();
13491         var cselitem = false;
13492         if (csel.length) {
13493             var ix = this.view.indexOf(csel[0]);
13494             cselitem  = this.store.getAt(ix);
13495             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13496                 cselitem = false;
13497             }
13498             
13499         }
13500         
13501         this.store.each(function(v) { 
13502             if (cselitem) {
13503                 // start at existing selection.
13504                 if (cselitem.id == v.id) {
13505                     cselitem = false;
13506                 }
13507                 return true;
13508             }
13509                 
13510             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13511                 match = this.store.indexOf(v);
13512                 return false;
13513             }
13514             return true;
13515         }, this);
13516         
13517         if (match === false) {
13518             return true; // no more action?
13519         }
13520         // scroll to?
13521         this.view.select(match);
13522         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13523         sn.scrollIntoView(sn.dom.parentNode, false);
13524     },
13525     
13526     onViewScroll : function(e, t){
13527         
13528         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){
13529             return;
13530         }
13531         
13532         this.hasQuery = true;
13533         
13534         this.loading = this.list.select('.loading', true).first();
13535         
13536         if(this.loading === null){
13537             this.list.createChild({
13538                 tag: 'div',
13539                 cls: 'loading roo-select2-more-results roo-select2-active',
13540                 html: 'Loading more results...'
13541             });
13542             
13543             this.loading = this.list.select('.loading', true).first();
13544             
13545             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13546             
13547             this.loading.hide();
13548         }
13549         
13550         this.loading.show();
13551         
13552         var _combo = this;
13553         
13554         this.page++;
13555         this.loadNext = true;
13556         
13557         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13558         
13559         return;
13560     },
13561     
13562     addItem : function(o)
13563     {   
13564         var dv = ''; // display value
13565         
13566         if (this.displayField) {
13567             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13568         } else {
13569             // this is an error condition!!!
13570             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13571         }
13572         
13573         if(!dv.length){
13574             return;
13575         }
13576         
13577         var choice = this.choices.createChild({
13578             tag: 'li',
13579             cls: 'roo-select2-search-choice',
13580             cn: [
13581                 {
13582                     tag: 'div',
13583                     html: dv
13584                 },
13585                 {
13586                     tag: 'a',
13587                     href: '#',
13588                     cls: 'roo-select2-search-choice-close',
13589                     tabindex: '-1'
13590                 }
13591             ]
13592             
13593         }, this.searchField);
13594         
13595         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13596         
13597         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13598         
13599         this.item.push(o);
13600         
13601         this.lastData = o;
13602         
13603         this.syncValue();
13604         
13605         this.inputEl().dom.value = '';
13606         
13607         this.validate();
13608     },
13609     
13610     onRemoveItem : function(e, _self, o)
13611     {
13612         e.preventDefault();
13613         
13614         this.lastItem = Roo.apply([], this.item);
13615         
13616         var index = this.item.indexOf(o.data) * 1;
13617         
13618         if( index < 0){
13619             Roo.log('not this item?!');
13620             return;
13621         }
13622         
13623         this.item.splice(index, 1);
13624         o.item.remove();
13625         
13626         this.syncValue();
13627         
13628         this.fireEvent('remove', this, e);
13629         
13630         this.validate();
13631         
13632     },
13633     
13634     syncValue : function()
13635     {
13636         if(!this.item.length){
13637             this.clearValue();
13638             return;
13639         }
13640             
13641         var value = [];
13642         var _this = this;
13643         Roo.each(this.item, function(i){
13644             if(_this.valueField){
13645                 value.push(i[_this.valueField]);
13646                 return;
13647             }
13648
13649             value.push(i);
13650         });
13651
13652         this.value = value.join(',');
13653
13654         if(this.hiddenField){
13655             this.hiddenField.dom.value = this.value;
13656         }
13657         
13658         this.store.fireEvent("datachanged", this.store);
13659     },
13660     
13661     clearItem : function()
13662     {
13663         if(!this.multiple){
13664             return;
13665         }
13666         
13667         this.item = [];
13668         
13669         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13670            c.remove();
13671         });
13672         
13673         this.syncValue();
13674         
13675         this.validate();
13676         
13677         if(this.tickable && !Roo.isTouch){
13678             this.view.refresh();
13679         }
13680     },
13681     
13682     inputEl: function ()
13683     {
13684         if(Roo.isTouch && this.mobileTouchView){
13685             return this.el.select('input.form-control',true).first();
13686         }
13687         
13688         if(this.tickable){
13689             return this.searchField;
13690         }
13691         
13692         return this.el.select('input.form-control',true).first();
13693     },
13694     
13695     
13696     onTickableFooterButtonClick : function(e, btn, el)
13697     {
13698         e.preventDefault();
13699         
13700         this.lastItem = Roo.apply([], this.item);
13701         
13702         if(btn && btn.name == 'cancel'){
13703             this.tickItems = Roo.apply([], this.item);
13704             this.collapse();
13705             return;
13706         }
13707         
13708         this.clearItem();
13709         
13710         var _this = this;
13711         
13712         Roo.each(this.tickItems, function(o){
13713             _this.addItem(o);
13714         });
13715         
13716         this.collapse();
13717         
13718     },
13719     
13720     validate : function()
13721     {
13722         var v = this.getRawValue();
13723         
13724         if(this.multiple){
13725             v = this.getValue();
13726         }
13727         
13728         if(this.disabled || this.allowBlank || v.length){
13729             this.markValid();
13730             return true;
13731         }
13732         
13733         this.markInvalid();
13734         return false;
13735     },
13736     
13737     tickableInputEl : function()
13738     {
13739         if(!this.tickable || !this.editable){
13740             return this.inputEl();
13741         }
13742         
13743         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13744     },
13745     
13746     
13747     getAutoCreateTouchView : function()
13748     {
13749         var id = Roo.id();
13750         
13751         var cfg = {
13752             cls: 'form-group' //input-group
13753         };
13754         
13755         var input =  {
13756             tag: 'input',
13757             id : id,
13758             type : this.inputType,
13759             cls : 'form-control x-combo-noedit',
13760             autocomplete: 'new-password',
13761             placeholder : this.placeholder || '',
13762             readonly : true
13763         };
13764         
13765         if (this.name) {
13766             input.name = this.name;
13767         }
13768         
13769         if (this.size) {
13770             input.cls += ' input-' + this.size;
13771         }
13772         
13773         if (this.disabled) {
13774             input.disabled = true;
13775         }
13776         
13777         var inputblock = {
13778             cls : '',
13779             cn : [
13780                 input
13781             ]
13782         };
13783         
13784         if(this.before){
13785             inputblock.cls += ' input-group';
13786             
13787             inputblock.cn.unshift({
13788                 tag :'span',
13789                 cls : 'input-group-addon',
13790                 html : this.before
13791             });
13792         }
13793         
13794         if(this.removable && !this.multiple){
13795             inputblock.cls += ' roo-removable';
13796             
13797             inputblock.cn.push({
13798                 tag: 'button',
13799                 html : 'x',
13800                 cls : 'roo-combo-removable-btn close'
13801             });
13802         }
13803
13804         if(this.hasFeedback && !this.allowBlank){
13805             
13806             inputblock.cls += ' has-feedback';
13807             
13808             inputblock.cn.push({
13809                 tag: 'span',
13810                 cls: 'glyphicon form-control-feedback'
13811             });
13812             
13813         }
13814         
13815         if (this.after) {
13816             
13817             inputblock.cls += (this.before) ? '' : ' input-group';
13818             
13819             inputblock.cn.push({
13820                 tag :'span',
13821                 cls : 'input-group-addon',
13822                 html : this.after
13823             });
13824         }
13825
13826         var box = {
13827             tag: 'div',
13828             cn: [
13829                 {
13830                     tag: 'input',
13831                     type : 'hidden',
13832                     cls: 'form-hidden-field'
13833                 },
13834                 inputblock
13835             ]
13836             
13837         };
13838         
13839         if(this.multiple){
13840             box = {
13841                 tag: 'div',
13842                 cn: [
13843                     {
13844                         tag: 'input',
13845                         type : 'hidden',
13846                         cls: 'form-hidden-field'
13847                     },
13848                     {
13849                         tag: 'ul',
13850                         cls: 'roo-select2-choices',
13851                         cn:[
13852                             {
13853                                 tag: 'li',
13854                                 cls: 'roo-select2-search-field',
13855                                 cn: [
13856
13857                                     inputblock
13858                                 ]
13859                             }
13860                         ]
13861                     }
13862                 ]
13863             }
13864         };
13865         
13866         var combobox = {
13867             cls: 'roo-select2-container input-group',
13868             cn: [
13869                 box
13870             ]
13871         };
13872         
13873         if(this.multiple){
13874             combobox.cls += ' roo-select2-container-multi';
13875         }
13876         
13877         var align = this.labelAlign || this.parentLabelAlign();
13878         
13879         cfg.cn = combobox;
13880         
13881         if(this.fieldLabel.length){
13882             
13883             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13884             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13885             
13886             cfg.cn = [
13887                 {
13888                     tag: 'label',
13889                     cls : 'control-label ' + lw,
13890                     html : this.fieldLabel
13891
13892                 },
13893                 {
13894                     cls : cw, 
13895                     cn: [
13896                         combobox
13897                     ]
13898                 }
13899             ];
13900         }
13901         
13902         var settings = this;
13903         
13904         ['xs','sm','md','lg'].map(function(size){
13905             if (settings[size]) {
13906                 cfg.cls += ' col-' + size + '-' + settings[size];
13907             }
13908         });
13909         
13910         return cfg;
13911     },
13912     
13913     initTouchView : function()
13914     {
13915         this.renderTouchView();
13916         
13917         this.touchViewEl.on('scroll', function(){
13918             this.el.dom.scrollTop = 0;
13919         }, this);
13920         
13921         this.originalValue = this.getValue();
13922         
13923         this.inputEl().on("click", this.showTouchView, this);
13924         
13925         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13926         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13927         
13928         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13929         
13930         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13931         this.store.on('load', this.onTouchViewLoad, this);
13932         this.store.on('loadexception', this.onTouchViewLoadException, this);
13933         
13934         if(this.hiddenName){
13935             
13936             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13937             
13938             this.hiddenField.dom.value =
13939                 this.hiddenValue !== undefined ? this.hiddenValue :
13940                 this.value !== undefined ? this.value : '';
13941         
13942             this.el.dom.removeAttribute('name');
13943             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13944         }
13945         
13946         if(this.multiple){
13947             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13948             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13949         }
13950         
13951         if(this.removable && !this.multiple){
13952             var close = this.closeTriggerEl();
13953             if(close){
13954                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13955                 close.on('click', this.removeBtnClick, this, close);
13956             }
13957         }
13958         /*
13959          * fix the bug in Safari iOS8
13960          */
13961         this.inputEl().on("focus", function(e){
13962             document.activeElement.blur();
13963         }, this);
13964         
13965         return;
13966         
13967         
13968     },
13969     
13970     renderTouchView : function()
13971     {
13972         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13973         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13974         
13975         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13976         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13977         
13978         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13979         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13980         this.touchViewBodyEl.setStyle('overflow', 'auto');
13981         
13982         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13983         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13984         
13985         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13986         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13987         
13988     },
13989     
13990     showTouchView : function()
13991     {
13992         if(this.disabled){
13993             return;
13994         }
13995         
13996         this.touchViewHeaderEl.hide();
13997
13998         if(this.fieldLabel.length){
13999             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
14000             this.touchViewHeaderEl.show();
14001         }
14002
14003         this.touchViewEl.show();
14004
14005         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14006         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14007
14008         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14009
14010         if(this.fieldLabel.length){
14011             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14012         }
14013         
14014         this.touchViewBodyEl.setHeight(bodyHeight);
14015
14016         if(this.animate){
14017             var _this = this;
14018             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14019         }else{
14020             this.touchViewEl.addClass('in');
14021         }
14022
14023         this.doTouchViewQuery();
14024         
14025     },
14026     
14027     hideTouchView : function()
14028     {
14029         this.touchViewEl.removeClass('in');
14030
14031         if(this.animate){
14032             var _this = this;
14033             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14034         }else{
14035             this.touchViewEl.setStyle('display', 'none');
14036         }
14037         
14038     },
14039     
14040     setTouchViewValue : function()
14041     {
14042         if(this.multiple){
14043             this.clearItem();
14044         
14045             var _this = this;
14046
14047             Roo.each(this.tickItems, function(o){
14048                 this.addItem(o);
14049             }, this);
14050         }
14051         
14052         this.hideTouchView();
14053     },
14054     
14055     doTouchViewQuery : function()
14056     {
14057         var qe = {
14058             query: '',
14059             forceAll: true,
14060             combo: this,
14061             cancel:false
14062         };
14063         
14064         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14065             return false;
14066         }
14067         
14068         if(!this.alwaysQuery || this.mode == 'local'){
14069             this.onTouchViewLoad();
14070             return;
14071         }
14072         
14073         this.store.load();
14074     },
14075     
14076     onTouchViewBeforeLoad : function(combo,opts)
14077     {
14078         return;
14079     },
14080
14081     // private
14082     onTouchViewLoad : function()
14083     {
14084         if(this.store.getCount() < 1){
14085             this.onTouchViewEmptyResults();
14086             return;
14087         }
14088         
14089         this.clearTouchView();
14090         
14091         var rawValue = this.getRawValue();
14092         
14093         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14094         
14095         this.tickItems = [];
14096         
14097         this.store.data.each(function(d, rowIndex){
14098             var row = this.touchViewListGroup.createChild(template);
14099             
14100             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14101                 row.addClass(d.data.cls);
14102             }
14103             
14104             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14105                 var cfg = {
14106                     data : d.data,
14107                     html : d.data[this.displayField]
14108                 };
14109                 
14110                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14111                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14112                 }
14113             }
14114             
14115             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
14116                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14117             }
14118             
14119             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
14120                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14121                 this.tickItems.push(d.data);
14122             }
14123             
14124             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14125             
14126         }, this);
14127         
14128         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14129         
14130         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14131
14132         if(this.fieldLabel.length){
14133             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14134         }
14135
14136         var listHeight = this.touchViewListGroup.getHeight();
14137         
14138         var _this = this;
14139         
14140         if(firstChecked && listHeight > bodyHeight){
14141             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14142         }
14143         
14144     },
14145     
14146     onTouchViewLoadException : function()
14147     {
14148         this.hideTouchView();
14149     },
14150     
14151     onTouchViewEmptyResults : function()
14152     {
14153         this.clearTouchView();
14154         
14155         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14156         
14157         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14158         
14159     },
14160     
14161     clearTouchView : function()
14162     {
14163         this.touchViewListGroup.dom.innerHTML = '';
14164     },
14165     
14166     onTouchViewClick : function(e, el, o)
14167     {
14168         e.preventDefault();
14169         
14170         var row = o.row;
14171         var rowIndex = o.rowIndex;
14172         
14173         var r = this.store.getAt(rowIndex);
14174         
14175         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14176             
14177             if(!this.multiple){
14178                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14179                     c.dom.removeAttribute('checked');
14180                 }, this);
14181
14182                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14183
14184                 this.setFromData(r.data);
14185
14186                 var close = this.closeTriggerEl();
14187
14188                 if(close){
14189                     close.show();
14190                 }
14191
14192                 this.hideTouchView();
14193
14194                 this.fireEvent('select', this, r, rowIndex);
14195
14196                 return;
14197             }
14198
14199             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14200                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14201                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14202                 return;
14203             }
14204
14205             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14206             this.addItem(r.data);
14207             this.tickItems.push(r.data);
14208         }
14209     }
14210     
14211
14212     /** 
14213     * @cfg {Boolean} grow 
14214     * @hide 
14215     */
14216     /** 
14217     * @cfg {Number} growMin 
14218     * @hide 
14219     */
14220     /** 
14221     * @cfg {Number} growMax 
14222     * @hide 
14223     */
14224     /**
14225      * @hide
14226      * @method autoSize
14227      */
14228 });
14229
14230 Roo.apply(Roo.bootstrap.ComboBox,  {
14231     
14232     header : {
14233         tag: 'div',
14234         cls: 'modal-header',
14235         cn: [
14236             {
14237                 tag: 'h4',
14238                 cls: 'modal-title'
14239             }
14240         ]
14241     },
14242     
14243     body : {
14244         tag: 'div',
14245         cls: 'modal-body',
14246         cn: [
14247             {
14248                 tag: 'ul',
14249                 cls: 'list-group'
14250             }
14251         ]
14252     },
14253     
14254     listItemRadio : {
14255         tag: 'li',
14256         cls: 'list-group-item',
14257         cn: [
14258             {
14259                 tag: 'span',
14260                 cls: 'roo-combobox-list-group-item-value'
14261             },
14262             {
14263                 tag: 'div',
14264                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14265                 cn: [
14266                     {
14267                         tag: 'input',
14268                         type: 'radio'
14269                     },
14270                     {
14271                         tag: 'label'
14272                     }
14273                 ]
14274             }
14275         ]
14276     },
14277     
14278     listItemCheckbox : {
14279         tag: 'li',
14280         cls: 'list-group-item',
14281         cn: [
14282             {
14283                 tag: 'span',
14284                 cls: 'roo-combobox-list-group-item-value'
14285             },
14286             {
14287                 tag: 'div',
14288                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14289                 cn: [
14290                     {
14291                         tag: 'input',
14292                         type: 'checkbox'
14293                     },
14294                     {
14295                         tag: 'label'
14296                     }
14297                 ]
14298             }
14299         ]
14300     },
14301     
14302     emptyResult : {
14303         tag: 'div',
14304         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14305     },
14306     
14307     footer : {
14308         tag: 'div',
14309         cls: 'modal-footer',
14310         cn: [
14311             {
14312                 tag: 'div',
14313                 cls: 'row',
14314                 cn: [
14315                     {
14316                         tag: 'div',
14317                         cls: 'col-xs-6 text-left',
14318                         cn: {
14319                             tag: 'button',
14320                             cls: 'btn btn-danger roo-touch-view-cancel',
14321                             html: 'Cancel'
14322                         }
14323                     },
14324                     {
14325                         tag: 'div',
14326                         cls: 'col-xs-6 text-right',
14327                         cn: {
14328                             tag: 'button',
14329                             cls: 'btn btn-success roo-touch-view-ok',
14330                             html: 'OK'
14331                         }
14332                     }
14333                 ]
14334             }
14335         ]
14336         
14337     }
14338 });
14339
14340 Roo.apply(Roo.bootstrap.ComboBox,  {
14341     
14342     touchViewTemplate : {
14343         tag: 'div',
14344         cls: 'modal fade roo-combobox-touch-view',
14345         cn: [
14346             {
14347                 tag: 'div',
14348                 cls: 'modal-dialog',
14349                 style : 'position:fixed', // we have to fix position....
14350                 cn: [
14351                     {
14352                         tag: 'div',
14353                         cls: 'modal-content',
14354                         cn: [
14355                             Roo.bootstrap.ComboBox.header,
14356                             Roo.bootstrap.ComboBox.body,
14357                             Roo.bootstrap.ComboBox.footer
14358                         ]
14359                     }
14360                 ]
14361             }
14362         ]
14363     }
14364 });/*
14365  * Based on:
14366  * Ext JS Library 1.1.1
14367  * Copyright(c) 2006-2007, Ext JS, LLC.
14368  *
14369  * Originally Released Under LGPL - original licence link has changed is not relivant.
14370  *
14371  * Fork - LGPL
14372  * <script type="text/javascript">
14373  */
14374
14375 /**
14376  * @class Roo.View
14377  * @extends Roo.util.Observable
14378  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14379  * This class also supports single and multi selection modes. <br>
14380  * Create a data model bound view:
14381  <pre><code>
14382  var store = new Roo.data.Store(...);
14383
14384  var view = new Roo.View({
14385     el : "my-element",
14386     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14387  
14388     singleSelect: true,
14389     selectedClass: "ydataview-selected",
14390     store: store
14391  });
14392
14393  // listen for node click?
14394  view.on("click", function(vw, index, node, e){
14395  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14396  });
14397
14398  // load XML data
14399  dataModel.load("foobar.xml");
14400  </code></pre>
14401  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14402  * <br><br>
14403  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14404  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14405  * 
14406  * Note: old style constructor is still suported (container, template, config)
14407  * 
14408  * @constructor
14409  * Create a new View
14410  * @param {Object} config The config object
14411  * 
14412  */
14413 Roo.View = function(config, depreciated_tpl, depreciated_config){
14414     
14415     this.parent = false;
14416     
14417     if (typeof(depreciated_tpl) == 'undefined') {
14418         // new way.. - universal constructor.
14419         Roo.apply(this, config);
14420         this.el  = Roo.get(this.el);
14421     } else {
14422         // old format..
14423         this.el  = Roo.get(config);
14424         this.tpl = depreciated_tpl;
14425         Roo.apply(this, depreciated_config);
14426     }
14427     this.wrapEl  = this.el.wrap().wrap();
14428     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14429     
14430     
14431     if(typeof(this.tpl) == "string"){
14432         this.tpl = new Roo.Template(this.tpl);
14433     } else {
14434         // support xtype ctors..
14435         this.tpl = new Roo.factory(this.tpl, Roo);
14436     }
14437     
14438     
14439     this.tpl.compile();
14440     
14441     /** @private */
14442     this.addEvents({
14443         /**
14444          * @event beforeclick
14445          * Fires before a click is processed. Returns false to cancel the default action.
14446          * @param {Roo.View} this
14447          * @param {Number} index The index of the target node
14448          * @param {HTMLElement} node The target node
14449          * @param {Roo.EventObject} e The raw event object
14450          */
14451             "beforeclick" : true,
14452         /**
14453          * @event click
14454          * Fires when a template node is clicked.
14455          * @param {Roo.View} this
14456          * @param {Number} index The index of the target node
14457          * @param {HTMLElement} node The target node
14458          * @param {Roo.EventObject} e The raw event object
14459          */
14460             "click" : true,
14461         /**
14462          * @event dblclick
14463          * Fires when a template node is double clicked.
14464          * @param {Roo.View} this
14465          * @param {Number} index The index of the target node
14466          * @param {HTMLElement} node The target node
14467          * @param {Roo.EventObject} e The raw event object
14468          */
14469             "dblclick" : true,
14470         /**
14471          * @event contextmenu
14472          * Fires when a template node is right clicked.
14473          * @param {Roo.View} this
14474          * @param {Number} index The index of the target node
14475          * @param {HTMLElement} node The target node
14476          * @param {Roo.EventObject} e The raw event object
14477          */
14478             "contextmenu" : true,
14479         /**
14480          * @event selectionchange
14481          * Fires when the selected nodes change.
14482          * @param {Roo.View} this
14483          * @param {Array} selections Array of the selected nodes
14484          */
14485             "selectionchange" : true,
14486     
14487         /**
14488          * @event beforeselect
14489          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14490          * @param {Roo.View} this
14491          * @param {HTMLElement} node The node to be selected
14492          * @param {Array} selections Array of currently selected nodes
14493          */
14494             "beforeselect" : true,
14495         /**
14496          * @event preparedata
14497          * Fires on every row to render, to allow you to change the data.
14498          * @param {Roo.View} this
14499          * @param {Object} data to be rendered (change this)
14500          */
14501           "preparedata" : true
14502           
14503           
14504         });
14505
14506
14507
14508     this.el.on({
14509         "click": this.onClick,
14510         "dblclick": this.onDblClick,
14511         "contextmenu": this.onContextMenu,
14512         scope:this
14513     });
14514
14515     this.selections = [];
14516     this.nodes = [];
14517     this.cmp = new Roo.CompositeElementLite([]);
14518     if(this.store){
14519         this.store = Roo.factory(this.store, Roo.data);
14520         this.setStore(this.store, true);
14521     }
14522     
14523     if ( this.footer && this.footer.xtype) {
14524            
14525          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14526         
14527         this.footer.dataSource = this.store;
14528         this.footer.container = fctr;
14529         this.footer = Roo.factory(this.footer, Roo);
14530         fctr.insertFirst(this.el);
14531         
14532         // this is a bit insane - as the paging toolbar seems to detach the el..
14533 //        dom.parentNode.parentNode.parentNode
14534          // they get detached?
14535     }
14536     
14537     
14538     Roo.View.superclass.constructor.call(this);
14539     
14540     
14541 };
14542
14543 Roo.extend(Roo.View, Roo.util.Observable, {
14544     
14545      /**
14546      * @cfg {Roo.data.Store} store Data store to load data from.
14547      */
14548     store : false,
14549     
14550     /**
14551      * @cfg {String|Roo.Element} el The container element.
14552      */
14553     el : '',
14554     
14555     /**
14556      * @cfg {String|Roo.Template} tpl The template used by this View 
14557      */
14558     tpl : false,
14559     /**
14560      * @cfg {String} dataName the named area of the template to use as the data area
14561      *                          Works with domtemplates roo-name="name"
14562      */
14563     dataName: false,
14564     /**
14565      * @cfg {String} selectedClass The css class to add to selected nodes
14566      */
14567     selectedClass : "x-view-selected",
14568      /**
14569      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14570      */
14571     emptyText : "",
14572     
14573     /**
14574      * @cfg {String} text to display on mask (default Loading)
14575      */
14576     mask : false,
14577     /**
14578      * @cfg {Boolean} multiSelect Allow multiple selection
14579      */
14580     multiSelect : false,
14581     /**
14582      * @cfg {Boolean} singleSelect Allow single selection
14583      */
14584     singleSelect:  false,
14585     
14586     /**
14587      * @cfg {Boolean} toggleSelect - selecting 
14588      */
14589     toggleSelect : false,
14590     
14591     /**
14592      * @cfg {Boolean} tickable - selecting 
14593      */
14594     tickable : false,
14595     
14596     /**
14597      * Returns the element this view is bound to.
14598      * @return {Roo.Element}
14599      */
14600     getEl : function(){
14601         return this.wrapEl;
14602     },
14603     
14604     
14605
14606     /**
14607      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14608      */
14609     refresh : function(){
14610         //Roo.log('refresh');
14611         var t = this.tpl;
14612         
14613         // if we are using something like 'domtemplate', then
14614         // the what gets used is:
14615         // t.applySubtemplate(NAME, data, wrapping data..)
14616         // the outer template then get' applied with
14617         //     the store 'extra data'
14618         // and the body get's added to the
14619         //      roo-name="data" node?
14620         //      <span class='roo-tpl-{name}'></span> ?????
14621         
14622         
14623         
14624         this.clearSelections();
14625         this.el.update("");
14626         var html = [];
14627         var records = this.store.getRange();
14628         if(records.length < 1) {
14629             
14630             // is this valid??  = should it render a template??
14631             
14632             this.el.update(this.emptyText);
14633             return;
14634         }
14635         var el = this.el;
14636         if (this.dataName) {
14637             this.el.update(t.apply(this.store.meta)); //????
14638             el = this.el.child('.roo-tpl-' + this.dataName);
14639         }
14640         
14641         for(var i = 0, len = records.length; i < len; i++){
14642             var data = this.prepareData(records[i].data, i, records[i]);
14643             this.fireEvent("preparedata", this, data, i, records[i]);
14644             
14645             var d = Roo.apply({}, data);
14646             
14647             if(this.tickable){
14648                 Roo.apply(d, {'roo-id' : Roo.id()});
14649                 
14650                 var _this = this;
14651             
14652                 Roo.each(this.parent.item, function(item){
14653                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14654                         return;
14655                     }
14656                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14657                 });
14658             }
14659             
14660             html[html.length] = Roo.util.Format.trim(
14661                 this.dataName ?
14662                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14663                     t.apply(d)
14664             );
14665         }
14666         
14667         
14668         
14669         el.update(html.join(""));
14670         this.nodes = el.dom.childNodes;
14671         this.updateIndexes(0);
14672     },
14673     
14674
14675     /**
14676      * Function to override to reformat the data that is sent to
14677      * the template for each node.
14678      * DEPRICATED - use the preparedata event handler.
14679      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14680      * a JSON object for an UpdateManager bound view).
14681      */
14682     prepareData : function(data, index, record)
14683     {
14684         this.fireEvent("preparedata", this, data, index, record);
14685         return data;
14686     },
14687
14688     onUpdate : function(ds, record){
14689         // Roo.log('on update');   
14690         this.clearSelections();
14691         var index = this.store.indexOf(record);
14692         var n = this.nodes[index];
14693         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14694         n.parentNode.removeChild(n);
14695         this.updateIndexes(index, index);
14696     },
14697
14698     
14699     
14700 // --------- FIXME     
14701     onAdd : function(ds, records, index)
14702     {
14703         //Roo.log(['on Add', ds, records, index] );        
14704         this.clearSelections();
14705         if(this.nodes.length == 0){
14706             this.refresh();
14707             return;
14708         }
14709         var n = this.nodes[index];
14710         for(var i = 0, len = records.length; i < len; i++){
14711             var d = this.prepareData(records[i].data, i, records[i]);
14712             if(n){
14713                 this.tpl.insertBefore(n, d);
14714             }else{
14715                 
14716                 this.tpl.append(this.el, d);
14717             }
14718         }
14719         this.updateIndexes(index);
14720     },
14721
14722     onRemove : function(ds, record, index){
14723        // Roo.log('onRemove');
14724         this.clearSelections();
14725         var el = this.dataName  ?
14726             this.el.child('.roo-tpl-' + this.dataName) :
14727             this.el; 
14728         
14729         el.dom.removeChild(this.nodes[index]);
14730         this.updateIndexes(index);
14731     },
14732
14733     /**
14734      * Refresh an individual node.
14735      * @param {Number} index
14736      */
14737     refreshNode : function(index){
14738         this.onUpdate(this.store, this.store.getAt(index));
14739     },
14740
14741     updateIndexes : function(startIndex, endIndex){
14742         var ns = this.nodes;
14743         startIndex = startIndex || 0;
14744         endIndex = endIndex || ns.length - 1;
14745         for(var i = startIndex; i <= endIndex; i++){
14746             ns[i].nodeIndex = i;
14747         }
14748     },
14749
14750     /**
14751      * Changes the data store this view uses and refresh the view.
14752      * @param {Store} store
14753      */
14754     setStore : function(store, initial){
14755         if(!initial && this.store){
14756             this.store.un("datachanged", this.refresh);
14757             this.store.un("add", this.onAdd);
14758             this.store.un("remove", this.onRemove);
14759             this.store.un("update", this.onUpdate);
14760             this.store.un("clear", this.refresh);
14761             this.store.un("beforeload", this.onBeforeLoad);
14762             this.store.un("load", this.onLoad);
14763             this.store.un("loadexception", this.onLoad);
14764         }
14765         if(store){
14766           
14767             store.on("datachanged", this.refresh, this);
14768             store.on("add", this.onAdd, this);
14769             store.on("remove", this.onRemove, this);
14770             store.on("update", this.onUpdate, this);
14771             store.on("clear", this.refresh, this);
14772             store.on("beforeload", this.onBeforeLoad, this);
14773             store.on("load", this.onLoad, this);
14774             store.on("loadexception", this.onLoad, this);
14775         }
14776         
14777         if(store){
14778             this.refresh();
14779         }
14780     },
14781     /**
14782      * onbeforeLoad - masks the loading area.
14783      *
14784      */
14785     onBeforeLoad : function(store,opts)
14786     {
14787          //Roo.log('onBeforeLoad');   
14788         if (!opts.add) {
14789             this.el.update("");
14790         }
14791         this.el.mask(this.mask ? this.mask : "Loading" ); 
14792     },
14793     onLoad : function ()
14794     {
14795         this.el.unmask();
14796     },
14797     
14798
14799     /**
14800      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14801      * @param {HTMLElement} node
14802      * @return {HTMLElement} The template node
14803      */
14804     findItemFromChild : function(node){
14805         var el = this.dataName  ?
14806             this.el.child('.roo-tpl-' + this.dataName,true) :
14807             this.el.dom; 
14808         
14809         if(!node || node.parentNode == el){
14810                     return node;
14811             }
14812             var p = node.parentNode;
14813             while(p && p != el){
14814             if(p.parentNode == el){
14815                 return p;
14816             }
14817             p = p.parentNode;
14818         }
14819             return null;
14820     },
14821
14822     /** @ignore */
14823     onClick : function(e){
14824         var item = this.findItemFromChild(e.getTarget());
14825         if(item){
14826             var index = this.indexOf(item);
14827             if(this.onItemClick(item, index, e) !== false){
14828                 this.fireEvent("click", this, index, item, e);
14829             }
14830         }else{
14831             this.clearSelections();
14832         }
14833     },
14834
14835     /** @ignore */
14836     onContextMenu : function(e){
14837         var item = this.findItemFromChild(e.getTarget());
14838         if(item){
14839             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14840         }
14841     },
14842
14843     /** @ignore */
14844     onDblClick : function(e){
14845         var item = this.findItemFromChild(e.getTarget());
14846         if(item){
14847             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14848         }
14849     },
14850
14851     onItemClick : function(item, index, e)
14852     {
14853         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14854             return false;
14855         }
14856         if (this.toggleSelect) {
14857             var m = this.isSelected(item) ? 'unselect' : 'select';
14858             //Roo.log(m);
14859             var _t = this;
14860             _t[m](item, true, false);
14861             return true;
14862         }
14863         if(this.multiSelect || this.singleSelect){
14864             if(this.multiSelect && e.shiftKey && this.lastSelection){
14865                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14866             }else{
14867                 this.select(item, this.multiSelect && e.ctrlKey);
14868                 this.lastSelection = item;
14869             }
14870             
14871             if(!this.tickable){
14872                 e.preventDefault();
14873             }
14874             
14875         }
14876         return true;
14877     },
14878
14879     /**
14880      * Get the number of selected nodes.
14881      * @return {Number}
14882      */
14883     getSelectionCount : function(){
14884         return this.selections.length;
14885     },
14886
14887     /**
14888      * Get the currently selected nodes.
14889      * @return {Array} An array of HTMLElements
14890      */
14891     getSelectedNodes : function(){
14892         return this.selections;
14893     },
14894
14895     /**
14896      * Get the indexes of the selected nodes.
14897      * @return {Array}
14898      */
14899     getSelectedIndexes : function(){
14900         var indexes = [], s = this.selections;
14901         for(var i = 0, len = s.length; i < len; i++){
14902             indexes.push(s[i].nodeIndex);
14903         }
14904         return indexes;
14905     },
14906
14907     /**
14908      * Clear all selections
14909      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14910      */
14911     clearSelections : function(suppressEvent){
14912         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14913             this.cmp.elements = this.selections;
14914             this.cmp.removeClass(this.selectedClass);
14915             this.selections = [];
14916             if(!suppressEvent){
14917                 this.fireEvent("selectionchange", this, this.selections);
14918             }
14919         }
14920     },
14921
14922     /**
14923      * Returns true if the passed node is selected
14924      * @param {HTMLElement/Number} node The node or node index
14925      * @return {Boolean}
14926      */
14927     isSelected : function(node){
14928         var s = this.selections;
14929         if(s.length < 1){
14930             return false;
14931         }
14932         node = this.getNode(node);
14933         return s.indexOf(node) !== -1;
14934     },
14935
14936     /**
14937      * Selects nodes.
14938      * @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
14939      * @param {Boolean} keepExisting (optional) true to keep existing selections
14940      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14941      */
14942     select : function(nodeInfo, keepExisting, suppressEvent){
14943         if(nodeInfo instanceof Array){
14944             if(!keepExisting){
14945                 this.clearSelections(true);
14946             }
14947             for(var i = 0, len = nodeInfo.length; i < len; i++){
14948                 this.select(nodeInfo[i], true, true);
14949             }
14950             return;
14951         } 
14952         var node = this.getNode(nodeInfo);
14953         if(!node || this.isSelected(node)){
14954             return; // already selected.
14955         }
14956         if(!keepExisting){
14957             this.clearSelections(true);
14958         }
14959         
14960         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14961             Roo.fly(node).addClass(this.selectedClass);
14962             this.selections.push(node);
14963             if(!suppressEvent){
14964                 this.fireEvent("selectionchange", this, this.selections);
14965             }
14966         }
14967         
14968         
14969     },
14970       /**
14971      * Unselects nodes.
14972      * @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
14973      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14974      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14975      */
14976     unselect : function(nodeInfo, keepExisting, suppressEvent)
14977     {
14978         if(nodeInfo instanceof Array){
14979             Roo.each(this.selections, function(s) {
14980                 this.unselect(s, nodeInfo);
14981             }, this);
14982             return;
14983         }
14984         var node = this.getNode(nodeInfo);
14985         if(!node || !this.isSelected(node)){
14986             //Roo.log("not selected");
14987             return; // not selected.
14988         }
14989         // fireevent???
14990         var ns = [];
14991         Roo.each(this.selections, function(s) {
14992             if (s == node ) {
14993                 Roo.fly(node).removeClass(this.selectedClass);
14994
14995                 return;
14996             }
14997             ns.push(s);
14998         },this);
14999         
15000         this.selections= ns;
15001         this.fireEvent("selectionchange", this, this.selections);
15002     },
15003
15004     /**
15005      * Gets a template node.
15006      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15007      * @return {HTMLElement} The node or null if it wasn't found
15008      */
15009     getNode : function(nodeInfo){
15010         if(typeof nodeInfo == "string"){
15011             return document.getElementById(nodeInfo);
15012         }else if(typeof nodeInfo == "number"){
15013             return this.nodes[nodeInfo];
15014         }
15015         return nodeInfo;
15016     },
15017
15018     /**
15019      * Gets a range template nodes.
15020      * @param {Number} startIndex
15021      * @param {Number} endIndex
15022      * @return {Array} An array of nodes
15023      */
15024     getNodes : function(start, end){
15025         var ns = this.nodes;
15026         start = start || 0;
15027         end = typeof end == "undefined" ? ns.length - 1 : end;
15028         var nodes = [];
15029         if(start <= end){
15030             for(var i = start; i <= end; i++){
15031                 nodes.push(ns[i]);
15032             }
15033         } else{
15034             for(var i = start; i >= end; i--){
15035                 nodes.push(ns[i]);
15036             }
15037         }
15038         return nodes;
15039     },
15040
15041     /**
15042      * Finds the index of the passed node
15043      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15044      * @return {Number} The index of the node or -1
15045      */
15046     indexOf : function(node){
15047         node = this.getNode(node);
15048         if(typeof node.nodeIndex == "number"){
15049             return node.nodeIndex;
15050         }
15051         var ns = this.nodes;
15052         for(var i = 0, len = ns.length; i < len; i++){
15053             if(ns[i] == node){
15054                 return i;
15055             }
15056         }
15057         return -1;
15058     }
15059 });
15060 /*
15061  * - LGPL
15062  *
15063  * based on jquery fullcalendar
15064  * 
15065  */
15066
15067 Roo.bootstrap = Roo.bootstrap || {};
15068 /**
15069  * @class Roo.bootstrap.Calendar
15070  * @extends Roo.bootstrap.Component
15071  * Bootstrap Calendar class
15072  * @cfg {Boolean} loadMask (true|false) default false
15073  * @cfg {Object} header generate the user specific header of the calendar, default false
15074
15075  * @constructor
15076  * Create a new Container
15077  * @param {Object} config The config object
15078  */
15079
15080
15081
15082 Roo.bootstrap.Calendar = function(config){
15083     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15084      this.addEvents({
15085         /**
15086              * @event select
15087              * Fires when a date is selected
15088              * @param {DatePicker} this
15089              * @param {Date} date The selected date
15090              */
15091         'select': true,
15092         /**
15093              * @event monthchange
15094              * Fires when the displayed month changes 
15095              * @param {DatePicker} this
15096              * @param {Date} date The selected month
15097              */
15098         'monthchange': true,
15099         /**
15100              * @event evententer
15101              * Fires when mouse over an event
15102              * @param {Calendar} this
15103              * @param {event} Event
15104              */
15105         'evententer': true,
15106         /**
15107              * @event eventleave
15108              * Fires when the mouse leaves an
15109              * @param {Calendar} this
15110              * @param {event}
15111              */
15112         'eventleave': true,
15113         /**
15114              * @event eventclick
15115              * Fires when the mouse click an
15116              * @param {Calendar} this
15117              * @param {event}
15118              */
15119         'eventclick': true
15120         
15121     });
15122
15123 };
15124
15125 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15126     
15127      /**
15128      * @cfg {Number} startDay
15129      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15130      */
15131     startDay : 0,
15132     
15133     loadMask : false,
15134     
15135     header : false,
15136       
15137     getAutoCreate : function(){
15138         
15139         
15140         var fc_button = function(name, corner, style, content ) {
15141             return Roo.apply({},{
15142                 tag : 'span',
15143                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15144                          (corner.length ?
15145                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15146                             ''
15147                         ),
15148                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15149                 unselectable: 'on'
15150             });
15151         };
15152         
15153         var header = {};
15154         
15155         if(!this.header){
15156             header = {
15157                 tag : 'table',
15158                 cls : 'fc-header',
15159                 style : 'width:100%',
15160                 cn : [
15161                     {
15162                         tag: 'tr',
15163                         cn : [
15164                             {
15165                                 tag : 'td',
15166                                 cls : 'fc-header-left',
15167                                 cn : [
15168                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15169                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15170                                     { tag: 'span', cls: 'fc-header-space' },
15171                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15172
15173
15174                                 ]
15175                             },
15176
15177                             {
15178                                 tag : 'td',
15179                                 cls : 'fc-header-center',
15180                                 cn : [
15181                                     {
15182                                         tag: 'span',
15183                                         cls: 'fc-header-title',
15184                                         cn : {
15185                                             tag: 'H2',
15186                                             html : 'month / year'
15187                                         }
15188                                     }
15189
15190                                 ]
15191                             },
15192                             {
15193                                 tag : 'td',
15194                                 cls : 'fc-header-right',
15195                                 cn : [
15196                               /*      fc_button('month', 'left', '', 'month' ),
15197                                     fc_button('week', '', '', 'week' ),
15198                                     fc_button('day', 'right', '', 'day' )
15199                                 */    
15200
15201                                 ]
15202                             }
15203
15204                         ]
15205                     }
15206                 ]
15207             };
15208         }
15209         
15210         header = this.header;
15211         
15212        
15213         var cal_heads = function() {
15214             var ret = [];
15215             // fixme - handle this.
15216             
15217             for (var i =0; i < Date.dayNames.length; i++) {
15218                 var d = Date.dayNames[i];
15219                 ret.push({
15220                     tag: 'th',
15221                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15222                     html : d.substring(0,3)
15223                 });
15224                 
15225             }
15226             ret[0].cls += ' fc-first';
15227             ret[6].cls += ' fc-last';
15228             return ret;
15229         };
15230         var cal_cell = function(n) {
15231             return  {
15232                 tag: 'td',
15233                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15234                 cn : [
15235                     {
15236                         cn : [
15237                             {
15238                                 cls: 'fc-day-number',
15239                                 html: 'D'
15240                             },
15241                             {
15242                                 cls: 'fc-day-content',
15243                              
15244                                 cn : [
15245                                      {
15246                                         style: 'position: relative;' // height: 17px;
15247                                     }
15248                                 ]
15249                             }
15250                             
15251                             
15252                         ]
15253                     }
15254                 ]
15255                 
15256             }
15257         };
15258         var cal_rows = function() {
15259             
15260             var ret = [];
15261             for (var r = 0; r < 6; r++) {
15262                 var row= {
15263                     tag : 'tr',
15264                     cls : 'fc-week',
15265                     cn : []
15266                 };
15267                 
15268                 for (var i =0; i < Date.dayNames.length; i++) {
15269                     var d = Date.dayNames[i];
15270                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15271
15272                 }
15273                 row.cn[0].cls+=' fc-first';
15274                 row.cn[0].cn[0].style = 'min-height:90px';
15275                 row.cn[6].cls+=' fc-last';
15276                 ret.push(row);
15277                 
15278             }
15279             ret[0].cls += ' fc-first';
15280             ret[4].cls += ' fc-prev-last';
15281             ret[5].cls += ' fc-last';
15282             return ret;
15283             
15284         };
15285         
15286         var cal_table = {
15287             tag: 'table',
15288             cls: 'fc-border-separate',
15289             style : 'width:100%',
15290             cellspacing  : 0,
15291             cn : [
15292                 { 
15293                     tag: 'thead',
15294                     cn : [
15295                         { 
15296                             tag: 'tr',
15297                             cls : 'fc-first fc-last',
15298                             cn : cal_heads()
15299                         }
15300                     ]
15301                 },
15302                 { 
15303                     tag: 'tbody',
15304                     cn : cal_rows()
15305                 }
15306                   
15307             ]
15308         };
15309          
15310          var cfg = {
15311             cls : 'fc fc-ltr',
15312             cn : [
15313                 header,
15314                 {
15315                     cls : 'fc-content',
15316                     style : "position: relative;",
15317                     cn : [
15318                         {
15319                             cls : 'fc-view fc-view-month fc-grid',
15320                             style : 'position: relative',
15321                             unselectable : 'on',
15322                             cn : [
15323                                 {
15324                                     cls : 'fc-event-container',
15325                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15326                                 },
15327                                 cal_table
15328                             ]
15329                         }
15330                     ]
15331     
15332                 }
15333            ] 
15334             
15335         };
15336         
15337          
15338         
15339         return cfg;
15340     },
15341     
15342     
15343     initEvents : function()
15344     {
15345         if(!this.store){
15346             throw "can not find store for calendar";
15347         }
15348         
15349         var mark = {
15350             tag: "div",
15351             cls:"x-dlg-mask",
15352             style: "text-align:center",
15353             cn: [
15354                 {
15355                     tag: "div",
15356                     style: "background-color:white;width:50%;margin:250 auto",
15357                     cn: [
15358                         {
15359                             tag: "img",
15360                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15361                         },
15362                         {
15363                             tag: "span",
15364                             html: "Loading"
15365                         }
15366                         
15367                     ]
15368                 }
15369             ]
15370         };
15371         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15372         
15373         var size = this.el.select('.fc-content', true).first().getSize();
15374         this.maskEl.setSize(size.width, size.height);
15375         this.maskEl.enableDisplayMode("block");
15376         if(!this.loadMask){
15377             this.maskEl.hide();
15378         }
15379         
15380         this.store = Roo.factory(this.store, Roo.data);
15381         this.store.on('load', this.onLoad, this);
15382         this.store.on('beforeload', this.onBeforeLoad, this);
15383         
15384         this.resize();
15385         
15386         this.cells = this.el.select('.fc-day',true);
15387         //Roo.log(this.cells);
15388         this.textNodes = this.el.query('.fc-day-number');
15389         this.cells.addClassOnOver('fc-state-hover');
15390         
15391         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15392         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15393         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15394         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15395         
15396         this.on('monthchange', this.onMonthChange, this);
15397         
15398         this.update(new Date().clearTime());
15399     },
15400     
15401     resize : function() {
15402         var sz  = this.el.getSize();
15403         
15404         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15405         this.el.select('.fc-day-content div',true).setHeight(34);
15406     },
15407     
15408     
15409     // private
15410     showPrevMonth : function(e){
15411         this.update(this.activeDate.add("mo", -1));
15412     },
15413     showToday : function(e){
15414         this.update(new Date().clearTime());
15415     },
15416     // private
15417     showNextMonth : function(e){
15418         this.update(this.activeDate.add("mo", 1));
15419     },
15420
15421     // private
15422     showPrevYear : function(){
15423         this.update(this.activeDate.add("y", -1));
15424     },
15425
15426     // private
15427     showNextYear : function(){
15428         this.update(this.activeDate.add("y", 1));
15429     },
15430
15431     
15432    // private
15433     update : function(date)
15434     {
15435         var vd = this.activeDate;
15436         this.activeDate = date;
15437 //        if(vd && this.el){
15438 //            var t = date.getTime();
15439 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15440 //                Roo.log('using add remove');
15441 //                
15442 //                this.fireEvent('monthchange', this, date);
15443 //                
15444 //                this.cells.removeClass("fc-state-highlight");
15445 //                this.cells.each(function(c){
15446 //                   if(c.dateValue == t){
15447 //                       c.addClass("fc-state-highlight");
15448 //                       setTimeout(function(){
15449 //                            try{c.dom.firstChild.focus();}catch(e){}
15450 //                       }, 50);
15451 //                       return false;
15452 //                   }
15453 //                   return true;
15454 //                });
15455 //                return;
15456 //            }
15457 //        }
15458         
15459         var days = date.getDaysInMonth();
15460         
15461         var firstOfMonth = date.getFirstDateOfMonth();
15462         var startingPos = firstOfMonth.getDay()-this.startDay;
15463         
15464         if(startingPos < this.startDay){
15465             startingPos += 7;
15466         }
15467         
15468         var pm = date.add(Date.MONTH, -1);
15469         var prevStart = pm.getDaysInMonth()-startingPos;
15470 //        
15471         this.cells = this.el.select('.fc-day',true);
15472         this.textNodes = this.el.query('.fc-day-number');
15473         this.cells.addClassOnOver('fc-state-hover');
15474         
15475         var cells = this.cells.elements;
15476         var textEls = this.textNodes;
15477         
15478         Roo.each(cells, function(cell){
15479             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15480         });
15481         
15482         days += startingPos;
15483
15484         // convert everything to numbers so it's fast
15485         var day = 86400000;
15486         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15487         //Roo.log(d);
15488         //Roo.log(pm);
15489         //Roo.log(prevStart);
15490         
15491         var today = new Date().clearTime().getTime();
15492         var sel = date.clearTime().getTime();
15493         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15494         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15495         var ddMatch = this.disabledDatesRE;
15496         var ddText = this.disabledDatesText;
15497         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15498         var ddaysText = this.disabledDaysText;
15499         var format = this.format;
15500         
15501         var setCellClass = function(cal, cell){
15502             cell.row = 0;
15503             cell.events = [];
15504             cell.more = [];
15505             //Roo.log('set Cell Class');
15506             cell.title = "";
15507             var t = d.getTime();
15508             
15509             //Roo.log(d);
15510             
15511             cell.dateValue = t;
15512             if(t == today){
15513                 cell.className += " fc-today";
15514                 cell.className += " fc-state-highlight";
15515                 cell.title = cal.todayText;
15516             }
15517             if(t == sel){
15518                 // disable highlight in other month..
15519                 //cell.className += " fc-state-highlight";
15520                 
15521             }
15522             // disabling
15523             if(t < min) {
15524                 cell.className = " fc-state-disabled";
15525                 cell.title = cal.minText;
15526                 return;
15527             }
15528             if(t > max) {
15529                 cell.className = " fc-state-disabled";
15530                 cell.title = cal.maxText;
15531                 return;
15532             }
15533             if(ddays){
15534                 if(ddays.indexOf(d.getDay()) != -1){
15535                     cell.title = ddaysText;
15536                     cell.className = " fc-state-disabled";
15537                 }
15538             }
15539             if(ddMatch && format){
15540                 var fvalue = d.dateFormat(format);
15541                 if(ddMatch.test(fvalue)){
15542                     cell.title = ddText.replace("%0", fvalue);
15543                     cell.className = " fc-state-disabled";
15544                 }
15545             }
15546             
15547             if (!cell.initialClassName) {
15548                 cell.initialClassName = cell.dom.className;
15549             }
15550             
15551             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15552         };
15553
15554         var i = 0;
15555         
15556         for(; i < startingPos; i++) {
15557             textEls[i].innerHTML = (++prevStart);
15558             d.setDate(d.getDate()+1);
15559             
15560             cells[i].className = "fc-past fc-other-month";
15561             setCellClass(this, cells[i]);
15562         }
15563         
15564         var intDay = 0;
15565         
15566         for(; i < days; i++){
15567             intDay = i - startingPos + 1;
15568             textEls[i].innerHTML = (intDay);
15569             d.setDate(d.getDate()+1);
15570             
15571             cells[i].className = ''; // "x-date-active";
15572             setCellClass(this, cells[i]);
15573         }
15574         var extraDays = 0;
15575         
15576         for(; i < 42; i++) {
15577             textEls[i].innerHTML = (++extraDays);
15578             d.setDate(d.getDate()+1);
15579             
15580             cells[i].className = "fc-future fc-other-month";
15581             setCellClass(this, cells[i]);
15582         }
15583         
15584         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15585         
15586         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15587         
15588         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15589         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15590         
15591         if(totalRows != 6){
15592             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15593             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15594         }
15595         
15596         this.fireEvent('monthchange', this, date);
15597         
15598         
15599         /*
15600         if(!this.internalRender){
15601             var main = this.el.dom.firstChild;
15602             var w = main.offsetWidth;
15603             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15604             Roo.fly(main).setWidth(w);
15605             this.internalRender = true;
15606             // opera does not respect the auto grow header center column
15607             // then, after it gets a width opera refuses to recalculate
15608             // without a second pass
15609             if(Roo.isOpera && !this.secondPass){
15610                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15611                 this.secondPass = true;
15612                 this.update.defer(10, this, [date]);
15613             }
15614         }
15615         */
15616         
15617     },
15618     
15619     findCell : function(dt) {
15620         dt = dt.clearTime().getTime();
15621         var ret = false;
15622         this.cells.each(function(c){
15623             //Roo.log("check " +c.dateValue + '?=' + dt);
15624             if(c.dateValue == dt){
15625                 ret = c;
15626                 return false;
15627             }
15628             return true;
15629         });
15630         
15631         return ret;
15632     },
15633     
15634     findCells : function(ev) {
15635         var s = ev.start.clone().clearTime().getTime();
15636        // Roo.log(s);
15637         var e= ev.end.clone().clearTime().getTime();
15638        // Roo.log(e);
15639         var ret = [];
15640         this.cells.each(function(c){
15641              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15642             
15643             if(c.dateValue > e){
15644                 return ;
15645             }
15646             if(c.dateValue < s){
15647                 return ;
15648             }
15649             ret.push(c);
15650         });
15651         
15652         return ret;    
15653     },
15654     
15655 //    findBestRow: function(cells)
15656 //    {
15657 //        var ret = 0;
15658 //        
15659 //        for (var i =0 ; i < cells.length;i++) {
15660 //            ret  = Math.max(cells[i].rows || 0,ret);
15661 //        }
15662 //        return ret;
15663 //        
15664 //    },
15665     
15666     
15667     addItem : function(ev)
15668     {
15669         // look for vertical location slot in
15670         var cells = this.findCells(ev);
15671         
15672 //        ev.row = this.findBestRow(cells);
15673         
15674         // work out the location.
15675         
15676         var crow = false;
15677         var rows = [];
15678         for(var i =0; i < cells.length; i++) {
15679             
15680             cells[i].row = cells[0].row;
15681             
15682             if(i == 0){
15683                 cells[i].row = cells[i].row + 1;
15684             }
15685             
15686             if (!crow) {
15687                 crow = {
15688                     start : cells[i],
15689                     end :  cells[i]
15690                 };
15691                 continue;
15692             }
15693             if (crow.start.getY() == cells[i].getY()) {
15694                 // on same row.
15695                 crow.end = cells[i];
15696                 continue;
15697             }
15698             // different row.
15699             rows.push(crow);
15700             crow = {
15701                 start: cells[i],
15702                 end : cells[i]
15703             };
15704             
15705         }
15706         
15707         rows.push(crow);
15708         ev.els = [];
15709         ev.rows = rows;
15710         ev.cells = cells;
15711         
15712         cells[0].events.push(ev);
15713         
15714         this.calevents.push(ev);
15715     },
15716     
15717     clearEvents: function() {
15718         
15719         if(!this.calevents){
15720             return;
15721         }
15722         
15723         Roo.each(this.cells.elements, function(c){
15724             c.row = 0;
15725             c.events = [];
15726             c.more = [];
15727         });
15728         
15729         Roo.each(this.calevents, function(e) {
15730             Roo.each(e.els, function(el) {
15731                 el.un('mouseenter' ,this.onEventEnter, this);
15732                 el.un('mouseleave' ,this.onEventLeave, this);
15733                 el.remove();
15734             },this);
15735         },this);
15736         
15737         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15738             e.remove();
15739         });
15740         
15741     },
15742     
15743     renderEvents: function()
15744     {   
15745         var _this = this;
15746         
15747         this.cells.each(function(c) {
15748             
15749             if(c.row < 5){
15750                 return;
15751             }
15752             
15753             var ev = c.events;
15754             
15755             var r = 4;
15756             if(c.row != c.events.length){
15757                 r = 4 - (4 - (c.row - c.events.length));
15758             }
15759             
15760             c.events = ev.slice(0, r);
15761             c.more = ev.slice(r);
15762             
15763             if(c.more.length && c.more.length == 1){
15764                 c.events.push(c.more.pop());
15765             }
15766             
15767             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15768             
15769         });
15770             
15771         this.cells.each(function(c) {
15772             
15773             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15774             
15775             
15776             for (var e = 0; e < c.events.length; e++){
15777                 var ev = c.events[e];
15778                 var rows = ev.rows;
15779                 
15780                 for(var i = 0; i < rows.length; i++) {
15781                 
15782                     // how many rows should it span..
15783
15784                     var  cfg = {
15785                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15786                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15787
15788                         unselectable : "on",
15789                         cn : [
15790                             {
15791                                 cls: 'fc-event-inner',
15792                                 cn : [
15793     //                                {
15794     //                                  tag:'span',
15795     //                                  cls: 'fc-event-time',
15796     //                                  html : cells.length > 1 ? '' : ev.time
15797     //                                },
15798                                     {
15799                                       tag:'span',
15800                                       cls: 'fc-event-title',
15801                                       html : String.format('{0}', ev.title)
15802                                     }
15803
15804
15805                                 ]
15806                             },
15807                             {
15808                                 cls: 'ui-resizable-handle ui-resizable-e',
15809                                 html : '&nbsp;&nbsp;&nbsp'
15810                             }
15811
15812                         ]
15813                     };
15814
15815                     if (i == 0) {
15816                         cfg.cls += ' fc-event-start';
15817                     }
15818                     if ((i+1) == rows.length) {
15819                         cfg.cls += ' fc-event-end';
15820                     }
15821
15822                     var ctr = _this.el.select('.fc-event-container',true).first();
15823                     var cg = ctr.createChild(cfg);
15824
15825                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15826                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15827
15828                     var r = (c.more.length) ? 1 : 0;
15829                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15830                     cg.setWidth(ebox.right - sbox.x -2);
15831
15832                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15833                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15834                     cg.on('click', _this.onEventClick, _this, ev);
15835
15836                     ev.els.push(cg);
15837                     
15838                 }
15839                 
15840             }
15841             
15842             
15843             if(c.more.length){
15844                 var  cfg = {
15845                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15846                     style : 'position: absolute',
15847                     unselectable : "on",
15848                     cn : [
15849                         {
15850                             cls: 'fc-event-inner',
15851                             cn : [
15852                                 {
15853                                   tag:'span',
15854                                   cls: 'fc-event-title',
15855                                   html : 'More'
15856                                 }
15857
15858
15859                             ]
15860                         },
15861                         {
15862                             cls: 'ui-resizable-handle ui-resizable-e',
15863                             html : '&nbsp;&nbsp;&nbsp'
15864                         }
15865
15866                     ]
15867                 };
15868
15869                 var ctr = _this.el.select('.fc-event-container',true).first();
15870                 var cg = ctr.createChild(cfg);
15871
15872                 var sbox = c.select('.fc-day-content',true).first().getBox();
15873                 var ebox = c.select('.fc-day-content',true).first().getBox();
15874                 //Roo.log(cg);
15875                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15876                 cg.setWidth(ebox.right - sbox.x -2);
15877
15878                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15879                 
15880             }
15881             
15882         });
15883         
15884         
15885         
15886     },
15887     
15888     onEventEnter: function (e, el,event,d) {
15889         this.fireEvent('evententer', this, el, event);
15890     },
15891     
15892     onEventLeave: function (e, el,event,d) {
15893         this.fireEvent('eventleave', this, el, event);
15894     },
15895     
15896     onEventClick: function (e, el,event,d) {
15897         this.fireEvent('eventclick', this, el, event);
15898     },
15899     
15900     onMonthChange: function () {
15901         this.store.load();
15902     },
15903     
15904     onMoreEventClick: function(e, el, more)
15905     {
15906         var _this = this;
15907         
15908         this.calpopover.placement = 'right';
15909         this.calpopover.setTitle('More');
15910         
15911         this.calpopover.setContent('');
15912         
15913         var ctr = this.calpopover.el.select('.popover-content', true).first();
15914         
15915         Roo.each(more, function(m){
15916             var cfg = {
15917                 cls : 'fc-event-hori fc-event-draggable',
15918                 html : m.title
15919             };
15920             var cg = ctr.createChild(cfg);
15921             
15922             cg.on('click', _this.onEventClick, _this, m);
15923         });
15924         
15925         this.calpopover.show(el);
15926         
15927         
15928     },
15929     
15930     onLoad: function () 
15931     {   
15932         this.calevents = [];
15933         var cal = this;
15934         
15935         if(this.store.getCount() > 0){
15936             this.store.data.each(function(d){
15937                cal.addItem({
15938                     id : d.data.id,
15939                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15940                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15941                     time : d.data.start_time,
15942                     title : d.data.title,
15943                     description : d.data.description,
15944                     venue : d.data.venue
15945                 });
15946             });
15947         }
15948         
15949         this.renderEvents();
15950         
15951         if(this.calevents.length && this.loadMask){
15952             this.maskEl.hide();
15953         }
15954     },
15955     
15956     onBeforeLoad: function()
15957     {
15958         this.clearEvents();
15959         if(this.loadMask){
15960             this.maskEl.show();
15961         }
15962     }
15963 });
15964
15965  
15966  /*
15967  * - LGPL
15968  *
15969  * element
15970  * 
15971  */
15972
15973 /**
15974  * @class Roo.bootstrap.Popover
15975  * @extends Roo.bootstrap.Component
15976  * Bootstrap Popover class
15977  * @cfg {String} html contents of the popover   (or false to use children..)
15978  * @cfg {String} title of popover (or false to hide)
15979  * @cfg {String} placement how it is placed
15980  * @cfg {String} trigger click || hover (or false to trigger manually)
15981  * @cfg {String} over what (parent or false to trigger manually.)
15982  * @cfg {Number} delay - delay before showing
15983  
15984  * @constructor
15985  * Create a new Popover
15986  * @param {Object} config The config object
15987  */
15988
15989 Roo.bootstrap.Popover = function(config){
15990     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15991     
15992     this.addEvents({
15993         // raw events
15994          /**
15995          * @event show
15996          * After the popover show
15997          * 
15998          * @param {Roo.bootstrap.Popover} this
15999          */
16000         "show" : true,
16001         /**
16002          * @event hide
16003          * After the popover hide
16004          * 
16005          * @param {Roo.bootstrap.Popover} this
16006          */
16007         "hide" : true
16008     });
16009 };
16010
16011 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16012     
16013     title: 'Fill in a title',
16014     html: false,
16015     
16016     placement : 'right',
16017     trigger : 'hover', // hover
16018     
16019     delay : 0,
16020     
16021     over: 'parent',
16022     
16023     can_build_overlaid : false,
16024     
16025     getChildContainer : function()
16026     {
16027         return this.el.select('.popover-content',true).first();
16028     },
16029     
16030     getAutoCreate : function(){
16031          
16032         var cfg = {
16033            cls : 'popover roo-dynamic',
16034            style: 'display:block',
16035            cn : [
16036                 {
16037                     cls : 'arrow'
16038                 },
16039                 {
16040                     cls : 'popover-inner',
16041                     cn : [
16042                         {
16043                             tag: 'h3',
16044                             cls: 'popover-title',
16045                             html : this.title
16046                         },
16047                         {
16048                             cls : 'popover-content',
16049                             html : this.html
16050                         }
16051                     ]
16052                     
16053                 }
16054            ]
16055         };
16056         
16057         return cfg;
16058     },
16059     setTitle: function(str)
16060     {
16061         this.title = str;
16062         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16063     },
16064     setContent: function(str)
16065     {
16066         this.html = str;
16067         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16068     },
16069     // as it get's added to the bottom of the page.
16070     onRender : function(ct, position)
16071     {
16072         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16073         if(!this.el){
16074             var cfg = Roo.apply({},  this.getAutoCreate());
16075             cfg.id = Roo.id();
16076             
16077             if (this.cls) {
16078                 cfg.cls += ' ' + this.cls;
16079             }
16080             if (this.style) {
16081                 cfg.style = this.style;
16082             }
16083             //Roo.log("adding to ");
16084             this.el = Roo.get(document.body).createChild(cfg, position);
16085 //            Roo.log(this.el);
16086         }
16087         this.initEvents();
16088     },
16089     
16090     initEvents : function()
16091     {
16092         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16093         this.el.enableDisplayMode('block');
16094         this.el.hide();
16095         if (this.over === false) {
16096             return; 
16097         }
16098         if (this.triggers === false) {
16099             return;
16100         }
16101         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16102         var triggers = this.trigger ? this.trigger.split(' ') : [];
16103         Roo.each(triggers, function(trigger) {
16104         
16105             if (trigger == 'click') {
16106                 on_el.on('click', this.toggle, this);
16107             } else if (trigger != 'manual') {
16108                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16109                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16110       
16111                 on_el.on(eventIn  ,this.enter, this);
16112                 on_el.on(eventOut, this.leave, this);
16113             }
16114         }, this);
16115         
16116     },
16117     
16118     
16119     // private
16120     timeout : null,
16121     hoverState : null,
16122     
16123     toggle : function () {
16124         this.hoverState == 'in' ? this.leave() : this.enter();
16125     },
16126     
16127     enter : function () {
16128         
16129         clearTimeout(this.timeout);
16130     
16131         this.hoverState = 'in';
16132     
16133         if (!this.delay || !this.delay.show) {
16134             this.show();
16135             return;
16136         }
16137         var _t = this;
16138         this.timeout = setTimeout(function () {
16139             if (_t.hoverState == 'in') {
16140                 _t.show();
16141             }
16142         }, this.delay.show)
16143     },
16144     
16145     leave : function() {
16146         clearTimeout(this.timeout);
16147     
16148         this.hoverState = 'out';
16149     
16150         if (!this.delay || !this.delay.hide) {
16151             this.hide();
16152             return;
16153         }
16154         var _t = this;
16155         this.timeout = setTimeout(function () {
16156             if (_t.hoverState == 'out') {
16157                 _t.hide();
16158             }
16159         }, this.delay.hide)
16160     },
16161     
16162     show : function (on_el)
16163     {
16164         if (!on_el) {
16165             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16166         }
16167         
16168         // set content.
16169         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16170         if (this.html !== false) {
16171             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16172         }
16173         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16174         if (!this.title.length) {
16175             this.el.select('.popover-title',true).hide();
16176         }
16177         
16178         var placement = typeof this.placement == 'function' ?
16179             this.placement.call(this, this.el, on_el) :
16180             this.placement;
16181             
16182         var autoToken = /\s?auto?\s?/i;
16183         var autoPlace = autoToken.test(placement);
16184         if (autoPlace) {
16185             placement = placement.replace(autoToken, '') || 'top';
16186         }
16187         
16188         //this.el.detach()
16189         //this.el.setXY([0,0]);
16190         this.el.show();
16191         this.el.dom.style.display='block';
16192         this.el.addClass(placement);
16193         
16194         //this.el.appendTo(on_el);
16195         
16196         var p = this.getPosition();
16197         var box = this.el.getBox();
16198         
16199         if (autoPlace) {
16200             // fixme..
16201         }
16202         var align = Roo.bootstrap.Popover.alignment[placement];
16203         this.el.alignTo(on_el, align[0],align[1]);
16204         //var arrow = this.el.select('.arrow',true).first();
16205         //arrow.set(align[2], 
16206         
16207         this.el.addClass('in');
16208         
16209         
16210         if (this.el.hasClass('fade')) {
16211             // fade it?
16212         }
16213         
16214         this.hoverState = 'in';
16215         
16216         this.fireEvent('show', this);
16217         
16218     },
16219     hide : function()
16220     {
16221         this.el.setXY([0,0]);
16222         this.el.removeClass('in');
16223         this.el.hide();
16224         this.hoverState = null;
16225         
16226         this.fireEvent('hide', this);
16227     }
16228     
16229 });
16230
16231 Roo.bootstrap.Popover.alignment = {
16232     'left' : ['r-l', [-10,0], 'right'],
16233     'right' : ['l-r', [10,0], 'left'],
16234     'bottom' : ['t-b', [0,10], 'top'],
16235     'top' : [ 'b-t', [0,-10], 'bottom']
16236 };
16237
16238  /*
16239  * - LGPL
16240  *
16241  * Progress
16242  * 
16243  */
16244
16245 /**
16246  * @class Roo.bootstrap.Progress
16247  * @extends Roo.bootstrap.Component
16248  * Bootstrap Progress class
16249  * @cfg {Boolean} striped striped of the progress bar
16250  * @cfg {Boolean} active animated of the progress bar
16251  * 
16252  * 
16253  * @constructor
16254  * Create a new Progress
16255  * @param {Object} config The config object
16256  */
16257
16258 Roo.bootstrap.Progress = function(config){
16259     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16260 };
16261
16262 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16263     
16264     striped : false,
16265     active: false,
16266     
16267     getAutoCreate : function(){
16268         var cfg = {
16269             tag: 'div',
16270             cls: 'progress'
16271         };
16272         
16273         
16274         if(this.striped){
16275             cfg.cls += ' progress-striped';
16276         }
16277       
16278         if(this.active){
16279             cfg.cls += ' active';
16280         }
16281         
16282         
16283         return cfg;
16284     }
16285    
16286 });
16287
16288  
16289
16290  /*
16291  * - LGPL
16292  *
16293  * ProgressBar
16294  * 
16295  */
16296
16297 /**
16298  * @class Roo.bootstrap.ProgressBar
16299  * @extends Roo.bootstrap.Component
16300  * Bootstrap ProgressBar class
16301  * @cfg {Number} aria_valuenow aria-value now
16302  * @cfg {Number} aria_valuemin aria-value min
16303  * @cfg {Number} aria_valuemax aria-value max
16304  * @cfg {String} label label for the progress bar
16305  * @cfg {String} panel (success | info | warning | danger )
16306  * @cfg {String} role role of the progress bar
16307  * @cfg {String} sr_only text
16308  * 
16309  * 
16310  * @constructor
16311  * Create a new ProgressBar
16312  * @param {Object} config The config object
16313  */
16314
16315 Roo.bootstrap.ProgressBar = function(config){
16316     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16317 };
16318
16319 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16320     
16321     aria_valuenow : 0,
16322     aria_valuemin : 0,
16323     aria_valuemax : 100,
16324     label : false,
16325     panel : false,
16326     role : false,
16327     sr_only: false,
16328     
16329     getAutoCreate : function()
16330     {
16331         
16332         var cfg = {
16333             tag: 'div',
16334             cls: 'progress-bar',
16335             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16336         };
16337         
16338         if(this.sr_only){
16339             cfg.cn = {
16340                 tag: 'span',
16341                 cls: 'sr-only',
16342                 html: this.sr_only
16343             }
16344         }
16345         
16346         if(this.role){
16347             cfg.role = this.role;
16348         }
16349         
16350         if(this.aria_valuenow){
16351             cfg['aria-valuenow'] = this.aria_valuenow;
16352         }
16353         
16354         if(this.aria_valuemin){
16355             cfg['aria-valuemin'] = this.aria_valuemin;
16356         }
16357         
16358         if(this.aria_valuemax){
16359             cfg['aria-valuemax'] = this.aria_valuemax;
16360         }
16361         
16362         if(this.label && !this.sr_only){
16363             cfg.html = this.label;
16364         }
16365         
16366         if(this.panel){
16367             cfg.cls += ' progress-bar-' + this.panel;
16368         }
16369         
16370         return cfg;
16371     },
16372     
16373     update : function(aria_valuenow)
16374     {
16375         this.aria_valuenow = aria_valuenow;
16376         
16377         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16378     }
16379    
16380 });
16381
16382  
16383
16384  /*
16385  * - LGPL
16386  *
16387  * column
16388  * 
16389  */
16390
16391 /**
16392  * @class Roo.bootstrap.TabGroup
16393  * @extends Roo.bootstrap.Column
16394  * Bootstrap Column class
16395  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16396  * @cfg {Boolean} carousel true to make the group behave like a carousel
16397  * @cfg {Boolean} bullets show bullets for the panels
16398  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16399  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16400  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16401  * 
16402  * @constructor
16403  * Create a new TabGroup
16404  * @param {Object} config The config object
16405  */
16406
16407 Roo.bootstrap.TabGroup = function(config){
16408     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16409     if (!this.navId) {
16410         this.navId = Roo.id();
16411     }
16412     this.tabs = [];
16413     Roo.bootstrap.TabGroup.register(this);
16414     
16415 };
16416
16417 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16418     
16419     carousel : false,
16420     transition : false,
16421     bullets : 0,
16422     timer : 0,
16423     autoslide : false,
16424     slideFn : false,
16425     slideOnTouch : false,
16426     
16427     getAutoCreate : function()
16428     {
16429         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16430         
16431         cfg.cls += ' tab-content';
16432         
16433         if (this.carousel) {
16434             cfg.cls += ' carousel slide';
16435             
16436             cfg.cn = [{
16437                cls : 'carousel-inner'
16438             }];
16439         
16440             if(this.bullets  && !Roo.isTouch){
16441                 
16442                 var bullets = {
16443                     cls : 'carousel-bullets',
16444                     cn : []
16445                 };
16446                
16447                 if(this.bullets_cls){
16448                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16449                 }
16450                  /*
16451                 for (var i = 0; i < this.bullets; i++){
16452                     bullets.cn.push({
16453                         cls : 'bullet bullet-' + i
16454                     });
16455                 }
16456                 */
16457                 bullets.cn.push({
16458                     cls : 'clear'
16459                 });
16460                 
16461                 cfg.cn[0].cn = bullets;
16462             }
16463         }
16464         
16465         return cfg;
16466     },
16467     
16468     initEvents:  function()
16469     {
16470         if(Roo.isTouch && this.slideOnTouch){
16471             this.el.on("touchstart", this.onTouchStart, this);
16472         }
16473         
16474         if(this.autoslide){
16475             var _this = this;
16476             
16477             this.slideFn = window.setInterval(function() {
16478                 _this.showPanelNext();
16479             }, this.timer);
16480         }
16481         
16482         if(this.tabs.length < 2){
16483             var bulletsEl = this.el.select('.carousel-bullets',true).first();
16484             bulletsEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
16485         }
16486         
16487     },
16488     
16489     onTouchStart : function(e, el, o)
16490     {
16491         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16492             return;
16493         }
16494         
16495         this.showPanelNext();
16496     },
16497     
16498     getChildContainer : function()
16499     {
16500         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16501     },
16502     
16503     /**
16504     * register a Navigation item
16505     * @param {Roo.bootstrap.NavItem} the navitem to add
16506     */
16507     register : function(item)
16508     {
16509         this.tabs.push( item);
16510         item.navId = this.navId; // not really needed..
16511         this.addBullet();
16512     
16513     },
16514     
16515     getActivePanel : function()
16516     {
16517         var r = false;
16518         Roo.each(this.tabs, function(t) {
16519             if (t.active) {
16520                 r = t;
16521                 return false;
16522             }
16523             return null;
16524         });
16525         return r;
16526         
16527     },
16528     getPanelByName : function(n)
16529     {
16530         var r = false;
16531         Roo.each(this.tabs, function(t) {
16532             if (t.tabId == n) {
16533                 r = t;
16534                 return false;
16535             }
16536             return null;
16537         });
16538         return r;
16539     },
16540     indexOfPanel : function(p)
16541     {
16542         var r = false;
16543         Roo.each(this.tabs, function(t,i) {
16544             if (t.tabId == p.tabId) {
16545                 r = i;
16546                 return false;
16547             }
16548             return null;
16549         });
16550         return r;
16551     },
16552     /**
16553      * show a specific panel
16554      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16555      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16556      */
16557     showPanel : function (pan)
16558     {
16559         if(this.transition || typeof(pan) == 'undefined'){
16560             Roo.log("waiting for the transitionend");
16561             return;
16562         }
16563         
16564         if (typeof(pan) == 'number') {
16565             pan = this.tabs[pan];
16566         }
16567         
16568         if (typeof(pan) == 'string') {
16569             pan = this.getPanelByName(pan);
16570         }
16571         
16572         var cur = this.getActivePanel();
16573         
16574         if(!pan || !cur){
16575             Roo.log('pan or acitve pan is undefined');
16576             return false;
16577         }
16578         
16579         if (pan.tabId == this.getActivePanel().tabId) {
16580             return true;
16581         }
16582         
16583         if (false === cur.fireEvent('beforedeactivate')) {
16584             return false;
16585         }
16586         
16587         if(this.bullets > 0 && !Roo.isTouch){
16588             this.setActiveBullet(this.indexOfPanel(pan));
16589         }
16590         
16591         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16592             
16593             this.transition = true;
16594             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16595             var lr = dir == 'next' ? 'left' : 'right';
16596             pan.el.addClass(dir); // or prev
16597             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16598             cur.el.addClass(lr); // or right
16599             pan.el.addClass(lr);
16600             
16601             var _this = this;
16602             cur.el.on('transitionend', function() {
16603                 Roo.log("trans end?");
16604                 
16605                 pan.el.removeClass([lr,dir]);
16606                 pan.setActive(true);
16607                 
16608                 cur.el.removeClass([lr]);
16609                 cur.setActive(false);
16610                 
16611                 _this.transition = false;
16612                 
16613             }, this, { single:  true } );
16614             
16615             return true;
16616         }
16617         
16618         cur.setActive(false);
16619         pan.setActive(true);
16620         
16621         return true;
16622         
16623     },
16624     showPanelNext : function()
16625     {
16626         var i = this.indexOfPanel(this.getActivePanel());
16627         
16628         if (i >= this.tabs.length - 1 && !this.autoslide) {
16629             return;
16630         }
16631         
16632         if (i >= this.tabs.length - 1 && this.autoslide) {
16633             i = -1;
16634         }
16635         
16636         this.showPanel(this.tabs[i+1]);
16637     },
16638     
16639     showPanelPrev : function()
16640     {
16641         var i = this.indexOfPanel(this.getActivePanel());
16642         
16643         if (i  < 1 && !this.autoslide) {
16644             return;
16645         }
16646         
16647         if (i < 1 && this.autoslide) {
16648             i = this.tabs.length;
16649         }
16650         
16651         this.showPanel(this.tabs[i-1]);
16652     },
16653     
16654     
16655     addBullet: function()
16656     {
16657         if(!this.bullets || Roo.isTouch){
16658             return;
16659         }
16660         var ctr = this.el.select('.carousel-bullets',true).first();
16661         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16662         var bullet = ctr.createChild({
16663             cls : 'bullet bullet-' + i
16664         },ctr.dom.lastChild);
16665         
16666         
16667         var _this = this;
16668         
16669         bullet.on('click', (function(e, el, o, ii, t){
16670
16671             e.preventDefault();
16672
16673             this.showPanel(ii);
16674
16675             if(this.autoslide && this.slideFn){
16676                 clearInterval(this.slideFn);
16677                 this.slideFn = window.setInterval(function() {
16678                     _this.showPanelNext();
16679                 }, this.timer);
16680             }
16681
16682         }).createDelegate(this, [i, bullet], true));
16683                 
16684         
16685     },
16686      
16687     setActiveBullet : function(i)
16688     {
16689         if(Roo.isTouch){
16690             return;
16691         }
16692         
16693         Roo.each(this.el.select('.bullet', true).elements, function(el){
16694             el.removeClass('selected');
16695         });
16696
16697         var bullet = this.el.select('.bullet-' + i, true).first();
16698         
16699         if(!bullet){
16700             return;
16701         }
16702         
16703         bullet.addClass('selected');
16704     }
16705     
16706     
16707   
16708 });
16709
16710  
16711
16712  
16713  
16714 Roo.apply(Roo.bootstrap.TabGroup, {
16715     
16716     groups: {},
16717      /**
16718     * register a Navigation Group
16719     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16720     */
16721     register : function(navgrp)
16722     {
16723         this.groups[navgrp.navId] = navgrp;
16724         
16725     },
16726     /**
16727     * fetch a Navigation Group based on the navigation ID
16728     * if one does not exist , it will get created.
16729     * @param {string} the navgroup to add
16730     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16731     */
16732     get: function(navId) {
16733         if (typeof(this.groups[navId]) == 'undefined') {
16734             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16735         }
16736         return this.groups[navId] ;
16737     }
16738     
16739     
16740     
16741 });
16742
16743  /*
16744  * - LGPL
16745  *
16746  * TabPanel
16747  * 
16748  */
16749
16750 /**
16751  * @class Roo.bootstrap.TabPanel
16752  * @extends Roo.bootstrap.Component
16753  * Bootstrap TabPanel class
16754  * @cfg {Boolean} active panel active
16755  * @cfg {String} html panel content
16756  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16757  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16758  * 
16759  * 
16760  * @constructor
16761  * Create a new TabPanel
16762  * @param {Object} config The config object
16763  */
16764
16765 Roo.bootstrap.TabPanel = function(config){
16766     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16767     this.addEvents({
16768         /**
16769              * @event changed
16770              * Fires when the active status changes
16771              * @param {Roo.bootstrap.TabPanel} this
16772              * @param {Boolean} state the new state
16773             
16774          */
16775         'changed': true,
16776         /**
16777              * @event beforedeactivate
16778              * Fires before a tab is de-activated - can be used to do validation on a form.
16779              * @param {Roo.bootstrap.TabPanel} this
16780              * @return {Boolean} false if there is an error
16781             
16782          */
16783         'beforedeactivate': true
16784      });
16785     
16786     this.tabId = this.tabId || Roo.id();
16787   
16788 };
16789
16790 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16791     
16792     active: false,
16793     html: false,
16794     tabId: false,
16795     navId : false,
16796     
16797     getAutoCreate : function(){
16798         var cfg = {
16799             tag: 'div',
16800             // item is needed for carousel - not sure if it has any effect otherwise
16801             cls: 'tab-pane item',
16802             html: this.html || ''
16803         };
16804         
16805         if(this.active){
16806             cfg.cls += ' active';
16807         }
16808         
16809         if(this.tabId){
16810             cfg.tabId = this.tabId;
16811         }
16812         
16813         
16814         return cfg;
16815     },
16816     
16817     initEvents:  function()
16818     {
16819         var p = this.parent();
16820         this.navId = this.navId || p.navId;
16821         
16822         if (typeof(this.navId) != 'undefined') {
16823             // not really needed.. but just in case.. parent should be a NavGroup.
16824             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16825             
16826             tg.register(this);
16827             
16828             var i = tg.tabs.length - 1;
16829             
16830             if(this.active && tg.bullets > 0 && i < tg.bullets){
16831                 tg.setActiveBullet(i);
16832             }
16833         }
16834         
16835     },
16836     
16837     
16838     onRender : function(ct, position)
16839     {
16840        // Roo.log("Call onRender: " + this.xtype);
16841         
16842         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16843         
16844         
16845         
16846         
16847         
16848     },
16849     
16850     setActive: function(state)
16851     {
16852         Roo.log("panel - set active " + this.tabId + "=" + state);
16853         
16854         this.active = state;
16855         if (!state) {
16856             this.el.removeClass('active');
16857             
16858         } else  if (!this.el.hasClass('active')) {
16859             this.el.addClass('active');
16860         }
16861         
16862         this.fireEvent('changed', this, state);
16863     }
16864     
16865     
16866 });
16867  
16868
16869  
16870
16871  /*
16872  * - LGPL
16873  *
16874  * DateField
16875  * 
16876  */
16877
16878 /**
16879  * @class Roo.bootstrap.DateField
16880  * @extends Roo.bootstrap.Input
16881  * Bootstrap DateField class
16882  * @cfg {Number} weekStart default 0
16883  * @cfg {String} viewMode default empty, (months|years)
16884  * @cfg {String} minViewMode default empty, (months|years)
16885  * @cfg {Number} startDate default -Infinity
16886  * @cfg {Number} endDate default Infinity
16887  * @cfg {Boolean} todayHighlight default false
16888  * @cfg {Boolean} todayBtn default false
16889  * @cfg {Boolean} calendarWeeks default false
16890  * @cfg {Object} daysOfWeekDisabled default empty
16891  * @cfg {Boolean} singleMode default false (true | false)
16892  * 
16893  * @cfg {Boolean} keyboardNavigation default true
16894  * @cfg {String} language default en
16895  * 
16896  * @constructor
16897  * Create a new DateField
16898  * @param {Object} config The config object
16899  */
16900
16901 Roo.bootstrap.DateField = function(config){
16902     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16903      this.addEvents({
16904             /**
16905              * @event show
16906              * Fires when this field show.
16907              * @param {Roo.bootstrap.DateField} this
16908              * @param {Mixed} date The date value
16909              */
16910             show : true,
16911             /**
16912              * @event show
16913              * Fires when this field hide.
16914              * @param {Roo.bootstrap.DateField} this
16915              * @param {Mixed} date The date value
16916              */
16917             hide : true,
16918             /**
16919              * @event select
16920              * Fires when select a date.
16921              * @param {Roo.bootstrap.DateField} this
16922              * @param {Mixed} date The date value
16923              */
16924             select : true,
16925             /**
16926              * @event beforeselect
16927              * Fires when before select a date.
16928              * @param {Roo.bootstrap.DateField} this
16929              * @param {Mixed} date The date value
16930              */
16931             beforeselect : true
16932         });
16933 };
16934
16935 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16936     
16937     /**
16938      * @cfg {String} format
16939      * The default date format string which can be overriden for localization support.  The format must be
16940      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16941      */
16942     format : "m/d/y",
16943     /**
16944      * @cfg {String} altFormats
16945      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16946      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16947      */
16948     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16949     
16950     weekStart : 0,
16951     
16952     viewMode : '',
16953     
16954     minViewMode : '',
16955     
16956     todayHighlight : false,
16957     
16958     todayBtn: false,
16959     
16960     language: 'en',
16961     
16962     keyboardNavigation: true,
16963     
16964     calendarWeeks: false,
16965     
16966     startDate: -Infinity,
16967     
16968     endDate: Infinity,
16969     
16970     daysOfWeekDisabled: [],
16971     
16972     _events: [],
16973     
16974     singleMode : false,
16975     
16976     UTCDate: function()
16977     {
16978         return new Date(Date.UTC.apply(Date, arguments));
16979     },
16980     
16981     UTCToday: function()
16982     {
16983         var today = new Date();
16984         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16985     },
16986     
16987     getDate: function() {
16988             var d = this.getUTCDate();
16989             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16990     },
16991     
16992     getUTCDate: function() {
16993             return this.date;
16994     },
16995     
16996     setDate: function(d) {
16997             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16998     },
16999     
17000     setUTCDate: function(d) {
17001             this.date = d;
17002             this.setValue(this.formatDate(this.date));
17003     },
17004         
17005     onRender: function(ct, position)
17006     {
17007         
17008         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17009         
17010         this.language = this.language || 'en';
17011         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17012         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17013         
17014         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17015         this.format = this.format || 'm/d/y';
17016         this.isInline = false;
17017         this.isInput = true;
17018         this.component = this.el.select('.add-on', true).first() || false;
17019         this.component = (this.component && this.component.length === 0) ? false : this.component;
17020         this.hasInput = this.component && this.inputEL().length;
17021         
17022         if (typeof(this.minViewMode === 'string')) {
17023             switch (this.minViewMode) {
17024                 case 'months':
17025                     this.minViewMode = 1;
17026                     break;
17027                 case 'years':
17028                     this.minViewMode = 2;
17029                     break;
17030                 default:
17031                     this.minViewMode = 0;
17032                     break;
17033             }
17034         }
17035         
17036         if (typeof(this.viewMode === 'string')) {
17037             switch (this.viewMode) {
17038                 case 'months':
17039                     this.viewMode = 1;
17040                     break;
17041                 case 'years':
17042                     this.viewMode = 2;
17043                     break;
17044                 default:
17045                     this.viewMode = 0;
17046                     break;
17047             }
17048         }
17049                 
17050         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17051         
17052 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17053         
17054         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17055         
17056         this.picker().on('mousedown', this.onMousedown, this);
17057         this.picker().on('click', this.onClick, this);
17058         
17059         this.picker().addClass('datepicker-dropdown');
17060         
17061         this.startViewMode = this.viewMode;
17062         
17063         if(this.singleMode){
17064             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17065                 v.setVisibilityMode(Roo.Element.DISPLAY);
17066                 v.hide();
17067             });
17068             
17069             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17070                 v.setStyle('width', '189px');
17071             });
17072         }
17073         
17074         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17075             if(!this.calendarWeeks){
17076                 v.remove();
17077                 return;
17078             }
17079             
17080             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17081             v.attr('colspan', function(i, val){
17082                 return parseInt(val) + 1;
17083             });
17084         });
17085                         
17086         
17087         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17088         
17089         this.setStartDate(this.startDate);
17090         this.setEndDate(this.endDate);
17091         
17092         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17093         
17094         this.fillDow();
17095         this.fillMonths();
17096         this.update();
17097         this.showMode();
17098         
17099         if(this.isInline) {
17100             this.show();
17101         }
17102     },
17103     
17104     picker : function()
17105     {
17106         return this.pickerEl;
17107 //        return this.el.select('.datepicker', true).first();
17108     },
17109     
17110     fillDow: function()
17111     {
17112         var dowCnt = this.weekStart;
17113         
17114         var dow = {
17115             tag: 'tr',
17116             cn: [
17117                 
17118             ]
17119         };
17120         
17121         if(this.calendarWeeks){
17122             dow.cn.push({
17123                 tag: 'th',
17124                 cls: 'cw',
17125                 html: '&nbsp;'
17126             })
17127         }
17128         
17129         while (dowCnt < this.weekStart + 7) {
17130             dow.cn.push({
17131                 tag: 'th',
17132                 cls: 'dow',
17133                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17134             });
17135         }
17136         
17137         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17138     },
17139     
17140     fillMonths: function()
17141     {    
17142         var i = 0;
17143         var months = this.picker().select('>.datepicker-months td', true).first();
17144         
17145         months.dom.innerHTML = '';
17146         
17147         while (i < 12) {
17148             var month = {
17149                 tag: 'span',
17150                 cls: 'month',
17151                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17152             };
17153             
17154             months.createChild(month);
17155         }
17156         
17157     },
17158     
17159     update: function()
17160     {
17161         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;
17162         
17163         if (this.date < this.startDate) {
17164             this.viewDate = new Date(this.startDate);
17165         } else if (this.date > this.endDate) {
17166             this.viewDate = new Date(this.endDate);
17167         } else {
17168             this.viewDate = new Date(this.date);
17169         }
17170         
17171         this.fill();
17172     },
17173     
17174     fill: function() 
17175     {
17176         var d = new Date(this.viewDate),
17177                 year = d.getUTCFullYear(),
17178                 month = d.getUTCMonth(),
17179                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17180                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17181                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17182                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17183                 currentDate = this.date && this.date.valueOf(),
17184                 today = this.UTCToday();
17185         
17186         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17187         
17188 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17189         
17190 //        this.picker.select('>tfoot th.today').
17191 //                                              .text(dates[this.language].today)
17192 //                                              .toggle(this.todayBtn !== false);
17193     
17194         this.updateNavArrows();
17195         this.fillMonths();
17196                                                 
17197         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17198         
17199         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17200          
17201         prevMonth.setUTCDate(day);
17202         
17203         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17204         
17205         var nextMonth = new Date(prevMonth);
17206         
17207         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17208         
17209         nextMonth = nextMonth.valueOf();
17210         
17211         var fillMonths = false;
17212         
17213         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17214         
17215         while(prevMonth.valueOf() < nextMonth) {
17216             var clsName = '';
17217             
17218             if (prevMonth.getUTCDay() === this.weekStart) {
17219                 if(fillMonths){
17220                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17221                 }
17222                     
17223                 fillMonths = {
17224                     tag: 'tr',
17225                     cn: []
17226                 };
17227                 
17228                 if(this.calendarWeeks){
17229                     // ISO 8601: First week contains first thursday.
17230                     // ISO also states week starts on Monday, but we can be more abstract here.
17231                     var
17232                     // Start of current week: based on weekstart/current date
17233                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17234                     // Thursday of this week
17235                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17236                     // First Thursday of year, year from thursday
17237                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17238                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17239                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17240                     
17241                     fillMonths.cn.push({
17242                         tag: 'td',
17243                         cls: 'cw',
17244                         html: calWeek
17245                     });
17246                 }
17247             }
17248             
17249             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17250                 clsName += ' old';
17251             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17252                 clsName += ' new';
17253             }
17254             if (this.todayHighlight &&
17255                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17256                 prevMonth.getUTCMonth() == today.getMonth() &&
17257                 prevMonth.getUTCDate() == today.getDate()) {
17258                 clsName += ' today';
17259             }
17260             
17261             if (currentDate && prevMonth.valueOf() === currentDate) {
17262                 clsName += ' active';
17263             }
17264             
17265             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17266                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17267                     clsName += ' disabled';
17268             }
17269             
17270             fillMonths.cn.push({
17271                 tag: 'td',
17272                 cls: 'day ' + clsName,
17273                 html: prevMonth.getDate()
17274             });
17275             
17276             prevMonth.setDate(prevMonth.getDate()+1);
17277         }
17278           
17279         var currentYear = this.date && this.date.getUTCFullYear();
17280         var currentMonth = this.date && this.date.getUTCMonth();
17281         
17282         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17283         
17284         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17285             v.removeClass('active');
17286             
17287             if(currentYear === year && k === currentMonth){
17288                 v.addClass('active');
17289             }
17290             
17291             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17292                 v.addClass('disabled');
17293             }
17294             
17295         });
17296         
17297         
17298         year = parseInt(year/10, 10) * 10;
17299         
17300         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17301         
17302         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17303         
17304         year -= 1;
17305         for (var i = -1; i < 11; i++) {
17306             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17307                 tag: 'span',
17308                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17309                 html: year
17310             });
17311             
17312             year += 1;
17313         }
17314     },
17315     
17316     showMode: function(dir) 
17317     {
17318         if (dir) {
17319             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17320         }
17321         
17322         Roo.each(this.picker().select('>div',true).elements, function(v){
17323             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17324             v.hide();
17325         });
17326         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17327     },
17328     
17329     place: function()
17330     {
17331         if(this.isInline) {
17332             return;
17333         }
17334         
17335         this.picker().removeClass(['bottom', 'top']);
17336         
17337         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17338             /*
17339              * place to the top of element!
17340              *
17341              */
17342             
17343             this.picker().addClass('top');
17344             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17345             
17346             return;
17347         }
17348         
17349         this.picker().addClass('bottom');
17350         
17351         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17352     },
17353     
17354     parseDate : function(value)
17355     {
17356         if(!value || value instanceof Date){
17357             return value;
17358         }
17359         var v = Date.parseDate(value, this.format);
17360         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17361             v = Date.parseDate(value, 'Y-m-d');
17362         }
17363         if(!v && this.altFormats){
17364             if(!this.altFormatsArray){
17365                 this.altFormatsArray = this.altFormats.split("|");
17366             }
17367             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17368                 v = Date.parseDate(value, this.altFormatsArray[i]);
17369             }
17370         }
17371         return v;
17372     },
17373     
17374     formatDate : function(date, fmt)
17375     {   
17376         return (!date || !(date instanceof Date)) ?
17377         date : date.dateFormat(fmt || this.format);
17378     },
17379     
17380     onFocus : function()
17381     {
17382         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17383         this.show();
17384     },
17385     
17386     onBlur : function()
17387     {
17388         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17389         
17390         var d = this.inputEl().getValue();
17391         
17392         this.setValue(d);
17393                 
17394         this.hide();
17395     },
17396     
17397     show : function()
17398     {
17399         this.picker().show();
17400         this.update();
17401         this.place();
17402         
17403         this.fireEvent('show', this, this.date);
17404     },
17405     
17406     hide : function()
17407     {
17408         if(this.isInline) {
17409             return;
17410         }
17411         this.picker().hide();
17412         this.viewMode = this.startViewMode;
17413         this.showMode();
17414         
17415         this.fireEvent('hide', this, this.date);
17416         
17417     },
17418     
17419     onMousedown: function(e)
17420     {
17421         e.stopPropagation();
17422         e.preventDefault();
17423     },
17424     
17425     keyup: function(e)
17426     {
17427         Roo.bootstrap.DateField.superclass.keyup.call(this);
17428         this.update();
17429     },
17430
17431     setValue: function(v)
17432     {
17433         if(this.fireEvent('beforeselect', this, v) !== false){
17434             var d = new Date(this.parseDate(v) ).clearTime();
17435         
17436             if(isNaN(d.getTime())){
17437                 this.date = this.viewDate = '';
17438                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17439                 return;
17440             }
17441
17442             v = this.formatDate(d);
17443
17444             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17445
17446             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17447
17448             this.update();
17449
17450             this.fireEvent('select', this, this.date);
17451         }
17452     },
17453     
17454     getValue: function()
17455     {
17456         return this.formatDate(this.date);
17457     },
17458     
17459     fireKey: function(e)
17460     {
17461         if (!this.picker().isVisible()){
17462             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17463                 this.show();
17464             }
17465             return;
17466         }
17467         
17468         var dateChanged = false,
17469         dir, day, month,
17470         newDate, newViewDate;
17471         
17472         switch(e.keyCode){
17473             case 27: // escape
17474                 this.hide();
17475                 e.preventDefault();
17476                 break;
17477             case 37: // left
17478             case 39: // right
17479                 if (!this.keyboardNavigation) {
17480                     break;
17481                 }
17482                 dir = e.keyCode == 37 ? -1 : 1;
17483                 
17484                 if (e.ctrlKey){
17485                     newDate = this.moveYear(this.date, dir);
17486                     newViewDate = this.moveYear(this.viewDate, dir);
17487                 } else if (e.shiftKey){
17488                     newDate = this.moveMonth(this.date, dir);
17489                     newViewDate = this.moveMonth(this.viewDate, dir);
17490                 } else {
17491                     newDate = new Date(this.date);
17492                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17493                     newViewDate = new Date(this.viewDate);
17494                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17495                 }
17496                 if (this.dateWithinRange(newDate)){
17497                     this.date = newDate;
17498                     this.viewDate = newViewDate;
17499                     this.setValue(this.formatDate(this.date));
17500 //                    this.update();
17501                     e.preventDefault();
17502                     dateChanged = true;
17503                 }
17504                 break;
17505             case 38: // up
17506             case 40: // down
17507                 if (!this.keyboardNavigation) {
17508                     break;
17509                 }
17510                 dir = e.keyCode == 38 ? -1 : 1;
17511                 if (e.ctrlKey){
17512                     newDate = this.moveYear(this.date, dir);
17513                     newViewDate = this.moveYear(this.viewDate, dir);
17514                 } else if (e.shiftKey){
17515                     newDate = this.moveMonth(this.date, dir);
17516                     newViewDate = this.moveMonth(this.viewDate, dir);
17517                 } else {
17518                     newDate = new Date(this.date);
17519                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17520                     newViewDate = new Date(this.viewDate);
17521                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17522                 }
17523                 if (this.dateWithinRange(newDate)){
17524                     this.date = newDate;
17525                     this.viewDate = newViewDate;
17526                     this.setValue(this.formatDate(this.date));
17527 //                    this.update();
17528                     e.preventDefault();
17529                     dateChanged = true;
17530                 }
17531                 break;
17532             case 13: // enter
17533                 this.setValue(this.formatDate(this.date));
17534                 this.hide();
17535                 e.preventDefault();
17536                 break;
17537             case 9: // tab
17538                 this.setValue(this.formatDate(this.date));
17539                 this.hide();
17540                 break;
17541             case 16: // shift
17542             case 17: // ctrl
17543             case 18: // alt
17544                 break;
17545             default :
17546                 this.hide();
17547                 
17548         }
17549     },
17550     
17551     
17552     onClick: function(e) 
17553     {
17554         e.stopPropagation();
17555         e.preventDefault();
17556         
17557         var target = e.getTarget();
17558         
17559         if(target.nodeName.toLowerCase() === 'i'){
17560             target = Roo.get(target).dom.parentNode;
17561         }
17562         
17563         var nodeName = target.nodeName;
17564         var className = target.className;
17565         var html = target.innerHTML;
17566         //Roo.log(nodeName);
17567         
17568         switch(nodeName.toLowerCase()) {
17569             case 'th':
17570                 switch(className) {
17571                     case 'switch':
17572                         this.showMode(1);
17573                         break;
17574                     case 'prev':
17575                     case 'next':
17576                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17577                         switch(this.viewMode){
17578                                 case 0:
17579                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17580                                         break;
17581                                 case 1:
17582                                 case 2:
17583                                         this.viewDate = this.moveYear(this.viewDate, dir);
17584                                         break;
17585                         }
17586                         this.fill();
17587                         break;
17588                     case 'today':
17589                         var date = new Date();
17590                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17591 //                        this.fill()
17592                         this.setValue(this.formatDate(this.date));
17593                         
17594                         this.hide();
17595                         break;
17596                 }
17597                 break;
17598             case 'span':
17599                 if (className.indexOf('disabled') < 0) {
17600                     this.viewDate.setUTCDate(1);
17601                     if (className.indexOf('month') > -1) {
17602                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17603                     } else {
17604                         var year = parseInt(html, 10) || 0;
17605                         this.viewDate.setUTCFullYear(year);
17606                         
17607                     }
17608                     
17609                     if(this.singleMode){
17610                         this.setValue(this.formatDate(this.viewDate));
17611                         this.hide();
17612                         return;
17613                     }
17614                     
17615                     this.showMode(-1);
17616                     this.fill();
17617                 }
17618                 break;
17619                 
17620             case 'td':
17621                 //Roo.log(className);
17622                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17623                     var day = parseInt(html, 10) || 1;
17624                     var year = this.viewDate.getUTCFullYear(),
17625                         month = this.viewDate.getUTCMonth();
17626
17627                     if (className.indexOf('old') > -1) {
17628                         if(month === 0 ){
17629                             month = 11;
17630                             year -= 1;
17631                         }else{
17632                             month -= 1;
17633                         }
17634                     } else if (className.indexOf('new') > -1) {
17635                         if (month == 11) {
17636                             month = 0;
17637                             year += 1;
17638                         } else {
17639                             month += 1;
17640                         }
17641                     }
17642                     //Roo.log([year,month,day]);
17643                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17644                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17645 //                    this.fill();
17646                     //Roo.log(this.formatDate(this.date));
17647                     this.setValue(this.formatDate(this.date));
17648                     this.hide();
17649                 }
17650                 break;
17651         }
17652     },
17653     
17654     setStartDate: function(startDate)
17655     {
17656         this.startDate = startDate || -Infinity;
17657         if (this.startDate !== -Infinity) {
17658             this.startDate = this.parseDate(this.startDate);
17659         }
17660         this.update();
17661         this.updateNavArrows();
17662     },
17663
17664     setEndDate: function(endDate)
17665     {
17666         this.endDate = endDate || Infinity;
17667         if (this.endDate !== Infinity) {
17668             this.endDate = this.parseDate(this.endDate);
17669         }
17670         this.update();
17671         this.updateNavArrows();
17672     },
17673     
17674     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17675     {
17676         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17677         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17678             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17679         }
17680         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17681             return parseInt(d, 10);
17682         });
17683         this.update();
17684         this.updateNavArrows();
17685     },
17686     
17687     updateNavArrows: function() 
17688     {
17689         if(this.singleMode){
17690             return;
17691         }
17692         
17693         var d = new Date(this.viewDate),
17694         year = d.getUTCFullYear(),
17695         month = d.getUTCMonth();
17696         
17697         Roo.each(this.picker().select('.prev', true).elements, function(v){
17698             v.show();
17699             switch (this.viewMode) {
17700                 case 0:
17701
17702                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17703                         v.hide();
17704                     }
17705                     break;
17706                 case 1:
17707                 case 2:
17708                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17709                         v.hide();
17710                     }
17711                     break;
17712             }
17713         });
17714         
17715         Roo.each(this.picker().select('.next', true).elements, function(v){
17716             v.show();
17717             switch (this.viewMode) {
17718                 case 0:
17719
17720                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17721                         v.hide();
17722                     }
17723                     break;
17724                 case 1:
17725                 case 2:
17726                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17727                         v.hide();
17728                     }
17729                     break;
17730             }
17731         })
17732     },
17733     
17734     moveMonth: function(date, dir)
17735     {
17736         if (!dir) {
17737             return date;
17738         }
17739         var new_date = new Date(date.valueOf()),
17740         day = new_date.getUTCDate(),
17741         month = new_date.getUTCMonth(),
17742         mag = Math.abs(dir),
17743         new_month, test;
17744         dir = dir > 0 ? 1 : -1;
17745         if (mag == 1){
17746             test = dir == -1
17747             // If going back one month, make sure month is not current month
17748             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17749             ? function(){
17750                 return new_date.getUTCMonth() == month;
17751             }
17752             // If going forward one month, make sure month is as expected
17753             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17754             : function(){
17755                 return new_date.getUTCMonth() != new_month;
17756             };
17757             new_month = month + dir;
17758             new_date.setUTCMonth(new_month);
17759             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17760             if (new_month < 0 || new_month > 11) {
17761                 new_month = (new_month + 12) % 12;
17762             }
17763         } else {
17764             // For magnitudes >1, move one month at a time...
17765             for (var i=0; i<mag; i++) {
17766                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17767                 new_date = this.moveMonth(new_date, dir);
17768             }
17769             // ...then reset the day, keeping it in the new month
17770             new_month = new_date.getUTCMonth();
17771             new_date.setUTCDate(day);
17772             test = function(){
17773                 return new_month != new_date.getUTCMonth();
17774             };
17775         }
17776         // Common date-resetting loop -- if date is beyond end of month, make it
17777         // end of month
17778         while (test()){
17779             new_date.setUTCDate(--day);
17780             new_date.setUTCMonth(new_month);
17781         }
17782         return new_date;
17783     },
17784
17785     moveYear: function(date, dir)
17786     {
17787         return this.moveMonth(date, dir*12);
17788     },
17789
17790     dateWithinRange: function(date)
17791     {
17792         return date >= this.startDate && date <= this.endDate;
17793     },
17794
17795     
17796     remove: function() 
17797     {
17798         this.picker().remove();
17799     }
17800    
17801 });
17802
17803 Roo.apply(Roo.bootstrap.DateField,  {
17804     
17805     head : {
17806         tag: 'thead',
17807         cn: [
17808         {
17809             tag: 'tr',
17810             cn: [
17811             {
17812                 tag: 'th',
17813                 cls: 'prev',
17814                 html: '<i class="fa fa-arrow-left"/>'
17815             },
17816             {
17817                 tag: 'th',
17818                 cls: 'switch',
17819                 colspan: '5'
17820             },
17821             {
17822                 tag: 'th',
17823                 cls: 'next',
17824                 html: '<i class="fa fa-arrow-right"/>'
17825             }
17826
17827             ]
17828         }
17829         ]
17830     },
17831     
17832     content : {
17833         tag: 'tbody',
17834         cn: [
17835         {
17836             tag: 'tr',
17837             cn: [
17838             {
17839                 tag: 'td',
17840                 colspan: '7'
17841             }
17842             ]
17843         }
17844         ]
17845     },
17846     
17847     footer : {
17848         tag: 'tfoot',
17849         cn: [
17850         {
17851             tag: 'tr',
17852             cn: [
17853             {
17854                 tag: 'th',
17855                 colspan: '7',
17856                 cls: 'today'
17857             }
17858                     
17859             ]
17860         }
17861         ]
17862     },
17863     
17864     dates:{
17865         en: {
17866             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17867             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17868             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17869             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17870             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17871             today: "Today"
17872         }
17873     },
17874     
17875     modes: [
17876     {
17877         clsName: 'days',
17878         navFnc: 'Month',
17879         navStep: 1
17880     },
17881     {
17882         clsName: 'months',
17883         navFnc: 'FullYear',
17884         navStep: 1
17885     },
17886     {
17887         clsName: 'years',
17888         navFnc: 'FullYear',
17889         navStep: 10
17890     }]
17891 });
17892
17893 Roo.apply(Roo.bootstrap.DateField,  {
17894   
17895     template : {
17896         tag: 'div',
17897         cls: 'datepicker dropdown-menu roo-dynamic',
17898         cn: [
17899         {
17900             tag: 'div',
17901             cls: 'datepicker-days',
17902             cn: [
17903             {
17904                 tag: 'table',
17905                 cls: 'table-condensed',
17906                 cn:[
17907                 Roo.bootstrap.DateField.head,
17908                 {
17909                     tag: 'tbody'
17910                 },
17911                 Roo.bootstrap.DateField.footer
17912                 ]
17913             }
17914             ]
17915         },
17916         {
17917             tag: 'div',
17918             cls: 'datepicker-months',
17919             cn: [
17920             {
17921                 tag: 'table',
17922                 cls: 'table-condensed',
17923                 cn:[
17924                 Roo.bootstrap.DateField.head,
17925                 Roo.bootstrap.DateField.content,
17926                 Roo.bootstrap.DateField.footer
17927                 ]
17928             }
17929             ]
17930         },
17931         {
17932             tag: 'div',
17933             cls: 'datepicker-years',
17934             cn: [
17935             {
17936                 tag: 'table',
17937                 cls: 'table-condensed',
17938                 cn:[
17939                 Roo.bootstrap.DateField.head,
17940                 Roo.bootstrap.DateField.content,
17941                 Roo.bootstrap.DateField.footer
17942                 ]
17943             }
17944             ]
17945         }
17946         ]
17947     }
17948 });
17949
17950  
17951
17952  /*
17953  * - LGPL
17954  *
17955  * TimeField
17956  * 
17957  */
17958
17959 /**
17960  * @class Roo.bootstrap.TimeField
17961  * @extends Roo.bootstrap.Input
17962  * Bootstrap DateField class
17963  * 
17964  * 
17965  * @constructor
17966  * Create a new TimeField
17967  * @param {Object} config The config object
17968  */
17969
17970 Roo.bootstrap.TimeField = function(config){
17971     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17972     this.addEvents({
17973             /**
17974              * @event show
17975              * Fires when this field show.
17976              * @param {Roo.bootstrap.DateField} thisthis
17977              * @param {Mixed} date The date value
17978              */
17979             show : true,
17980             /**
17981              * @event show
17982              * Fires when this field hide.
17983              * @param {Roo.bootstrap.DateField} this
17984              * @param {Mixed} date The date value
17985              */
17986             hide : true,
17987             /**
17988              * @event select
17989              * Fires when select a date.
17990              * @param {Roo.bootstrap.DateField} this
17991              * @param {Mixed} date The date value
17992              */
17993             select : true
17994         });
17995 };
17996
17997 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17998     
17999     /**
18000      * @cfg {String} format
18001      * The default time format string which can be overriden for localization support.  The format must be
18002      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18003      */
18004     format : "H:i",
18005        
18006     onRender: function(ct, position)
18007     {
18008         
18009         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18010                 
18011         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18012         
18013         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18014         
18015         this.pop = this.picker().select('>.datepicker-time',true).first();
18016         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18017         
18018         this.picker().on('mousedown', this.onMousedown, this);
18019         this.picker().on('click', this.onClick, this);
18020         
18021         this.picker().addClass('datepicker-dropdown');
18022     
18023         this.fillTime();
18024         this.update();
18025             
18026         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18027         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18028         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18029         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18030         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18031         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18032
18033     },
18034     
18035     fireKey: function(e){
18036         if (!this.picker().isVisible()){
18037             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18038                 this.show();
18039             }
18040             return;
18041         }
18042
18043         e.preventDefault();
18044         
18045         switch(e.keyCode){
18046             case 27: // escape
18047                 this.hide();
18048                 break;
18049             case 37: // left
18050             case 39: // right
18051                 this.onTogglePeriod();
18052                 break;
18053             case 38: // up
18054                 this.onIncrementMinutes();
18055                 break;
18056             case 40: // down
18057                 this.onDecrementMinutes();
18058                 break;
18059             case 13: // enter
18060             case 9: // tab
18061                 this.setTime();
18062                 break;
18063         }
18064     },
18065     
18066     onClick: function(e) {
18067         e.stopPropagation();
18068         e.preventDefault();
18069     },
18070     
18071     picker : function()
18072     {
18073         return this.el.select('.datepicker', true).first();
18074     },
18075     
18076     fillTime: function()
18077     {    
18078         var time = this.pop.select('tbody', true).first();
18079         
18080         time.dom.innerHTML = '';
18081         
18082         time.createChild({
18083             tag: 'tr',
18084             cn: [
18085                 {
18086                     tag: 'td',
18087                     cn: [
18088                         {
18089                             tag: 'a',
18090                             href: '#',
18091                             cls: 'btn',
18092                             cn: [
18093                                 {
18094                                     tag: 'span',
18095                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18096                                 }
18097                             ]
18098                         } 
18099                     ]
18100                 },
18101                 {
18102                     tag: 'td',
18103                     cls: 'separator'
18104                 },
18105                 {
18106                     tag: 'td',
18107                     cn: [
18108                         {
18109                             tag: 'a',
18110                             href: '#',
18111                             cls: 'btn',
18112                             cn: [
18113                                 {
18114                                     tag: 'span',
18115                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18116                                 }
18117                             ]
18118                         }
18119                     ]
18120                 },
18121                 {
18122                     tag: 'td',
18123                     cls: 'separator'
18124                 }
18125             ]
18126         });
18127         
18128         time.createChild({
18129             tag: 'tr',
18130             cn: [
18131                 {
18132                     tag: 'td',
18133                     cn: [
18134                         {
18135                             tag: 'span',
18136                             cls: 'timepicker-hour',
18137                             html: '00'
18138                         }  
18139                     ]
18140                 },
18141                 {
18142                     tag: 'td',
18143                     cls: 'separator',
18144                     html: ':'
18145                 },
18146                 {
18147                     tag: 'td',
18148                     cn: [
18149                         {
18150                             tag: 'span',
18151                             cls: 'timepicker-minute',
18152                             html: '00'
18153                         }  
18154                     ]
18155                 },
18156                 {
18157                     tag: 'td',
18158                     cls: 'separator'
18159                 },
18160                 {
18161                     tag: 'td',
18162                     cn: [
18163                         {
18164                             tag: 'button',
18165                             type: 'button',
18166                             cls: 'btn btn-primary period',
18167                             html: 'AM'
18168                             
18169                         }
18170                     ]
18171                 }
18172             ]
18173         });
18174         
18175         time.createChild({
18176             tag: 'tr',
18177             cn: [
18178                 {
18179                     tag: 'td',
18180                     cn: [
18181                         {
18182                             tag: 'a',
18183                             href: '#',
18184                             cls: 'btn',
18185                             cn: [
18186                                 {
18187                                     tag: 'span',
18188                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18189                                 }
18190                             ]
18191                         }
18192                     ]
18193                 },
18194                 {
18195                     tag: 'td',
18196                     cls: 'separator'
18197                 },
18198                 {
18199                     tag: 'td',
18200                     cn: [
18201                         {
18202                             tag: 'a',
18203                             href: '#',
18204                             cls: 'btn',
18205                             cn: [
18206                                 {
18207                                     tag: 'span',
18208                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18209                                 }
18210                             ]
18211                         }
18212                     ]
18213                 },
18214                 {
18215                     tag: 'td',
18216                     cls: 'separator'
18217                 }
18218             ]
18219         });
18220         
18221     },
18222     
18223     update: function()
18224     {
18225         
18226         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18227         
18228         this.fill();
18229     },
18230     
18231     fill: function() 
18232     {
18233         var hours = this.time.getHours();
18234         var minutes = this.time.getMinutes();
18235         var period = 'AM';
18236         
18237         if(hours > 11){
18238             period = 'PM';
18239         }
18240         
18241         if(hours == 0){
18242             hours = 12;
18243         }
18244         
18245         
18246         if(hours > 12){
18247             hours = hours - 12;
18248         }
18249         
18250         if(hours < 10){
18251             hours = '0' + hours;
18252         }
18253         
18254         if(minutes < 10){
18255             minutes = '0' + minutes;
18256         }
18257         
18258         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18259         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18260         this.pop.select('button', true).first().dom.innerHTML = period;
18261         
18262     },
18263     
18264     place: function()
18265     {   
18266         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18267         
18268         var cls = ['bottom'];
18269         
18270         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18271             cls.pop();
18272             cls.push('top');
18273         }
18274         
18275         cls.push('right');
18276         
18277         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18278             cls.pop();
18279             cls.push('left');
18280         }
18281         
18282         this.picker().addClass(cls.join('-'));
18283         
18284         var _this = this;
18285         
18286         Roo.each(cls, function(c){
18287             if(c == 'bottom'){
18288                 _this.picker().setTop(_this.inputEl().getHeight());
18289                 return;
18290             }
18291             if(c == 'top'){
18292                 _this.picker().setTop(0 - _this.picker().getHeight());
18293                 return;
18294             }
18295             
18296             if(c == 'left'){
18297                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18298                 return;
18299             }
18300             if(c == 'right'){
18301                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18302                 return;
18303             }
18304         });
18305         
18306     },
18307   
18308     onFocus : function()
18309     {
18310         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18311         this.show();
18312     },
18313     
18314     onBlur : function()
18315     {
18316         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18317         this.hide();
18318     },
18319     
18320     show : function()
18321     {
18322         this.picker().show();
18323         this.pop.show();
18324         this.update();
18325         this.place();
18326         
18327         this.fireEvent('show', this, this.date);
18328     },
18329     
18330     hide : function()
18331     {
18332         this.picker().hide();
18333         this.pop.hide();
18334         
18335         this.fireEvent('hide', this, this.date);
18336     },
18337     
18338     setTime : function()
18339     {
18340         this.hide();
18341         this.setValue(this.time.format(this.format));
18342         
18343         this.fireEvent('select', this, this.date);
18344         
18345         
18346     },
18347     
18348     onMousedown: function(e){
18349         e.stopPropagation();
18350         e.preventDefault();
18351     },
18352     
18353     onIncrementHours: function()
18354     {
18355         Roo.log('onIncrementHours');
18356         this.time = this.time.add(Date.HOUR, 1);
18357         this.update();
18358         
18359     },
18360     
18361     onDecrementHours: function()
18362     {
18363         Roo.log('onDecrementHours');
18364         this.time = this.time.add(Date.HOUR, -1);
18365         this.update();
18366     },
18367     
18368     onIncrementMinutes: function()
18369     {
18370         Roo.log('onIncrementMinutes');
18371         this.time = this.time.add(Date.MINUTE, 1);
18372         this.update();
18373     },
18374     
18375     onDecrementMinutes: function()
18376     {
18377         Roo.log('onDecrementMinutes');
18378         this.time = this.time.add(Date.MINUTE, -1);
18379         this.update();
18380     },
18381     
18382     onTogglePeriod: function()
18383     {
18384         Roo.log('onTogglePeriod');
18385         this.time = this.time.add(Date.HOUR, 12);
18386         this.update();
18387     }
18388     
18389    
18390 });
18391
18392 Roo.apply(Roo.bootstrap.TimeField,  {
18393     
18394     content : {
18395         tag: 'tbody',
18396         cn: [
18397             {
18398                 tag: 'tr',
18399                 cn: [
18400                 {
18401                     tag: 'td',
18402                     colspan: '7'
18403                 }
18404                 ]
18405             }
18406         ]
18407     },
18408     
18409     footer : {
18410         tag: 'tfoot',
18411         cn: [
18412             {
18413                 tag: 'tr',
18414                 cn: [
18415                 {
18416                     tag: 'th',
18417                     colspan: '7',
18418                     cls: '',
18419                     cn: [
18420                         {
18421                             tag: 'button',
18422                             cls: 'btn btn-info ok',
18423                             html: 'OK'
18424                         }
18425                     ]
18426                 }
18427
18428                 ]
18429             }
18430         ]
18431     }
18432 });
18433
18434 Roo.apply(Roo.bootstrap.TimeField,  {
18435   
18436     template : {
18437         tag: 'div',
18438         cls: 'datepicker dropdown-menu',
18439         cn: [
18440             {
18441                 tag: 'div',
18442                 cls: 'datepicker-time',
18443                 cn: [
18444                 {
18445                     tag: 'table',
18446                     cls: 'table-condensed',
18447                     cn:[
18448                     Roo.bootstrap.TimeField.content,
18449                     Roo.bootstrap.TimeField.footer
18450                     ]
18451                 }
18452                 ]
18453             }
18454         ]
18455     }
18456 });
18457
18458  
18459
18460  /*
18461  * - LGPL
18462  *
18463  * MonthField
18464  * 
18465  */
18466
18467 /**
18468  * @class Roo.bootstrap.MonthField
18469  * @extends Roo.bootstrap.Input
18470  * Bootstrap MonthField class
18471  * 
18472  * @cfg {String} language default en
18473  * 
18474  * @constructor
18475  * Create a new MonthField
18476  * @param {Object} config The config object
18477  */
18478
18479 Roo.bootstrap.MonthField = function(config){
18480     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18481     
18482     this.addEvents({
18483         /**
18484          * @event show
18485          * Fires when this field show.
18486          * @param {Roo.bootstrap.MonthField} this
18487          * @param {Mixed} date The date value
18488          */
18489         show : true,
18490         /**
18491          * @event show
18492          * Fires when this field hide.
18493          * @param {Roo.bootstrap.MonthField} this
18494          * @param {Mixed} date The date value
18495          */
18496         hide : true,
18497         /**
18498          * @event select
18499          * Fires when select a date.
18500          * @param {Roo.bootstrap.MonthField} this
18501          * @param {String} oldvalue The old value
18502          * @param {String} newvalue The new value
18503          */
18504         select : true
18505     });
18506 };
18507
18508 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18509     
18510     onRender: function(ct, position)
18511     {
18512         
18513         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18514         
18515         this.language = this.language || 'en';
18516         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18517         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18518         
18519         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18520         this.isInline = false;
18521         this.isInput = true;
18522         this.component = this.el.select('.add-on', true).first() || false;
18523         this.component = (this.component && this.component.length === 0) ? false : this.component;
18524         this.hasInput = this.component && this.inputEL().length;
18525         
18526         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18527         
18528         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18529         
18530         this.picker().on('mousedown', this.onMousedown, this);
18531         this.picker().on('click', this.onClick, this);
18532         
18533         this.picker().addClass('datepicker-dropdown');
18534         
18535         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18536             v.setStyle('width', '189px');
18537         });
18538         
18539         this.fillMonths();
18540         
18541         this.update();
18542         
18543         if(this.isInline) {
18544             this.show();
18545         }
18546         
18547     },
18548     
18549     setValue: function(v, suppressEvent)
18550     {   
18551         var o = this.getValue();
18552         
18553         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18554         
18555         this.update();
18556
18557         if(suppressEvent !== true){
18558             this.fireEvent('select', this, o, v);
18559         }
18560         
18561     },
18562     
18563     getValue: function()
18564     {
18565         return this.value;
18566     },
18567     
18568     onClick: function(e) 
18569     {
18570         e.stopPropagation();
18571         e.preventDefault();
18572         
18573         var target = e.getTarget();
18574         
18575         if(target.nodeName.toLowerCase() === 'i'){
18576             target = Roo.get(target).dom.parentNode;
18577         }
18578         
18579         var nodeName = target.nodeName;
18580         var className = target.className;
18581         var html = target.innerHTML;
18582         
18583         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18584             return;
18585         }
18586         
18587         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18588         
18589         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18590         
18591         this.hide();
18592                         
18593     },
18594     
18595     picker : function()
18596     {
18597         return this.pickerEl;
18598     },
18599     
18600     fillMonths: function()
18601     {    
18602         var i = 0;
18603         var months = this.picker().select('>.datepicker-months td', true).first();
18604         
18605         months.dom.innerHTML = '';
18606         
18607         while (i < 12) {
18608             var month = {
18609                 tag: 'span',
18610                 cls: 'month',
18611                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18612             };
18613             
18614             months.createChild(month);
18615         }
18616         
18617     },
18618     
18619     update: function()
18620     {
18621         var _this = this;
18622         
18623         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18624             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18625         }
18626         
18627         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18628             e.removeClass('active');
18629             
18630             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18631                 e.addClass('active');
18632             }
18633         })
18634     },
18635     
18636     place: function()
18637     {
18638         if(this.isInline) {
18639             return;
18640         }
18641         
18642         this.picker().removeClass(['bottom', 'top']);
18643         
18644         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18645             /*
18646              * place to the top of element!
18647              *
18648              */
18649             
18650             this.picker().addClass('top');
18651             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18652             
18653             return;
18654         }
18655         
18656         this.picker().addClass('bottom');
18657         
18658         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18659     },
18660     
18661     onFocus : function()
18662     {
18663         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18664         this.show();
18665     },
18666     
18667     onBlur : function()
18668     {
18669         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18670         
18671         var d = this.inputEl().getValue();
18672         
18673         this.setValue(d);
18674                 
18675         this.hide();
18676     },
18677     
18678     show : function()
18679     {
18680         this.picker().show();
18681         this.picker().select('>.datepicker-months', true).first().show();
18682         this.update();
18683         this.place();
18684         
18685         this.fireEvent('show', this, this.date);
18686     },
18687     
18688     hide : function()
18689     {
18690         if(this.isInline) {
18691             return;
18692         }
18693         this.picker().hide();
18694         this.fireEvent('hide', this, this.date);
18695         
18696     },
18697     
18698     onMousedown: function(e)
18699     {
18700         e.stopPropagation();
18701         e.preventDefault();
18702     },
18703     
18704     keyup: function(e)
18705     {
18706         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18707         this.update();
18708     },
18709
18710     fireKey: function(e)
18711     {
18712         if (!this.picker().isVisible()){
18713             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18714                 this.show();
18715             }
18716             return;
18717         }
18718         
18719         var dir;
18720         
18721         switch(e.keyCode){
18722             case 27: // escape
18723                 this.hide();
18724                 e.preventDefault();
18725                 break;
18726             case 37: // left
18727             case 39: // right
18728                 dir = e.keyCode == 37 ? -1 : 1;
18729                 
18730                 this.vIndex = this.vIndex + dir;
18731                 
18732                 if(this.vIndex < 0){
18733                     this.vIndex = 0;
18734                 }
18735                 
18736                 if(this.vIndex > 11){
18737                     this.vIndex = 11;
18738                 }
18739                 
18740                 if(isNaN(this.vIndex)){
18741                     this.vIndex = 0;
18742                 }
18743                 
18744                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18745                 
18746                 break;
18747             case 38: // up
18748             case 40: // down
18749                 
18750                 dir = e.keyCode == 38 ? -1 : 1;
18751                 
18752                 this.vIndex = this.vIndex + dir * 4;
18753                 
18754                 if(this.vIndex < 0){
18755                     this.vIndex = 0;
18756                 }
18757                 
18758                 if(this.vIndex > 11){
18759                     this.vIndex = 11;
18760                 }
18761                 
18762                 if(isNaN(this.vIndex)){
18763                     this.vIndex = 0;
18764                 }
18765                 
18766                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18767                 break;
18768                 
18769             case 13: // enter
18770                 
18771                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18772                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18773                 }
18774                 
18775                 this.hide();
18776                 e.preventDefault();
18777                 break;
18778             case 9: // tab
18779                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18780                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18781                 }
18782                 this.hide();
18783                 break;
18784             case 16: // shift
18785             case 17: // ctrl
18786             case 18: // alt
18787                 break;
18788             default :
18789                 this.hide();
18790                 
18791         }
18792     },
18793     
18794     remove: function() 
18795     {
18796         this.picker().remove();
18797     }
18798    
18799 });
18800
18801 Roo.apply(Roo.bootstrap.MonthField,  {
18802     
18803     content : {
18804         tag: 'tbody',
18805         cn: [
18806         {
18807             tag: 'tr',
18808             cn: [
18809             {
18810                 tag: 'td',
18811                 colspan: '7'
18812             }
18813             ]
18814         }
18815         ]
18816     },
18817     
18818     dates:{
18819         en: {
18820             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18821             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18822         }
18823     }
18824 });
18825
18826 Roo.apply(Roo.bootstrap.MonthField,  {
18827   
18828     template : {
18829         tag: 'div',
18830         cls: 'datepicker dropdown-menu roo-dynamic',
18831         cn: [
18832             {
18833                 tag: 'div',
18834                 cls: 'datepicker-months',
18835                 cn: [
18836                 {
18837                     tag: 'table',
18838                     cls: 'table-condensed',
18839                     cn:[
18840                         Roo.bootstrap.DateField.content
18841                     ]
18842                 }
18843                 ]
18844             }
18845         ]
18846     }
18847 });
18848
18849  
18850
18851  
18852  /*
18853  * - LGPL
18854  *
18855  * CheckBox
18856  * 
18857  */
18858
18859 /**
18860  * @class Roo.bootstrap.CheckBox
18861  * @extends Roo.bootstrap.Input
18862  * Bootstrap CheckBox class
18863  * 
18864  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18865  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18866  * @cfg {String} boxLabel The text that appears beside the checkbox
18867  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18868  * @cfg {Boolean} checked initnal the element
18869  * @cfg {Boolean} inline inline the element (default false)
18870  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18871  * 
18872  * @constructor
18873  * Create a new CheckBox
18874  * @param {Object} config The config object
18875  */
18876
18877 Roo.bootstrap.CheckBox = function(config){
18878     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18879    
18880     this.addEvents({
18881         /**
18882         * @event check
18883         * Fires when the element is checked or unchecked.
18884         * @param {Roo.bootstrap.CheckBox} this This input
18885         * @param {Boolean} checked The new checked value
18886         */
18887        check : true
18888     });
18889     
18890 };
18891
18892 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18893   
18894     inputType: 'checkbox',
18895     inputValue: 1,
18896     valueOff: 0,
18897     boxLabel: false,
18898     checked: false,
18899     weight : false,
18900     inline: false,
18901     
18902     getAutoCreate : function()
18903     {
18904         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18905         
18906         var id = Roo.id();
18907         
18908         var cfg = {};
18909         
18910         cfg.cls = 'form-group ' + this.inputType; //input-group
18911         
18912         if(this.inline){
18913             cfg.cls += ' ' + this.inputType + '-inline';
18914         }
18915         
18916         var input =  {
18917             tag: 'input',
18918             id : id,
18919             type : this.inputType,
18920             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18921             cls : 'roo-' + this.inputType, //'form-box',
18922             placeholder : this.placeholder || ''
18923             
18924         };
18925         
18926         if (this.weight) { // Validity check?
18927             cfg.cls += " " + this.inputType + "-" + this.weight;
18928         }
18929         
18930         if (this.disabled) {
18931             input.disabled=true;
18932         }
18933         
18934         if(this.checked){
18935             input.checked = this.checked;
18936         }
18937         
18938         if (this.name) {
18939             input.name = this.name;
18940         }
18941         
18942         if (this.size) {
18943             input.cls += ' input-' + this.size;
18944         }
18945         
18946         var settings=this;
18947         
18948         ['xs','sm','md','lg'].map(function(size){
18949             if (settings[size]) {
18950                 cfg.cls += ' col-' + size + '-' + settings[size];
18951             }
18952         });
18953         
18954         var inputblock = input;
18955          
18956         if (this.before || this.after) {
18957             
18958             inputblock = {
18959                 cls : 'input-group',
18960                 cn :  [] 
18961             };
18962             
18963             if (this.before) {
18964                 inputblock.cn.push({
18965                     tag :'span',
18966                     cls : 'input-group-addon',
18967                     html : this.before
18968                 });
18969             }
18970             
18971             inputblock.cn.push(input);
18972             
18973             if (this.after) {
18974                 inputblock.cn.push({
18975                     tag :'span',
18976                     cls : 'input-group-addon',
18977                     html : this.after
18978                 });
18979             }
18980             
18981         }
18982         
18983         if (align ==='left' && this.fieldLabel.length) {
18984 //                Roo.log("left and has label");
18985                 cfg.cn = [
18986                     
18987                     {
18988                         tag: 'label',
18989                         'for' :  id,
18990                         cls : 'control-label col-md-' + this.labelWidth,
18991                         html : this.fieldLabel
18992                         
18993                     },
18994                     {
18995                         cls : "col-md-" + (12 - this.labelWidth), 
18996                         cn: [
18997                             inputblock
18998                         ]
18999                     }
19000                     
19001                 ];
19002         } else if ( this.fieldLabel.length) {
19003 //                Roo.log(" label");
19004                 cfg.cn = [
19005                    
19006                     {
19007                         tag: this.boxLabel ? 'span' : 'label',
19008                         'for': id,
19009                         cls: 'control-label box-input-label',
19010                         //cls : 'input-group-addon',
19011                         html : this.fieldLabel
19012                         
19013                     },
19014                     
19015                     inputblock
19016                     
19017                 ];
19018
19019         } else {
19020             
19021 //                Roo.log(" no label && no align");
19022                 cfg.cn = [  inputblock ] ;
19023                 
19024                 
19025         }
19026         
19027         if(this.boxLabel){
19028              var boxLabelCfg = {
19029                 tag: 'label',
19030                 //'for': id, // box label is handled by onclick - so no for...
19031                 cls: 'box-label',
19032                 html: this.boxLabel
19033             };
19034             
19035             if(this.tooltip){
19036                 boxLabelCfg.tooltip = this.tooltip;
19037             }
19038              
19039             cfg.cn.push(boxLabelCfg);
19040         }
19041         
19042         
19043        
19044         return cfg;
19045         
19046     },
19047     
19048     /**
19049      * return the real input element.
19050      */
19051     inputEl: function ()
19052     {
19053         return this.el.select('input.roo-' + this.inputType,true).first();
19054     },
19055     
19056     labelEl: function()
19057     {
19058         return this.el.select('label.control-label',true).first();
19059     },
19060     /* depricated... */
19061     
19062     label: function()
19063     {
19064         return this.labelEl();
19065     },
19066     
19067     boxLabelEl: function()
19068     {
19069         return this.el.select('label.box-label',true).first();
19070     },
19071     
19072     initEvents : function()
19073     {
19074 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19075         
19076         this.inputEl().on('click', this.onClick,  this);
19077         
19078         if (this.boxLabel) { 
19079             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19080         }
19081         
19082         this.startValue = this.getValue();
19083         
19084         if(this.groupId){
19085             Roo.bootstrap.CheckBox.register(this);
19086         }
19087     },
19088     
19089     onClick : function()
19090     {   
19091         this.setChecked(!this.checked);
19092     },
19093     
19094     setChecked : function(state,suppressEvent)
19095     {
19096         this.startValue = this.getValue();
19097         
19098         if(this.inputType == 'radio'){
19099             
19100             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19101                 e.dom.checked = false;
19102             });
19103             
19104             this.inputEl().dom.checked = true;
19105             
19106             this.inputEl().dom.value = this.inputValue;
19107             
19108             if(suppressEvent !== true){
19109                 this.fireEvent('check', this, true);
19110             }
19111             
19112             this.validate();
19113             
19114             return;
19115         }
19116         
19117         this.checked = state;
19118         
19119         this.inputEl().dom.checked = state;
19120         
19121         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19122         
19123         if(suppressEvent !== true){
19124             this.fireEvent('check', this, state);
19125         }
19126         
19127         this.validate();
19128     },
19129     
19130     getValue : function()
19131     {
19132         if(this.inputType == 'radio'){
19133             return this.getGroupValue();
19134         }
19135         
19136         return this.inputEl().getValue();
19137         
19138     },
19139     
19140     getGroupValue : function()
19141     {
19142         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19143             return '';
19144         }
19145         
19146         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19147     },
19148     
19149     setValue : function(v,suppressEvent)
19150     {
19151         if(this.inputType == 'radio'){
19152             this.setGroupValue(v, suppressEvent);
19153             return;
19154         }
19155         
19156         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19157         
19158         this.validate();
19159     },
19160     
19161     setGroupValue : function(v, suppressEvent)
19162     {
19163         this.startValue = this.getValue();
19164         
19165         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19166             e.dom.checked = false;
19167             
19168             if(e.dom.value == v){
19169                 e.dom.checked = true;
19170             }
19171         });
19172         
19173         if(suppressEvent !== true){
19174             this.fireEvent('check', this, true);
19175         }
19176
19177         this.validate();
19178         
19179         return;
19180     },
19181     
19182     validate : function()
19183     {
19184         if(
19185                 this.disabled || 
19186                 (this.inputType == 'radio' && this.validateRadio()) ||
19187                 (this.inputType == 'checkbox' && this.validateCheckbox())
19188         ){
19189             this.markValid();
19190             return true;
19191         }
19192         
19193         this.markInvalid();
19194         return false;
19195     },
19196     
19197     validateRadio : function()
19198     {
19199         var valid = false;
19200         
19201         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19202             if(!e.dom.checked){
19203                 return;
19204             }
19205             
19206             valid = true;
19207             
19208             return false;
19209         });
19210         
19211         return valid;
19212     },
19213     
19214     validateCheckbox : function()
19215     {
19216         if(!this.groupId){
19217             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19218         }
19219         
19220         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19221         
19222         if(!group){
19223             return false;
19224         }
19225         
19226         var r = false;
19227         
19228         for(var i in group){
19229             if(r){
19230                 break;
19231             }
19232             
19233             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19234         }
19235         
19236         return r;
19237     },
19238     
19239     /**
19240      * Mark this field as valid
19241      */
19242     markValid : function()
19243     {
19244         if(this.allowBlank){
19245             return;
19246         }
19247         
19248         var _this = this;
19249         
19250         this.fireEvent('valid', this);
19251         
19252         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19253         
19254         if(this.groupId){
19255             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19256         }
19257         
19258         if(label){
19259             label.markValid();
19260         }
19261         
19262         if(this.inputType == 'radio'){
19263             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19264                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19265                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19266             });
19267             
19268             return;
19269         }
19270         
19271         if(!this.groupId){
19272             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19273             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19274             return;
19275         }
19276         
19277         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19278             
19279         if(!group){
19280             return;
19281         }
19282         
19283         for(var i in group){
19284             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19285             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19286         }
19287     },
19288     
19289      /**
19290      * Mark this field as invalid
19291      * @param {String} msg The validation message
19292      */
19293     markInvalid : function(msg)
19294     {
19295         if(this.allowBlank){
19296             return;
19297         }
19298         
19299         var _this = this;
19300         
19301         this.fireEvent('invalid', this, msg);
19302         
19303         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19304         
19305         if(this.groupId){
19306             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19307         }
19308         
19309         if(label){
19310             label.markInvalid();
19311         }
19312             
19313         if(this.inputType == 'radio'){
19314             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19315                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19316                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19317             });
19318             
19319             return;
19320         }
19321         
19322         if(!this.groupId){
19323             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19324             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19325             return;
19326         }
19327         
19328         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19329         
19330         if(!group){
19331             return;
19332         }
19333         
19334         for(var i in group){
19335             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19336             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19337         }
19338         
19339     }
19340     
19341 });
19342
19343 Roo.apply(Roo.bootstrap.CheckBox, {
19344     
19345     groups: {},
19346     
19347      /**
19348     * register a CheckBox Group
19349     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19350     */
19351     register : function(checkbox)
19352     {
19353         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19354             this.groups[checkbox.groupId] = {};
19355         }
19356         
19357         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19358             return;
19359         }
19360         
19361         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19362         
19363     },
19364     /**
19365     * fetch a CheckBox Group based on the group ID
19366     * @param {string} the group ID
19367     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19368     */
19369     get: function(groupId) {
19370         if (typeof(this.groups[groupId]) == 'undefined') {
19371             return false;
19372         }
19373         
19374         return this.groups[groupId] ;
19375     }
19376     
19377     
19378 });
19379 /*
19380  * - LGPL
19381  *
19382  * Radio
19383  *
19384  *
19385  * not inline
19386  *<div class="radio">
19387   <label>
19388     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19389     Option one is this and that&mdash;be sure to include why it's great
19390   </label>
19391 </div>
19392  *
19393  *
19394  *inline
19395  *<span>
19396  *<label class="radio-inline">fieldLabel</label>
19397  *<label class="radio-inline">
19398   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19399 </label>
19400 <span>
19401  * 
19402  * 
19403  */
19404
19405 /**
19406  * @class Roo.bootstrap.Radio
19407  * @extends Roo.bootstrap.CheckBox
19408  * Bootstrap Radio class
19409
19410  * @constructor
19411  * Create a new Radio
19412  * @param {Object} config The config object
19413  */
19414
19415 Roo.bootstrap.Radio = function(config){
19416     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19417    
19418 };
19419
19420 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19421     
19422     inputType: 'radio',
19423     inputValue: '',
19424     valueOff: '',
19425     
19426     getAutoCreate : function()
19427     {
19428         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19429         align = align || 'left'; // default...
19430         
19431         
19432         
19433         var id = Roo.id();
19434         
19435         var cfg = {
19436                 tag : this.inline ? 'span' : 'div',
19437                 cls : '',
19438                 cn : []
19439         };
19440         
19441         var inline = this.inline ? ' radio-inline' : '';
19442         
19443         var lbl = {
19444                 tag: 'label' ,
19445                 // does not need for, as we wrap the input with it..
19446                 'for' : id,
19447                 cls : 'control-label box-label' + inline,
19448                 cn : []
19449         };
19450         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19451         
19452         var fieldLabel = {
19453             tag: 'label' ,
19454             //cls : 'control-label' + inline,
19455             html : this.fieldLabel,
19456             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19457         };
19458         
19459  
19460         
19461         
19462         var input =  {
19463             tag: 'input',
19464             id : id,
19465             type : this.inputType,
19466             //value : (!this.checked) ? this.valueOff : this.inputValue,
19467             value : this.inputValue,
19468             cls : 'roo-radio',
19469             placeholder : this.placeholder || '' // ?? needed????
19470             
19471         };
19472         if (this.weight) { // Validity check?
19473             input.cls += " radio-" + this.weight;
19474         }
19475         if (this.disabled) {
19476             input.disabled=true;
19477         }
19478         
19479         if(this.checked){
19480             input.checked = this.checked;
19481         }
19482         
19483         if (this.name) {
19484             input.name = this.name;
19485         }
19486         
19487         if (this.size) {
19488             input.cls += ' input-' + this.size;
19489         }
19490         
19491         //?? can span's inline have a width??
19492         
19493         var settings=this;
19494         ['xs','sm','md','lg'].map(function(size){
19495             if (settings[size]) {
19496                 cfg.cls += ' col-' + size + '-' + settings[size];
19497             }
19498         });
19499         
19500         var inputblock = input;
19501         
19502         if (this.before || this.after) {
19503             
19504             inputblock = {
19505                 cls : 'input-group',
19506                 tag : 'span',
19507                 cn :  [] 
19508             };
19509             if (this.before) {
19510                 inputblock.cn.push({
19511                     tag :'span',
19512                     cls : 'input-group-addon',
19513                     html : this.before
19514                 });
19515             }
19516             inputblock.cn.push(input);
19517             if (this.after) {
19518                 inputblock.cn.push({
19519                     tag :'span',
19520                     cls : 'input-group-addon',
19521                     html : this.after
19522                 });
19523             }
19524             
19525         };
19526         
19527         
19528         if (this.fieldLabel && this.fieldLabel.length) {
19529             cfg.cn.push(fieldLabel);
19530         }
19531        
19532         // normal bootstrap puts the input inside the label.
19533         // however with our styled version - it has to go after the input.
19534        
19535         //lbl.cn.push(inputblock);
19536         
19537         var lblwrap =  {
19538             tag: 'span',
19539             cls: 'radio' + inline,
19540             cn: [
19541                 inputblock,
19542                 lbl
19543             ]
19544         };
19545         
19546         cfg.cn.push( lblwrap);
19547         
19548         if(this.boxLabel){
19549             lbl.cn.push({
19550                 tag: 'span',
19551                 html: this.boxLabel
19552             })
19553         }
19554          
19555         
19556         return cfg;
19557         
19558     },
19559     
19560     initEvents : function()
19561     {
19562 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19563         
19564         this.inputEl().on('click', this.onClick,  this);
19565         if (this.boxLabel) {
19566             //Roo.log('find label');
19567             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19568         }
19569         
19570     },
19571     
19572     inputEl: function ()
19573     {
19574         return this.el.select('input.roo-radio',true).first();
19575     },
19576     onClick : function()
19577     {   
19578         Roo.log("click");
19579         this.setChecked(true);
19580     },
19581     
19582     setChecked : function(state,suppressEvent)
19583     {
19584         if(state){
19585             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19586                 v.dom.checked = false;
19587             });
19588         }
19589         Roo.log(this.inputEl().dom);
19590         this.checked = state;
19591         this.inputEl().dom.checked = state;
19592         
19593         if(suppressEvent !== true){
19594             this.fireEvent('check', this, state);
19595         }
19596         
19597         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19598         
19599     },
19600     
19601     getGroupValue : function()
19602     {
19603         var value = '';
19604         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19605             if(v.dom.checked == true){
19606                 value = v.dom.value;
19607             }
19608         });
19609         
19610         return value;
19611     },
19612     
19613     /**
19614      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19615      * @return {Mixed} value The field value
19616      */
19617     getValue : function(){
19618         return this.getGroupValue();
19619     }
19620     
19621 });
19622
19623  
19624 //<script type="text/javascript">
19625
19626 /*
19627  * Based  Ext JS Library 1.1.1
19628  * Copyright(c) 2006-2007, Ext JS, LLC.
19629  * LGPL
19630  *
19631  */
19632  
19633 /**
19634  * @class Roo.HtmlEditorCore
19635  * @extends Roo.Component
19636  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19637  *
19638  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19639  */
19640
19641 Roo.HtmlEditorCore = function(config){
19642     
19643     
19644     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19645     
19646     
19647     this.addEvents({
19648         /**
19649          * @event initialize
19650          * Fires when the editor is fully initialized (including the iframe)
19651          * @param {Roo.HtmlEditorCore} this
19652          */
19653         initialize: true,
19654         /**
19655          * @event activate
19656          * Fires when the editor is first receives the focus. Any insertion must wait
19657          * until after this event.
19658          * @param {Roo.HtmlEditorCore} this
19659          */
19660         activate: true,
19661          /**
19662          * @event beforesync
19663          * Fires before the textarea is updated with content from the editor iframe. Return false
19664          * to cancel the sync.
19665          * @param {Roo.HtmlEditorCore} this
19666          * @param {String} html
19667          */
19668         beforesync: true,
19669          /**
19670          * @event beforepush
19671          * Fires before the iframe editor is updated with content from the textarea. Return false
19672          * to cancel the push.
19673          * @param {Roo.HtmlEditorCore} this
19674          * @param {String} html
19675          */
19676         beforepush: true,
19677          /**
19678          * @event sync
19679          * Fires when the textarea is updated with content from the editor iframe.
19680          * @param {Roo.HtmlEditorCore} this
19681          * @param {String} html
19682          */
19683         sync: true,
19684          /**
19685          * @event push
19686          * Fires when the iframe editor is updated with content from the textarea.
19687          * @param {Roo.HtmlEditorCore} this
19688          * @param {String} html
19689          */
19690         push: true,
19691         
19692         /**
19693          * @event editorevent
19694          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19695          * @param {Roo.HtmlEditorCore} this
19696          */
19697         editorevent: true
19698         
19699     });
19700     
19701     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19702     
19703     // defaults : white / black...
19704     this.applyBlacklists();
19705     
19706     
19707     
19708 };
19709
19710
19711 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19712
19713
19714      /**
19715      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19716      */
19717     
19718     owner : false,
19719     
19720      /**
19721      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19722      *                        Roo.resizable.
19723      */
19724     resizable : false,
19725      /**
19726      * @cfg {Number} height (in pixels)
19727      */   
19728     height: 300,
19729    /**
19730      * @cfg {Number} width (in pixels)
19731      */   
19732     width: 500,
19733     
19734     /**
19735      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19736      * 
19737      */
19738     stylesheets: false,
19739     
19740     // id of frame..
19741     frameId: false,
19742     
19743     // private properties
19744     validationEvent : false,
19745     deferHeight: true,
19746     initialized : false,
19747     activated : false,
19748     sourceEditMode : false,
19749     onFocus : Roo.emptyFn,
19750     iframePad:3,
19751     hideMode:'offsets',
19752     
19753     clearUp: true,
19754     
19755     // blacklist + whitelisted elements..
19756     black: false,
19757     white: false,
19758      
19759     
19760
19761     /**
19762      * Protected method that will not generally be called directly. It
19763      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19764      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19765      */
19766     getDocMarkup : function(){
19767         // body styles..
19768         var st = '';
19769         
19770         // inherit styels from page...?? 
19771         if (this.stylesheets === false) {
19772             
19773             Roo.get(document.head).select('style').each(function(node) {
19774                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19775             });
19776             
19777             Roo.get(document.head).select('link').each(function(node) { 
19778                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19779             });
19780             
19781         } else if (!this.stylesheets.length) {
19782                 // simple..
19783                 st = '<style type="text/css">' +
19784                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19785                    '</style>';
19786         } else { 
19787             
19788         }
19789         
19790         st +=  '<style type="text/css">' +
19791             'IMG { cursor: pointer } ' +
19792         '</style>';
19793
19794         
19795         return '<html><head>' + st  +
19796             //<style type="text/css">' +
19797             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19798             //'</style>' +
19799             ' </head><body class="roo-htmleditor-body"></body></html>';
19800     },
19801
19802     // private
19803     onRender : function(ct, position)
19804     {
19805         var _t = this;
19806         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19807         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19808         
19809         
19810         this.el.dom.style.border = '0 none';
19811         this.el.dom.setAttribute('tabIndex', -1);
19812         this.el.addClass('x-hidden hide');
19813         
19814         
19815         
19816         if(Roo.isIE){ // fix IE 1px bogus margin
19817             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19818         }
19819        
19820         
19821         this.frameId = Roo.id();
19822         
19823          
19824         
19825         var iframe = this.owner.wrap.createChild({
19826             tag: 'iframe',
19827             cls: 'form-control', // bootstrap..
19828             id: this.frameId,
19829             name: this.frameId,
19830             frameBorder : 'no',
19831             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19832         }, this.el
19833         );
19834         
19835         
19836         this.iframe = iframe.dom;
19837
19838          this.assignDocWin();
19839         
19840         this.doc.designMode = 'on';
19841        
19842         this.doc.open();
19843         this.doc.write(this.getDocMarkup());
19844         this.doc.close();
19845
19846         
19847         var task = { // must defer to wait for browser to be ready
19848             run : function(){
19849                 //console.log("run task?" + this.doc.readyState);
19850                 this.assignDocWin();
19851                 if(this.doc.body || this.doc.readyState == 'complete'){
19852                     try {
19853                         this.doc.designMode="on";
19854                     } catch (e) {
19855                         return;
19856                     }
19857                     Roo.TaskMgr.stop(task);
19858                     this.initEditor.defer(10, this);
19859                 }
19860             },
19861             interval : 10,
19862             duration: 10000,
19863             scope: this
19864         };
19865         Roo.TaskMgr.start(task);
19866
19867     },
19868
19869     // private
19870     onResize : function(w, h)
19871     {
19872          Roo.log('resize: ' +w + ',' + h );
19873         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19874         if(!this.iframe){
19875             return;
19876         }
19877         if(typeof w == 'number'){
19878             
19879             this.iframe.style.width = w + 'px';
19880         }
19881         if(typeof h == 'number'){
19882             
19883             this.iframe.style.height = h + 'px';
19884             if(this.doc){
19885                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19886             }
19887         }
19888         
19889     },
19890
19891     /**
19892      * Toggles the editor between standard and source edit mode.
19893      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19894      */
19895     toggleSourceEdit : function(sourceEditMode){
19896         
19897         this.sourceEditMode = sourceEditMode === true;
19898         
19899         if(this.sourceEditMode){
19900  
19901             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19902             
19903         }else{
19904             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19905             //this.iframe.className = '';
19906             this.deferFocus();
19907         }
19908         //this.setSize(this.owner.wrap.getSize());
19909         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19910     },
19911
19912     
19913   
19914
19915     /**
19916      * Protected method that will not generally be called directly. If you need/want
19917      * custom HTML cleanup, this is the method you should override.
19918      * @param {String} html The HTML to be cleaned
19919      * return {String} The cleaned HTML
19920      */
19921     cleanHtml : function(html){
19922         html = String(html);
19923         if(html.length > 5){
19924             if(Roo.isSafari){ // strip safari nonsense
19925                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19926             }
19927         }
19928         if(html == '&nbsp;'){
19929             html = '';
19930         }
19931         return html;
19932     },
19933
19934     /**
19935      * HTML Editor -> Textarea
19936      * Protected method that will not generally be called directly. Syncs the contents
19937      * of the editor iframe with the textarea.
19938      */
19939     syncValue : function(){
19940         if(this.initialized){
19941             var bd = (this.doc.body || this.doc.documentElement);
19942             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19943             var html = bd.innerHTML;
19944             if(Roo.isSafari){
19945                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19946                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19947                 if(m && m[1]){
19948                     html = '<div style="'+m[0]+'">' + html + '</div>';
19949                 }
19950             }
19951             html = this.cleanHtml(html);
19952             // fix up the special chars.. normaly like back quotes in word...
19953             // however we do not want to do this with chinese..
19954             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19955                 var cc = b.charCodeAt();
19956                 if (
19957                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19958                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19959                     (cc >= 0xf900 && cc < 0xfb00 )
19960                 ) {
19961                         return b;
19962                 }
19963                 return "&#"+cc+";" 
19964             });
19965             if(this.owner.fireEvent('beforesync', this, html) !== false){
19966                 this.el.dom.value = html;
19967                 this.owner.fireEvent('sync', this, html);
19968             }
19969         }
19970     },
19971
19972     /**
19973      * Protected method that will not generally be called directly. Pushes the value of the textarea
19974      * into the iframe editor.
19975      */
19976     pushValue : function(){
19977         if(this.initialized){
19978             var v = this.el.dom.value.trim();
19979             
19980 //            if(v.length < 1){
19981 //                v = '&#160;';
19982 //            }
19983             
19984             if(this.owner.fireEvent('beforepush', this, v) !== false){
19985                 var d = (this.doc.body || this.doc.documentElement);
19986                 d.innerHTML = v;
19987                 this.cleanUpPaste();
19988                 this.el.dom.value = d.innerHTML;
19989                 this.owner.fireEvent('push', this, v);
19990             }
19991         }
19992     },
19993
19994     // private
19995     deferFocus : function(){
19996         this.focus.defer(10, this);
19997     },
19998
19999     // doc'ed in Field
20000     focus : function(){
20001         if(this.win && !this.sourceEditMode){
20002             this.win.focus();
20003         }else{
20004             this.el.focus();
20005         }
20006     },
20007     
20008     assignDocWin: function()
20009     {
20010         var iframe = this.iframe;
20011         
20012          if(Roo.isIE){
20013             this.doc = iframe.contentWindow.document;
20014             this.win = iframe.contentWindow;
20015         } else {
20016 //            if (!Roo.get(this.frameId)) {
20017 //                return;
20018 //            }
20019 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20020 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20021             
20022             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20023                 return;
20024             }
20025             
20026             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20027             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20028         }
20029     },
20030     
20031     // private
20032     initEditor : function(){
20033         //console.log("INIT EDITOR");
20034         this.assignDocWin();
20035         
20036         
20037         
20038         this.doc.designMode="on";
20039         this.doc.open();
20040         this.doc.write(this.getDocMarkup());
20041         this.doc.close();
20042         
20043         var dbody = (this.doc.body || this.doc.documentElement);
20044         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20045         // this copies styles from the containing element into thsi one..
20046         // not sure why we need all of this..
20047         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20048         
20049         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20050         //ss['background-attachment'] = 'fixed'; // w3c
20051         dbody.bgProperties = 'fixed'; // ie
20052         //Roo.DomHelper.applyStyles(dbody, ss);
20053         Roo.EventManager.on(this.doc, {
20054             //'mousedown': this.onEditorEvent,
20055             'mouseup': this.onEditorEvent,
20056             'dblclick': this.onEditorEvent,
20057             'click': this.onEditorEvent,
20058             'keyup': this.onEditorEvent,
20059             buffer:100,
20060             scope: this
20061         });
20062         if(Roo.isGecko){
20063             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20064         }
20065         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20066             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20067         }
20068         this.initialized = true;
20069
20070         this.owner.fireEvent('initialize', this);
20071         this.pushValue();
20072     },
20073
20074     // private
20075     onDestroy : function(){
20076         
20077         
20078         
20079         if(this.rendered){
20080             
20081             //for (var i =0; i < this.toolbars.length;i++) {
20082             //    // fixme - ask toolbars for heights?
20083             //    this.toolbars[i].onDestroy();
20084            // }
20085             
20086             //this.wrap.dom.innerHTML = '';
20087             //this.wrap.remove();
20088         }
20089     },
20090
20091     // private
20092     onFirstFocus : function(){
20093         
20094         this.assignDocWin();
20095         
20096         
20097         this.activated = true;
20098          
20099     
20100         if(Roo.isGecko){ // prevent silly gecko errors
20101             this.win.focus();
20102             var s = this.win.getSelection();
20103             if(!s.focusNode || s.focusNode.nodeType != 3){
20104                 var r = s.getRangeAt(0);
20105                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20106                 r.collapse(true);
20107                 this.deferFocus();
20108             }
20109             try{
20110                 this.execCmd('useCSS', true);
20111                 this.execCmd('styleWithCSS', false);
20112             }catch(e){}
20113         }
20114         this.owner.fireEvent('activate', this);
20115     },
20116
20117     // private
20118     adjustFont: function(btn){
20119         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20120         //if(Roo.isSafari){ // safari
20121         //    adjust *= 2;
20122        // }
20123         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20124         if(Roo.isSafari){ // safari
20125             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20126             v =  (v < 10) ? 10 : v;
20127             v =  (v > 48) ? 48 : v;
20128             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20129             
20130         }
20131         
20132         
20133         v = Math.max(1, v+adjust);
20134         
20135         this.execCmd('FontSize', v  );
20136     },
20137
20138     onEditorEvent : function(e)
20139     {
20140         this.owner.fireEvent('editorevent', this, e);
20141       //  this.updateToolbar();
20142         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20143     },
20144
20145     insertTag : function(tg)
20146     {
20147         // could be a bit smarter... -> wrap the current selected tRoo..
20148         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20149             
20150             range = this.createRange(this.getSelection());
20151             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20152             wrappingNode.appendChild(range.extractContents());
20153             range.insertNode(wrappingNode);
20154
20155             return;
20156             
20157             
20158             
20159         }
20160         this.execCmd("formatblock",   tg);
20161         
20162     },
20163     
20164     insertText : function(txt)
20165     {
20166         
20167         
20168         var range = this.createRange();
20169         range.deleteContents();
20170                //alert(Sender.getAttribute('label'));
20171                
20172         range.insertNode(this.doc.createTextNode(txt));
20173     } ,
20174     
20175      
20176
20177     /**
20178      * Executes a Midas editor command on the editor document and performs necessary focus and
20179      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20180      * @param {String} cmd The Midas command
20181      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20182      */
20183     relayCmd : function(cmd, value){
20184         this.win.focus();
20185         this.execCmd(cmd, value);
20186         this.owner.fireEvent('editorevent', this);
20187         //this.updateToolbar();
20188         this.owner.deferFocus();
20189     },
20190
20191     /**
20192      * Executes a Midas editor command directly on the editor document.
20193      * For visual commands, you should use {@link #relayCmd} instead.
20194      * <b>This should only be called after the editor is initialized.</b>
20195      * @param {String} cmd The Midas command
20196      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20197      */
20198     execCmd : function(cmd, value){
20199         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20200         this.syncValue();
20201     },
20202  
20203  
20204    
20205     /**
20206      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20207      * to insert tRoo.
20208      * @param {String} text | dom node.. 
20209      */
20210     insertAtCursor : function(text)
20211     {
20212         
20213         
20214         
20215         if(!this.activated){
20216             return;
20217         }
20218         /*
20219         if(Roo.isIE){
20220             this.win.focus();
20221             var r = this.doc.selection.createRange();
20222             if(r){
20223                 r.collapse(true);
20224                 r.pasteHTML(text);
20225                 this.syncValue();
20226                 this.deferFocus();
20227             
20228             }
20229             return;
20230         }
20231         */
20232         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20233             this.win.focus();
20234             
20235             
20236             // from jquery ui (MIT licenced)
20237             var range, node;
20238             var win = this.win;
20239             
20240             if (win.getSelection && win.getSelection().getRangeAt) {
20241                 range = win.getSelection().getRangeAt(0);
20242                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20243                 range.insertNode(node);
20244             } else if (win.document.selection && win.document.selection.createRange) {
20245                 // no firefox support
20246                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20247                 win.document.selection.createRange().pasteHTML(txt);
20248             } else {
20249                 // no firefox support
20250                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20251                 this.execCmd('InsertHTML', txt);
20252             } 
20253             
20254             this.syncValue();
20255             
20256             this.deferFocus();
20257         }
20258     },
20259  // private
20260     mozKeyPress : function(e){
20261         if(e.ctrlKey){
20262             var c = e.getCharCode(), cmd;
20263           
20264             if(c > 0){
20265                 c = String.fromCharCode(c).toLowerCase();
20266                 switch(c){
20267                     case 'b':
20268                         cmd = 'bold';
20269                         break;
20270                     case 'i':
20271                         cmd = 'italic';
20272                         break;
20273                     
20274                     case 'u':
20275                         cmd = 'underline';
20276                         break;
20277                     
20278                     case 'v':
20279                         this.cleanUpPaste.defer(100, this);
20280                         return;
20281                         
20282                 }
20283                 if(cmd){
20284                     this.win.focus();
20285                     this.execCmd(cmd);
20286                     this.deferFocus();
20287                     e.preventDefault();
20288                 }
20289                 
20290             }
20291         }
20292     },
20293
20294     // private
20295     fixKeys : function(){ // load time branching for fastest keydown performance
20296         if(Roo.isIE){
20297             return function(e){
20298                 var k = e.getKey(), r;
20299                 if(k == e.TAB){
20300                     e.stopEvent();
20301                     r = this.doc.selection.createRange();
20302                     if(r){
20303                         r.collapse(true);
20304                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20305                         this.deferFocus();
20306                     }
20307                     return;
20308                 }
20309                 
20310                 if(k == e.ENTER){
20311                     r = this.doc.selection.createRange();
20312                     if(r){
20313                         var target = r.parentElement();
20314                         if(!target || target.tagName.toLowerCase() != 'li'){
20315                             e.stopEvent();
20316                             r.pasteHTML('<br />');
20317                             r.collapse(false);
20318                             r.select();
20319                         }
20320                     }
20321                 }
20322                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20323                     this.cleanUpPaste.defer(100, this);
20324                     return;
20325                 }
20326                 
20327                 
20328             };
20329         }else if(Roo.isOpera){
20330             return function(e){
20331                 var k = e.getKey();
20332                 if(k == e.TAB){
20333                     e.stopEvent();
20334                     this.win.focus();
20335                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20336                     this.deferFocus();
20337                 }
20338                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20339                     this.cleanUpPaste.defer(100, this);
20340                     return;
20341                 }
20342                 
20343             };
20344         }else if(Roo.isSafari){
20345             return function(e){
20346                 var k = e.getKey();
20347                 
20348                 if(k == e.TAB){
20349                     e.stopEvent();
20350                     this.execCmd('InsertText','\t');
20351                     this.deferFocus();
20352                     return;
20353                 }
20354                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20355                     this.cleanUpPaste.defer(100, this);
20356                     return;
20357                 }
20358                 
20359              };
20360         }
20361     }(),
20362     
20363     getAllAncestors: function()
20364     {
20365         var p = this.getSelectedNode();
20366         var a = [];
20367         if (!p) {
20368             a.push(p); // push blank onto stack..
20369             p = this.getParentElement();
20370         }
20371         
20372         
20373         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20374             a.push(p);
20375             p = p.parentNode;
20376         }
20377         a.push(this.doc.body);
20378         return a;
20379     },
20380     lastSel : false,
20381     lastSelNode : false,
20382     
20383     
20384     getSelection : function() 
20385     {
20386         this.assignDocWin();
20387         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20388     },
20389     
20390     getSelectedNode: function() 
20391     {
20392         // this may only work on Gecko!!!
20393         
20394         // should we cache this!!!!
20395         
20396         
20397         
20398          
20399         var range = this.createRange(this.getSelection()).cloneRange();
20400         
20401         if (Roo.isIE) {
20402             var parent = range.parentElement();
20403             while (true) {
20404                 var testRange = range.duplicate();
20405                 testRange.moveToElementText(parent);
20406                 if (testRange.inRange(range)) {
20407                     break;
20408                 }
20409                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20410                     break;
20411                 }
20412                 parent = parent.parentElement;
20413             }
20414             return parent;
20415         }
20416         
20417         // is ancestor a text element.
20418         var ac =  range.commonAncestorContainer;
20419         if (ac.nodeType == 3) {
20420             ac = ac.parentNode;
20421         }
20422         
20423         var ar = ac.childNodes;
20424          
20425         var nodes = [];
20426         var other_nodes = [];
20427         var has_other_nodes = false;
20428         for (var i=0;i<ar.length;i++) {
20429             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20430                 continue;
20431             }
20432             // fullly contained node.
20433             
20434             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20435                 nodes.push(ar[i]);
20436                 continue;
20437             }
20438             
20439             // probably selected..
20440             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20441                 other_nodes.push(ar[i]);
20442                 continue;
20443             }
20444             // outer..
20445             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20446                 continue;
20447             }
20448             
20449             
20450             has_other_nodes = true;
20451         }
20452         if (!nodes.length && other_nodes.length) {
20453             nodes= other_nodes;
20454         }
20455         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20456             return false;
20457         }
20458         
20459         return nodes[0];
20460     },
20461     createRange: function(sel)
20462     {
20463         // this has strange effects when using with 
20464         // top toolbar - not sure if it's a great idea.
20465         //this.editor.contentWindow.focus();
20466         if (typeof sel != "undefined") {
20467             try {
20468                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20469             } catch(e) {
20470                 return this.doc.createRange();
20471             }
20472         } else {
20473             return this.doc.createRange();
20474         }
20475     },
20476     getParentElement: function()
20477     {
20478         
20479         this.assignDocWin();
20480         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20481         
20482         var range = this.createRange(sel);
20483          
20484         try {
20485             var p = range.commonAncestorContainer;
20486             while (p.nodeType == 3) { // text node
20487                 p = p.parentNode;
20488             }
20489             return p;
20490         } catch (e) {
20491             return null;
20492         }
20493     
20494     },
20495     /***
20496      *
20497      * Range intersection.. the hard stuff...
20498      *  '-1' = before
20499      *  '0' = hits..
20500      *  '1' = after.
20501      *         [ -- selected range --- ]
20502      *   [fail]                        [fail]
20503      *
20504      *    basically..
20505      *      if end is before start or  hits it. fail.
20506      *      if start is after end or hits it fail.
20507      *
20508      *   if either hits (but other is outside. - then it's not 
20509      *   
20510      *    
20511      **/
20512     
20513     
20514     // @see http://www.thismuchiknow.co.uk/?p=64.
20515     rangeIntersectsNode : function(range, node)
20516     {
20517         var nodeRange = node.ownerDocument.createRange();
20518         try {
20519             nodeRange.selectNode(node);
20520         } catch (e) {
20521             nodeRange.selectNodeContents(node);
20522         }
20523     
20524         var rangeStartRange = range.cloneRange();
20525         rangeStartRange.collapse(true);
20526     
20527         var rangeEndRange = range.cloneRange();
20528         rangeEndRange.collapse(false);
20529     
20530         var nodeStartRange = nodeRange.cloneRange();
20531         nodeStartRange.collapse(true);
20532     
20533         var nodeEndRange = nodeRange.cloneRange();
20534         nodeEndRange.collapse(false);
20535     
20536         return rangeStartRange.compareBoundaryPoints(
20537                  Range.START_TO_START, nodeEndRange) == -1 &&
20538                rangeEndRange.compareBoundaryPoints(
20539                  Range.START_TO_START, nodeStartRange) == 1;
20540         
20541          
20542     },
20543     rangeCompareNode : function(range, node)
20544     {
20545         var nodeRange = node.ownerDocument.createRange();
20546         try {
20547             nodeRange.selectNode(node);
20548         } catch (e) {
20549             nodeRange.selectNodeContents(node);
20550         }
20551         
20552         
20553         range.collapse(true);
20554     
20555         nodeRange.collapse(true);
20556      
20557         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20558         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20559          
20560         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20561         
20562         var nodeIsBefore   =  ss == 1;
20563         var nodeIsAfter    = ee == -1;
20564         
20565         if (nodeIsBefore && nodeIsAfter) {
20566             return 0; // outer
20567         }
20568         if (!nodeIsBefore && nodeIsAfter) {
20569             return 1; //right trailed.
20570         }
20571         
20572         if (nodeIsBefore && !nodeIsAfter) {
20573             return 2;  // left trailed.
20574         }
20575         // fully contined.
20576         return 3;
20577     },
20578
20579     // private? - in a new class?
20580     cleanUpPaste :  function()
20581     {
20582         // cleans up the whole document..
20583         Roo.log('cleanuppaste');
20584         
20585         this.cleanUpChildren(this.doc.body);
20586         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20587         if (clean != this.doc.body.innerHTML) {
20588             this.doc.body.innerHTML = clean;
20589         }
20590         
20591     },
20592     
20593     cleanWordChars : function(input) {// change the chars to hex code
20594         var he = Roo.HtmlEditorCore;
20595         
20596         var output = input;
20597         Roo.each(he.swapCodes, function(sw) { 
20598             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20599             
20600             output = output.replace(swapper, sw[1]);
20601         });
20602         
20603         return output;
20604     },
20605     
20606     
20607     cleanUpChildren : function (n)
20608     {
20609         if (!n.childNodes.length) {
20610             return;
20611         }
20612         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20613            this.cleanUpChild(n.childNodes[i]);
20614         }
20615     },
20616     
20617     
20618         
20619     
20620     cleanUpChild : function (node)
20621     {
20622         var ed = this;
20623         //console.log(node);
20624         if (node.nodeName == "#text") {
20625             // clean up silly Windows -- stuff?
20626             return; 
20627         }
20628         if (node.nodeName == "#comment") {
20629             node.parentNode.removeChild(node);
20630             // clean up silly Windows -- stuff?
20631             return; 
20632         }
20633         var lcname = node.tagName.toLowerCase();
20634         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20635         // whitelist of tags..
20636         
20637         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20638             // remove node.
20639             node.parentNode.removeChild(node);
20640             return;
20641             
20642         }
20643         
20644         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20645         
20646         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20647         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20648         
20649         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20650         //    remove_keep_children = true;
20651         //}
20652         
20653         if (remove_keep_children) {
20654             this.cleanUpChildren(node);
20655             // inserts everything just before this node...
20656             while (node.childNodes.length) {
20657                 var cn = node.childNodes[0];
20658                 node.removeChild(cn);
20659                 node.parentNode.insertBefore(cn, node);
20660             }
20661             node.parentNode.removeChild(node);
20662             return;
20663         }
20664         
20665         if (!node.attributes || !node.attributes.length) {
20666             this.cleanUpChildren(node);
20667             return;
20668         }
20669         
20670         function cleanAttr(n,v)
20671         {
20672             
20673             if (v.match(/^\./) || v.match(/^\//)) {
20674                 return;
20675             }
20676             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20677                 return;
20678             }
20679             if (v.match(/^#/)) {
20680                 return;
20681             }
20682 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20683             node.removeAttribute(n);
20684             
20685         }
20686         
20687         var cwhite = this.cwhite;
20688         var cblack = this.cblack;
20689             
20690         function cleanStyle(n,v)
20691         {
20692             if (v.match(/expression/)) { //XSS?? should we even bother..
20693                 node.removeAttribute(n);
20694                 return;
20695             }
20696             
20697             var parts = v.split(/;/);
20698             var clean = [];
20699             
20700             Roo.each(parts, function(p) {
20701                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20702                 if (!p.length) {
20703                     return true;
20704                 }
20705                 var l = p.split(':').shift().replace(/\s+/g,'');
20706                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20707                 
20708                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20709 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20710                     //node.removeAttribute(n);
20711                     return true;
20712                 }
20713                 //Roo.log()
20714                 // only allow 'c whitelisted system attributes'
20715                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20716 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20717                     //node.removeAttribute(n);
20718                     return true;
20719                 }
20720                 
20721                 
20722                  
20723                 
20724                 clean.push(p);
20725                 return true;
20726             });
20727             if (clean.length) { 
20728                 node.setAttribute(n, clean.join(';'));
20729             } else {
20730                 node.removeAttribute(n);
20731             }
20732             
20733         }
20734         
20735         
20736         for (var i = node.attributes.length-1; i > -1 ; i--) {
20737             var a = node.attributes[i];
20738             //console.log(a);
20739             
20740             if (a.name.toLowerCase().substr(0,2)=='on')  {
20741                 node.removeAttribute(a.name);
20742                 continue;
20743             }
20744             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20745                 node.removeAttribute(a.name);
20746                 continue;
20747             }
20748             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20749                 cleanAttr(a.name,a.value); // fixme..
20750                 continue;
20751             }
20752             if (a.name == 'style') {
20753                 cleanStyle(a.name,a.value);
20754                 continue;
20755             }
20756             /// clean up MS crap..
20757             // tecnically this should be a list of valid class'es..
20758             
20759             
20760             if (a.name == 'class') {
20761                 if (a.value.match(/^Mso/)) {
20762                     node.className = '';
20763                 }
20764                 
20765                 if (a.value.match(/body/)) {
20766                     node.className = '';
20767                 }
20768                 continue;
20769             }
20770             
20771             // style cleanup!?
20772             // class cleanup?
20773             
20774         }
20775         
20776         
20777         this.cleanUpChildren(node);
20778         
20779         
20780     },
20781     
20782     /**
20783      * Clean up MS wordisms...
20784      */
20785     cleanWord : function(node)
20786     {
20787         
20788         
20789         if (!node) {
20790             this.cleanWord(this.doc.body);
20791             return;
20792         }
20793         if (node.nodeName == "#text") {
20794             // clean up silly Windows -- stuff?
20795             return; 
20796         }
20797         if (node.nodeName == "#comment") {
20798             node.parentNode.removeChild(node);
20799             // clean up silly Windows -- stuff?
20800             return; 
20801         }
20802         
20803         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20804             node.parentNode.removeChild(node);
20805             return;
20806         }
20807         
20808         // remove - but keep children..
20809         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20810             while (node.childNodes.length) {
20811                 var cn = node.childNodes[0];
20812                 node.removeChild(cn);
20813                 node.parentNode.insertBefore(cn, node);
20814             }
20815             node.parentNode.removeChild(node);
20816             this.iterateChildren(node, this.cleanWord);
20817             return;
20818         }
20819         // clean styles
20820         if (node.className.length) {
20821             
20822             var cn = node.className.split(/\W+/);
20823             var cna = [];
20824             Roo.each(cn, function(cls) {
20825                 if (cls.match(/Mso[a-zA-Z]+/)) {
20826                     return;
20827                 }
20828                 cna.push(cls);
20829             });
20830             node.className = cna.length ? cna.join(' ') : '';
20831             if (!cna.length) {
20832                 node.removeAttribute("class");
20833             }
20834         }
20835         
20836         if (node.hasAttribute("lang")) {
20837             node.removeAttribute("lang");
20838         }
20839         
20840         if (node.hasAttribute("style")) {
20841             
20842             var styles = node.getAttribute("style").split(";");
20843             var nstyle = [];
20844             Roo.each(styles, function(s) {
20845                 if (!s.match(/:/)) {
20846                     return;
20847                 }
20848                 var kv = s.split(":");
20849                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20850                     return;
20851                 }
20852                 // what ever is left... we allow.
20853                 nstyle.push(s);
20854             });
20855             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20856             if (!nstyle.length) {
20857                 node.removeAttribute('style');
20858             }
20859         }
20860         this.iterateChildren(node, this.cleanWord);
20861         
20862         
20863         
20864     },
20865     /**
20866      * iterateChildren of a Node, calling fn each time, using this as the scole..
20867      * @param {DomNode} node node to iterate children of.
20868      * @param {Function} fn method of this class to call on each item.
20869      */
20870     iterateChildren : function(node, fn)
20871     {
20872         if (!node.childNodes.length) {
20873                 return;
20874         }
20875         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20876            fn.call(this, node.childNodes[i])
20877         }
20878     },
20879     
20880     
20881     /**
20882      * cleanTableWidths.
20883      *
20884      * Quite often pasting from word etc.. results in tables with column and widths.
20885      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20886      *
20887      */
20888     cleanTableWidths : function(node)
20889     {
20890          
20891          
20892         if (!node) {
20893             this.cleanTableWidths(this.doc.body);
20894             return;
20895         }
20896         
20897         // ignore list...
20898         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20899             return; 
20900         }
20901         Roo.log(node.tagName);
20902         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20903             this.iterateChildren(node, this.cleanTableWidths);
20904             return;
20905         }
20906         if (node.hasAttribute('width')) {
20907             node.removeAttribute('width');
20908         }
20909         
20910          
20911         if (node.hasAttribute("style")) {
20912             // pretty basic...
20913             
20914             var styles = node.getAttribute("style").split(";");
20915             var nstyle = [];
20916             Roo.each(styles, function(s) {
20917                 if (!s.match(/:/)) {
20918                     return;
20919                 }
20920                 var kv = s.split(":");
20921                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20922                     return;
20923                 }
20924                 // what ever is left... we allow.
20925                 nstyle.push(s);
20926             });
20927             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20928             if (!nstyle.length) {
20929                 node.removeAttribute('style');
20930             }
20931         }
20932         
20933         this.iterateChildren(node, this.cleanTableWidths);
20934         
20935         
20936     },
20937     
20938     
20939     
20940     
20941     domToHTML : function(currentElement, depth, nopadtext) {
20942         
20943         depth = depth || 0;
20944         nopadtext = nopadtext || false;
20945     
20946         if (!currentElement) {
20947             return this.domToHTML(this.doc.body);
20948         }
20949         
20950         //Roo.log(currentElement);
20951         var j;
20952         var allText = false;
20953         var nodeName = currentElement.nodeName;
20954         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20955         
20956         if  (nodeName == '#text') {
20957             
20958             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20959         }
20960         
20961         
20962         var ret = '';
20963         if (nodeName != 'BODY') {
20964              
20965             var i = 0;
20966             // Prints the node tagName, such as <A>, <IMG>, etc
20967             if (tagName) {
20968                 var attr = [];
20969                 for(i = 0; i < currentElement.attributes.length;i++) {
20970                     // quoting?
20971                     var aname = currentElement.attributes.item(i).name;
20972                     if (!currentElement.attributes.item(i).value.length) {
20973                         continue;
20974                     }
20975                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20976                 }
20977                 
20978                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20979             } 
20980             else {
20981                 
20982                 // eack
20983             }
20984         } else {
20985             tagName = false;
20986         }
20987         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20988             return ret;
20989         }
20990         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20991             nopadtext = true;
20992         }
20993         
20994         
20995         // Traverse the tree
20996         i = 0;
20997         var currentElementChild = currentElement.childNodes.item(i);
20998         var allText = true;
20999         var innerHTML  = '';
21000         lastnode = '';
21001         while (currentElementChild) {
21002             // Formatting code (indent the tree so it looks nice on the screen)
21003             var nopad = nopadtext;
21004             if (lastnode == 'SPAN') {
21005                 nopad  = true;
21006             }
21007             // text
21008             if  (currentElementChild.nodeName == '#text') {
21009                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21010                 toadd = nopadtext ? toadd : toadd.trim();
21011                 if (!nopad && toadd.length > 80) {
21012                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21013                 }
21014                 innerHTML  += toadd;
21015                 
21016                 i++;
21017                 currentElementChild = currentElement.childNodes.item(i);
21018                 lastNode = '';
21019                 continue;
21020             }
21021             allText = false;
21022             
21023             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21024                 
21025             // Recursively traverse the tree structure of the child node
21026             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21027             lastnode = currentElementChild.nodeName;
21028             i++;
21029             currentElementChild=currentElement.childNodes.item(i);
21030         }
21031         
21032         ret += innerHTML;
21033         
21034         if (!allText) {
21035                 // The remaining code is mostly for formatting the tree
21036             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21037         }
21038         
21039         
21040         if (tagName) {
21041             ret+= "</"+tagName+">";
21042         }
21043         return ret;
21044         
21045     },
21046         
21047     applyBlacklists : function()
21048     {
21049         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21050         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21051         
21052         this.white = [];
21053         this.black = [];
21054         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21055             if (b.indexOf(tag) > -1) {
21056                 return;
21057             }
21058             this.white.push(tag);
21059             
21060         }, this);
21061         
21062         Roo.each(w, function(tag) {
21063             if (b.indexOf(tag) > -1) {
21064                 return;
21065             }
21066             if (this.white.indexOf(tag) > -1) {
21067                 return;
21068             }
21069             this.white.push(tag);
21070             
21071         }, this);
21072         
21073         
21074         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21075             if (w.indexOf(tag) > -1) {
21076                 return;
21077             }
21078             this.black.push(tag);
21079             
21080         }, this);
21081         
21082         Roo.each(b, function(tag) {
21083             if (w.indexOf(tag) > -1) {
21084                 return;
21085             }
21086             if (this.black.indexOf(tag) > -1) {
21087                 return;
21088             }
21089             this.black.push(tag);
21090             
21091         }, this);
21092         
21093         
21094         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21095         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21096         
21097         this.cwhite = [];
21098         this.cblack = [];
21099         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21100             if (b.indexOf(tag) > -1) {
21101                 return;
21102             }
21103             this.cwhite.push(tag);
21104             
21105         }, this);
21106         
21107         Roo.each(w, function(tag) {
21108             if (b.indexOf(tag) > -1) {
21109                 return;
21110             }
21111             if (this.cwhite.indexOf(tag) > -1) {
21112                 return;
21113             }
21114             this.cwhite.push(tag);
21115             
21116         }, this);
21117         
21118         
21119         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21120             if (w.indexOf(tag) > -1) {
21121                 return;
21122             }
21123             this.cblack.push(tag);
21124             
21125         }, this);
21126         
21127         Roo.each(b, function(tag) {
21128             if (w.indexOf(tag) > -1) {
21129                 return;
21130             }
21131             if (this.cblack.indexOf(tag) > -1) {
21132                 return;
21133             }
21134             this.cblack.push(tag);
21135             
21136         }, this);
21137     },
21138     
21139     setStylesheets : function(stylesheets)
21140     {
21141         if(typeof(stylesheets) == 'string'){
21142             Roo.get(this.iframe.contentDocument.head).createChild({
21143                 tag : 'link',
21144                 rel : 'stylesheet',
21145                 type : 'text/css',
21146                 href : stylesheets
21147             });
21148             
21149             return;
21150         }
21151         var _this = this;
21152      
21153         Roo.each(stylesheets, function(s) {
21154             if(!s.length){
21155                 return;
21156             }
21157             
21158             Roo.get(_this.iframe.contentDocument.head).createChild({
21159                 tag : 'link',
21160                 rel : 'stylesheet',
21161                 type : 'text/css',
21162                 href : s
21163             });
21164         });
21165
21166         
21167     },
21168     
21169     removeStylesheets : function()
21170     {
21171         var _this = this;
21172         
21173         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21174             s.remove();
21175         });
21176     }
21177     
21178     // hide stuff that is not compatible
21179     /**
21180      * @event blur
21181      * @hide
21182      */
21183     /**
21184      * @event change
21185      * @hide
21186      */
21187     /**
21188      * @event focus
21189      * @hide
21190      */
21191     /**
21192      * @event specialkey
21193      * @hide
21194      */
21195     /**
21196      * @cfg {String} fieldClass @hide
21197      */
21198     /**
21199      * @cfg {String} focusClass @hide
21200      */
21201     /**
21202      * @cfg {String} autoCreate @hide
21203      */
21204     /**
21205      * @cfg {String} inputType @hide
21206      */
21207     /**
21208      * @cfg {String} invalidClass @hide
21209      */
21210     /**
21211      * @cfg {String} invalidText @hide
21212      */
21213     /**
21214      * @cfg {String} msgFx @hide
21215      */
21216     /**
21217      * @cfg {String} validateOnBlur @hide
21218      */
21219 });
21220
21221 Roo.HtmlEditorCore.white = [
21222         'area', 'br', 'img', 'input', 'hr', 'wbr',
21223         
21224        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21225        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21226        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21227        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21228        'table',   'ul',         'xmp', 
21229        
21230        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21231       'thead',   'tr', 
21232      
21233       'dir', 'menu', 'ol', 'ul', 'dl',
21234        
21235       'embed',  'object'
21236 ];
21237
21238
21239 Roo.HtmlEditorCore.black = [
21240     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21241         'applet', // 
21242         'base',   'basefont', 'bgsound', 'blink',  'body', 
21243         'frame',  'frameset', 'head',    'html',   'ilayer', 
21244         'iframe', 'layer',  'link',     'meta',    'object',   
21245         'script', 'style' ,'title',  'xml' // clean later..
21246 ];
21247 Roo.HtmlEditorCore.clean = [
21248     'script', 'style', 'title', 'xml'
21249 ];
21250 Roo.HtmlEditorCore.remove = [
21251     'font'
21252 ];
21253 // attributes..
21254
21255 Roo.HtmlEditorCore.ablack = [
21256     'on'
21257 ];
21258     
21259 Roo.HtmlEditorCore.aclean = [ 
21260     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21261 ];
21262
21263 // protocols..
21264 Roo.HtmlEditorCore.pwhite= [
21265         'http',  'https',  'mailto'
21266 ];
21267
21268 // white listed style attributes.
21269 Roo.HtmlEditorCore.cwhite= [
21270       //  'text-align', /// default is to allow most things..
21271       
21272          
21273 //        'font-size'//??
21274 ];
21275
21276 // black listed style attributes.
21277 Roo.HtmlEditorCore.cblack= [
21278       //  'font-size' -- this can be set by the project 
21279 ];
21280
21281
21282 Roo.HtmlEditorCore.swapCodes   =[ 
21283     [    8211, "--" ], 
21284     [    8212, "--" ], 
21285     [    8216,  "'" ],  
21286     [    8217, "'" ],  
21287     [    8220, '"' ],  
21288     [    8221, '"' ],  
21289     [    8226, "*" ],  
21290     [    8230, "..." ]
21291 ]; 
21292
21293     /*
21294  * - LGPL
21295  *
21296  * HtmlEditor
21297  * 
21298  */
21299
21300 /**
21301  * @class Roo.bootstrap.HtmlEditor
21302  * @extends Roo.bootstrap.TextArea
21303  * Bootstrap HtmlEditor class
21304
21305  * @constructor
21306  * Create a new HtmlEditor
21307  * @param {Object} config The config object
21308  */
21309
21310 Roo.bootstrap.HtmlEditor = function(config){
21311     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21312     if (!this.toolbars) {
21313         this.toolbars = [];
21314     }
21315     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21316     this.addEvents({
21317             /**
21318              * @event initialize
21319              * Fires when the editor is fully initialized (including the iframe)
21320              * @param {HtmlEditor} this
21321              */
21322             initialize: true,
21323             /**
21324              * @event activate
21325              * Fires when the editor is first receives the focus. Any insertion must wait
21326              * until after this event.
21327              * @param {HtmlEditor} this
21328              */
21329             activate: true,
21330              /**
21331              * @event beforesync
21332              * Fires before the textarea is updated with content from the editor iframe. Return false
21333              * to cancel the sync.
21334              * @param {HtmlEditor} this
21335              * @param {String} html
21336              */
21337             beforesync: true,
21338              /**
21339              * @event beforepush
21340              * Fires before the iframe editor is updated with content from the textarea. Return false
21341              * to cancel the push.
21342              * @param {HtmlEditor} this
21343              * @param {String} html
21344              */
21345             beforepush: true,
21346              /**
21347              * @event sync
21348              * Fires when the textarea is updated with content from the editor iframe.
21349              * @param {HtmlEditor} this
21350              * @param {String} html
21351              */
21352             sync: true,
21353              /**
21354              * @event push
21355              * Fires when the iframe editor is updated with content from the textarea.
21356              * @param {HtmlEditor} this
21357              * @param {String} html
21358              */
21359             push: true,
21360              /**
21361              * @event editmodechange
21362              * Fires when the editor switches edit modes
21363              * @param {HtmlEditor} this
21364              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21365              */
21366             editmodechange: true,
21367             /**
21368              * @event editorevent
21369              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21370              * @param {HtmlEditor} this
21371              */
21372             editorevent: true,
21373             /**
21374              * @event firstfocus
21375              * Fires when on first focus - needed by toolbars..
21376              * @param {HtmlEditor} this
21377              */
21378             firstfocus: true,
21379             /**
21380              * @event autosave
21381              * Auto save the htmlEditor value as a file into Events
21382              * @param {HtmlEditor} this
21383              */
21384             autosave: true,
21385             /**
21386              * @event savedpreview
21387              * preview the saved version of htmlEditor
21388              * @param {HtmlEditor} this
21389              */
21390             savedpreview: true
21391         });
21392 };
21393
21394
21395 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21396     
21397     
21398       /**
21399      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21400      */
21401     toolbars : false,
21402    
21403      /**
21404      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21405      *                        Roo.resizable.
21406      */
21407     resizable : false,
21408      /**
21409      * @cfg {Number} height (in pixels)
21410      */   
21411     height: 300,
21412    /**
21413      * @cfg {Number} width (in pixels)
21414      */   
21415     width: false,
21416     
21417     /**
21418      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21419      * 
21420      */
21421     stylesheets: false,
21422     
21423     // id of frame..
21424     frameId: false,
21425     
21426     // private properties
21427     validationEvent : false,
21428     deferHeight: true,
21429     initialized : false,
21430     activated : false,
21431     
21432     onFocus : Roo.emptyFn,
21433     iframePad:3,
21434     hideMode:'offsets',
21435     
21436     
21437     tbContainer : false,
21438     
21439     toolbarContainer :function() {
21440         return this.wrap.select('.x-html-editor-tb',true).first();
21441     },
21442
21443     /**
21444      * Protected method that will not generally be called directly. It
21445      * is called when the editor creates its toolbar. Override this method if you need to
21446      * add custom toolbar buttons.
21447      * @param {HtmlEditor} editor
21448      */
21449     createToolbar : function(){
21450         
21451         Roo.log("create toolbars");
21452         
21453         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21454         this.toolbars[0].render(this.toolbarContainer());
21455         
21456         return;
21457         
21458 //        if (!editor.toolbars || !editor.toolbars.length) {
21459 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21460 //        }
21461 //        
21462 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21463 //            editor.toolbars[i] = Roo.factory(
21464 //                    typeof(editor.toolbars[i]) == 'string' ?
21465 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21466 //                Roo.bootstrap.HtmlEditor);
21467 //            editor.toolbars[i].init(editor);
21468 //        }
21469     },
21470
21471      
21472     // private
21473     onRender : function(ct, position)
21474     {
21475        // Roo.log("Call onRender: " + this.xtype);
21476         var _t = this;
21477         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21478       
21479         this.wrap = this.inputEl().wrap({
21480             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21481         });
21482         
21483         this.editorcore.onRender(ct, position);
21484          
21485         if (this.resizable) {
21486             this.resizeEl = new Roo.Resizable(this.wrap, {
21487                 pinned : true,
21488                 wrap: true,
21489                 dynamic : true,
21490                 minHeight : this.height,
21491                 height: this.height,
21492                 handles : this.resizable,
21493                 width: this.width,
21494                 listeners : {
21495                     resize : function(r, w, h) {
21496                         _t.onResize(w,h); // -something
21497                     }
21498                 }
21499             });
21500             
21501         }
21502         this.createToolbar(this);
21503        
21504         
21505         if(!this.width && this.resizable){
21506             this.setSize(this.wrap.getSize());
21507         }
21508         if (this.resizeEl) {
21509             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21510             // should trigger onReize..
21511         }
21512         
21513     },
21514
21515     // private
21516     onResize : function(w, h)
21517     {
21518         Roo.log('resize: ' +w + ',' + h );
21519         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21520         var ew = false;
21521         var eh = false;
21522         
21523         if(this.inputEl() ){
21524             if(typeof w == 'number'){
21525                 var aw = w - this.wrap.getFrameWidth('lr');
21526                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21527                 ew = aw;
21528             }
21529             if(typeof h == 'number'){
21530                  var tbh = -11;  // fixme it needs to tool bar size!
21531                 for (var i =0; i < this.toolbars.length;i++) {
21532                     // fixme - ask toolbars for heights?
21533                     tbh += this.toolbars[i].el.getHeight();
21534                     //if (this.toolbars[i].footer) {
21535                     //    tbh += this.toolbars[i].footer.el.getHeight();
21536                     //}
21537                 }
21538               
21539                 
21540                 
21541                 
21542                 
21543                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21544                 ah -= 5; // knock a few pixes off for look..
21545                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21546                 var eh = ah;
21547             }
21548         }
21549         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21550         this.editorcore.onResize(ew,eh);
21551         
21552     },
21553
21554     /**
21555      * Toggles the editor between standard and source edit mode.
21556      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21557      */
21558     toggleSourceEdit : function(sourceEditMode)
21559     {
21560         this.editorcore.toggleSourceEdit(sourceEditMode);
21561         
21562         if(this.editorcore.sourceEditMode){
21563             Roo.log('editor - showing textarea');
21564             
21565 //            Roo.log('in');
21566 //            Roo.log(this.syncValue());
21567             this.syncValue();
21568             this.inputEl().removeClass(['hide', 'x-hidden']);
21569             this.inputEl().dom.removeAttribute('tabIndex');
21570             this.inputEl().focus();
21571         }else{
21572             Roo.log('editor - hiding textarea');
21573 //            Roo.log('out')
21574 //            Roo.log(this.pushValue()); 
21575             this.pushValue();
21576             
21577             this.inputEl().addClass(['hide', 'x-hidden']);
21578             this.inputEl().dom.setAttribute('tabIndex', -1);
21579             //this.deferFocus();
21580         }
21581          
21582         if(this.resizable){
21583             this.setSize(this.wrap.getSize());
21584         }
21585         
21586         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21587     },
21588  
21589     // private (for BoxComponent)
21590     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21591
21592     // private (for BoxComponent)
21593     getResizeEl : function(){
21594         return this.wrap;
21595     },
21596
21597     // private (for BoxComponent)
21598     getPositionEl : function(){
21599         return this.wrap;
21600     },
21601
21602     // private
21603     initEvents : function(){
21604         this.originalValue = this.getValue();
21605     },
21606
21607 //    /**
21608 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21609 //     * @method
21610 //     */
21611 //    markInvalid : Roo.emptyFn,
21612 //    /**
21613 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21614 //     * @method
21615 //     */
21616 //    clearInvalid : Roo.emptyFn,
21617
21618     setValue : function(v){
21619         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21620         this.editorcore.pushValue();
21621     },
21622
21623      
21624     // private
21625     deferFocus : function(){
21626         this.focus.defer(10, this);
21627     },
21628
21629     // doc'ed in Field
21630     focus : function(){
21631         this.editorcore.focus();
21632         
21633     },
21634       
21635
21636     // private
21637     onDestroy : function(){
21638         
21639         
21640         
21641         if(this.rendered){
21642             
21643             for (var i =0; i < this.toolbars.length;i++) {
21644                 // fixme - ask toolbars for heights?
21645                 this.toolbars[i].onDestroy();
21646             }
21647             
21648             this.wrap.dom.innerHTML = '';
21649             this.wrap.remove();
21650         }
21651     },
21652
21653     // private
21654     onFirstFocus : function(){
21655         //Roo.log("onFirstFocus");
21656         this.editorcore.onFirstFocus();
21657          for (var i =0; i < this.toolbars.length;i++) {
21658             this.toolbars[i].onFirstFocus();
21659         }
21660         
21661     },
21662     
21663     // private
21664     syncValue : function()
21665     {   
21666         this.editorcore.syncValue();
21667     },
21668     
21669     pushValue : function()
21670     {   
21671         this.editorcore.pushValue();
21672     }
21673      
21674     
21675     // hide stuff that is not compatible
21676     /**
21677      * @event blur
21678      * @hide
21679      */
21680     /**
21681      * @event change
21682      * @hide
21683      */
21684     /**
21685      * @event focus
21686      * @hide
21687      */
21688     /**
21689      * @event specialkey
21690      * @hide
21691      */
21692     /**
21693      * @cfg {String} fieldClass @hide
21694      */
21695     /**
21696      * @cfg {String} focusClass @hide
21697      */
21698     /**
21699      * @cfg {String} autoCreate @hide
21700      */
21701     /**
21702      * @cfg {String} inputType @hide
21703      */
21704     /**
21705      * @cfg {String} invalidClass @hide
21706      */
21707     /**
21708      * @cfg {String} invalidText @hide
21709      */
21710     /**
21711      * @cfg {String} msgFx @hide
21712      */
21713     /**
21714      * @cfg {String} validateOnBlur @hide
21715      */
21716 });
21717  
21718     
21719    
21720    
21721    
21722       
21723 Roo.namespace('Roo.bootstrap.htmleditor');
21724 /**
21725  * @class Roo.bootstrap.HtmlEditorToolbar1
21726  * Basic Toolbar
21727  * 
21728  * Usage:
21729  *
21730  new Roo.bootstrap.HtmlEditor({
21731     ....
21732     toolbars : [
21733         new Roo.bootstrap.HtmlEditorToolbar1({
21734             disable : { fonts: 1 , format: 1, ..., ... , ...],
21735             btns : [ .... ]
21736         })
21737     }
21738      
21739  * 
21740  * @cfg {Object} disable List of elements to disable..
21741  * @cfg {Array} btns List of additional buttons.
21742  * 
21743  * 
21744  * NEEDS Extra CSS? 
21745  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21746  */
21747  
21748 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21749 {
21750     
21751     Roo.apply(this, config);
21752     
21753     // default disabled, based on 'good practice'..
21754     this.disable = this.disable || {};
21755     Roo.applyIf(this.disable, {
21756         fontSize : true,
21757         colors : true,
21758         specialElements : true
21759     });
21760     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21761     
21762     this.editor = config.editor;
21763     this.editorcore = config.editor.editorcore;
21764     
21765     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21766     
21767     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21768     // dont call parent... till later.
21769 }
21770 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21771      
21772     bar : true,
21773     
21774     editor : false,
21775     editorcore : false,
21776     
21777     
21778     formats : [
21779         "p" ,  
21780         "h1","h2","h3","h4","h5","h6", 
21781         "pre", "code", 
21782         "abbr", "acronym", "address", "cite", "samp", "var",
21783         'div','span'
21784     ],
21785     
21786     onRender : function(ct, position)
21787     {
21788        // Roo.log("Call onRender: " + this.xtype);
21789         
21790        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21791        Roo.log(this.el);
21792        this.el.dom.style.marginBottom = '0';
21793        var _this = this;
21794        var editorcore = this.editorcore;
21795        var editor= this.editor;
21796        
21797        var children = [];
21798        var btn = function(id,cmd , toggle, handler){
21799        
21800             var  event = toggle ? 'toggle' : 'click';
21801        
21802             var a = {
21803                 size : 'sm',
21804                 xtype: 'Button',
21805                 xns: Roo.bootstrap,
21806                 glyphicon : id,
21807                 cmd : id || cmd,
21808                 enableToggle:toggle !== false,
21809                 //html : 'submit'
21810                 pressed : toggle ? false : null,
21811                 listeners : {}
21812             };
21813             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21814                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21815             };
21816             children.push(a);
21817             return a;
21818        }
21819         
21820         var style = {
21821                 xtype: 'Button',
21822                 size : 'sm',
21823                 xns: Roo.bootstrap,
21824                 glyphicon : 'font',
21825                 //html : 'submit'
21826                 menu : {
21827                     xtype: 'Menu',
21828                     xns: Roo.bootstrap,
21829                     items:  []
21830                 }
21831         };
21832         Roo.each(this.formats, function(f) {
21833             style.menu.items.push({
21834                 xtype :'MenuItem',
21835                 xns: Roo.bootstrap,
21836                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21837                 tagname : f,
21838                 listeners : {
21839                     click : function()
21840                     {
21841                         editorcore.insertTag(this.tagname);
21842                         editor.focus();
21843                     }
21844                 }
21845                 
21846             });
21847         });
21848          children.push(style);   
21849             
21850             
21851         btn('bold',false,true);
21852         btn('italic',false,true);
21853         btn('align-left', 'justifyleft',true);
21854         btn('align-center', 'justifycenter',true);
21855         btn('align-right' , 'justifyright',true);
21856         btn('link', false, false, function(btn) {
21857             //Roo.log("create link?");
21858             var url = prompt(this.createLinkText, this.defaultLinkValue);
21859             if(url && url != 'http:/'+'/'){
21860                 this.editorcore.relayCmd('createlink', url);
21861             }
21862         }),
21863         btn('list','insertunorderedlist',true);
21864         btn('pencil', false,true, function(btn){
21865                 Roo.log(this);
21866                 
21867                 this.toggleSourceEdit(btn.pressed);
21868         });
21869         /*
21870         var cog = {
21871                 xtype: 'Button',
21872                 size : 'sm',
21873                 xns: Roo.bootstrap,
21874                 glyphicon : 'cog',
21875                 //html : 'submit'
21876                 menu : {
21877                     xtype: 'Menu',
21878                     xns: Roo.bootstrap,
21879                     items:  []
21880                 }
21881         };
21882         
21883         cog.menu.items.push({
21884             xtype :'MenuItem',
21885             xns: Roo.bootstrap,
21886             html : Clean styles,
21887             tagname : f,
21888             listeners : {
21889                 click : function()
21890                 {
21891                     editorcore.insertTag(this.tagname);
21892                     editor.focus();
21893                 }
21894             }
21895             
21896         });
21897        */
21898         
21899          
21900        this.xtype = 'NavSimplebar';
21901         
21902         for(var i=0;i< children.length;i++) {
21903             
21904             this.buttons.add(this.addxtypeChild(children[i]));
21905             
21906         }
21907         
21908         editor.on('editorevent', this.updateToolbar, this);
21909     },
21910     onBtnClick : function(id)
21911     {
21912        this.editorcore.relayCmd(id);
21913        this.editorcore.focus();
21914     },
21915     
21916     /**
21917      * Protected method that will not generally be called directly. It triggers
21918      * a toolbar update by reading the markup state of the current selection in the editor.
21919      */
21920     updateToolbar: function(){
21921
21922         if(!this.editorcore.activated){
21923             this.editor.onFirstFocus(); // is this neeed?
21924             return;
21925         }
21926
21927         var btns = this.buttons; 
21928         var doc = this.editorcore.doc;
21929         btns.get('bold').setActive(doc.queryCommandState('bold'));
21930         btns.get('italic').setActive(doc.queryCommandState('italic'));
21931         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21932         
21933         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21934         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21935         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21936         
21937         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21938         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21939          /*
21940         
21941         var ans = this.editorcore.getAllAncestors();
21942         if (this.formatCombo) {
21943             
21944             
21945             var store = this.formatCombo.store;
21946             this.formatCombo.setValue("");
21947             for (var i =0; i < ans.length;i++) {
21948                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21949                     // select it..
21950                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21951                     break;
21952                 }
21953             }
21954         }
21955         
21956         
21957         
21958         // hides menus... - so this cant be on a menu...
21959         Roo.bootstrap.MenuMgr.hideAll();
21960         */
21961         Roo.bootstrap.MenuMgr.hideAll();
21962         //this.editorsyncValue();
21963     },
21964     onFirstFocus: function() {
21965         this.buttons.each(function(item){
21966            item.enable();
21967         });
21968     },
21969     toggleSourceEdit : function(sourceEditMode){
21970         
21971           
21972         if(sourceEditMode){
21973             Roo.log("disabling buttons");
21974            this.buttons.each( function(item){
21975                 if(item.cmd != 'pencil'){
21976                     item.disable();
21977                 }
21978             });
21979           
21980         }else{
21981             Roo.log("enabling buttons");
21982             if(this.editorcore.initialized){
21983                 this.buttons.each( function(item){
21984                     item.enable();
21985                 });
21986             }
21987             
21988         }
21989         Roo.log("calling toggole on editor");
21990         // tell the editor that it's been pressed..
21991         this.editor.toggleSourceEdit(sourceEditMode);
21992        
21993     }
21994 });
21995
21996
21997
21998
21999
22000 /**
22001  * @class Roo.bootstrap.Table.AbstractSelectionModel
22002  * @extends Roo.util.Observable
22003  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22004  * implemented by descendant classes.  This class should not be directly instantiated.
22005  * @constructor
22006  */
22007 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22008     this.locked = false;
22009     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22010 };
22011
22012
22013 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22014     /** @ignore Called by the grid automatically. Do not call directly. */
22015     init : function(grid){
22016         this.grid = grid;
22017         this.initEvents();
22018     },
22019
22020     /**
22021      * Locks the selections.
22022      */
22023     lock : function(){
22024         this.locked = true;
22025     },
22026
22027     /**
22028      * Unlocks the selections.
22029      */
22030     unlock : function(){
22031         this.locked = false;
22032     },
22033
22034     /**
22035      * Returns true if the selections are locked.
22036      * @return {Boolean}
22037      */
22038     isLocked : function(){
22039         return this.locked;
22040     }
22041 });
22042 /**
22043  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22044  * @class Roo.bootstrap.Table.RowSelectionModel
22045  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22046  * It supports multiple selections and keyboard selection/navigation. 
22047  * @constructor
22048  * @param {Object} config
22049  */
22050
22051 Roo.bootstrap.Table.RowSelectionModel = function(config){
22052     Roo.apply(this, config);
22053     this.selections = new Roo.util.MixedCollection(false, function(o){
22054         return o.id;
22055     });
22056
22057     this.last = false;
22058     this.lastActive = false;
22059
22060     this.addEvents({
22061         /**
22062              * @event selectionchange
22063              * Fires when the selection changes
22064              * @param {SelectionModel} this
22065              */
22066             "selectionchange" : true,
22067         /**
22068              * @event afterselectionchange
22069              * Fires after the selection changes (eg. by key press or clicking)
22070              * @param {SelectionModel} this
22071              */
22072             "afterselectionchange" : true,
22073         /**
22074              * @event beforerowselect
22075              * Fires when a row is selected being selected, return false to cancel.
22076              * @param {SelectionModel} this
22077              * @param {Number} rowIndex The selected index
22078              * @param {Boolean} keepExisting False if other selections will be cleared
22079              */
22080             "beforerowselect" : true,
22081         /**
22082              * @event rowselect
22083              * Fires when a row is selected.
22084              * @param {SelectionModel} this
22085              * @param {Number} rowIndex The selected index
22086              * @param {Roo.data.Record} r The record
22087              */
22088             "rowselect" : true,
22089         /**
22090              * @event rowdeselect
22091              * Fires when a row is deselected.
22092              * @param {SelectionModel} this
22093              * @param {Number} rowIndex The selected index
22094              */
22095         "rowdeselect" : true
22096     });
22097     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22098     this.locked = false;
22099 };
22100
22101 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22102     /**
22103      * @cfg {Boolean} singleSelect
22104      * True to allow selection of only one row at a time (defaults to false)
22105      */
22106     singleSelect : false,
22107
22108     // private
22109     initEvents : function(){
22110
22111         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22112             this.grid.on("mousedown", this.handleMouseDown, this);
22113         }else{ // allow click to work like normal
22114             this.grid.on("rowclick", this.handleDragableRowClick, this);
22115         }
22116
22117         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22118             "up" : function(e){
22119                 if(!e.shiftKey){
22120                     this.selectPrevious(e.shiftKey);
22121                 }else if(this.last !== false && this.lastActive !== false){
22122                     var last = this.last;
22123                     this.selectRange(this.last,  this.lastActive-1);
22124                     this.grid.getView().focusRow(this.lastActive);
22125                     if(last !== false){
22126                         this.last = last;
22127                     }
22128                 }else{
22129                     this.selectFirstRow();
22130                 }
22131                 this.fireEvent("afterselectionchange", this);
22132             },
22133             "down" : function(e){
22134                 if(!e.shiftKey){
22135                     this.selectNext(e.shiftKey);
22136                 }else if(this.last !== false && this.lastActive !== false){
22137                     var last = this.last;
22138                     this.selectRange(this.last,  this.lastActive+1);
22139                     this.grid.getView().focusRow(this.lastActive);
22140                     if(last !== false){
22141                         this.last = last;
22142                     }
22143                 }else{
22144                     this.selectFirstRow();
22145                 }
22146                 this.fireEvent("afterselectionchange", this);
22147             },
22148             scope: this
22149         });
22150
22151         var view = this.grid.view;
22152         view.on("refresh", this.onRefresh, this);
22153         view.on("rowupdated", this.onRowUpdated, this);
22154         view.on("rowremoved", this.onRemove, this);
22155     },
22156
22157     // private
22158     onRefresh : function(){
22159         var ds = this.grid.dataSource, i, v = this.grid.view;
22160         var s = this.selections;
22161         s.each(function(r){
22162             if((i = ds.indexOfId(r.id)) != -1){
22163                 v.onRowSelect(i);
22164             }else{
22165                 s.remove(r);
22166             }
22167         });
22168     },
22169
22170     // private
22171     onRemove : function(v, index, r){
22172         this.selections.remove(r);
22173     },
22174
22175     // private
22176     onRowUpdated : function(v, index, r){
22177         if(this.isSelected(r)){
22178             v.onRowSelect(index);
22179         }
22180     },
22181
22182     /**
22183      * Select records.
22184      * @param {Array} records The records to select
22185      * @param {Boolean} keepExisting (optional) True to keep existing selections
22186      */
22187     selectRecords : function(records, keepExisting){
22188         if(!keepExisting){
22189             this.clearSelections();
22190         }
22191         var ds = this.grid.dataSource;
22192         for(var i = 0, len = records.length; i < len; i++){
22193             this.selectRow(ds.indexOf(records[i]), true);
22194         }
22195     },
22196
22197     /**
22198      * Gets the number of selected rows.
22199      * @return {Number}
22200      */
22201     getCount : function(){
22202         return this.selections.length;
22203     },
22204
22205     /**
22206      * Selects the first row in the grid.
22207      */
22208     selectFirstRow : function(){
22209         this.selectRow(0);
22210     },
22211
22212     /**
22213      * Select the last row.
22214      * @param {Boolean} keepExisting (optional) True to keep existing selections
22215      */
22216     selectLastRow : function(keepExisting){
22217         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22218     },
22219
22220     /**
22221      * Selects the row immediately following the last selected row.
22222      * @param {Boolean} keepExisting (optional) True to keep existing selections
22223      */
22224     selectNext : function(keepExisting){
22225         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22226             this.selectRow(this.last+1, keepExisting);
22227             this.grid.getView().focusRow(this.last);
22228         }
22229     },
22230
22231     /**
22232      * Selects the row that precedes the last selected row.
22233      * @param {Boolean} keepExisting (optional) True to keep existing selections
22234      */
22235     selectPrevious : function(keepExisting){
22236         if(this.last){
22237             this.selectRow(this.last-1, keepExisting);
22238             this.grid.getView().focusRow(this.last);
22239         }
22240     },
22241
22242     /**
22243      * Returns the selected records
22244      * @return {Array} Array of selected records
22245      */
22246     getSelections : function(){
22247         return [].concat(this.selections.items);
22248     },
22249
22250     /**
22251      * Returns the first selected record.
22252      * @return {Record}
22253      */
22254     getSelected : function(){
22255         return this.selections.itemAt(0);
22256     },
22257
22258
22259     /**
22260      * Clears all selections.
22261      */
22262     clearSelections : function(fast){
22263         if(this.locked) {
22264             return;
22265         }
22266         if(fast !== true){
22267             var ds = this.grid.dataSource;
22268             var s = this.selections;
22269             s.each(function(r){
22270                 this.deselectRow(ds.indexOfId(r.id));
22271             }, this);
22272             s.clear();
22273         }else{
22274             this.selections.clear();
22275         }
22276         this.last = false;
22277     },
22278
22279
22280     /**
22281      * Selects all rows.
22282      */
22283     selectAll : function(){
22284         if(this.locked) {
22285             return;
22286         }
22287         this.selections.clear();
22288         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22289             this.selectRow(i, true);
22290         }
22291     },
22292
22293     /**
22294      * Returns True if there is a selection.
22295      * @return {Boolean}
22296      */
22297     hasSelection : function(){
22298         return this.selections.length > 0;
22299     },
22300
22301     /**
22302      * Returns True if the specified row is selected.
22303      * @param {Number/Record} record The record or index of the record to check
22304      * @return {Boolean}
22305      */
22306     isSelected : function(index){
22307         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22308         return (r && this.selections.key(r.id) ? true : false);
22309     },
22310
22311     /**
22312      * Returns True if the specified record id is selected.
22313      * @param {String} id The id of record to check
22314      * @return {Boolean}
22315      */
22316     isIdSelected : function(id){
22317         return (this.selections.key(id) ? true : false);
22318     },
22319
22320     // private
22321     handleMouseDown : function(e, t){
22322         var view = this.grid.getView(), rowIndex;
22323         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22324             return;
22325         };
22326         if(e.shiftKey && this.last !== false){
22327             var last = this.last;
22328             this.selectRange(last, rowIndex, e.ctrlKey);
22329             this.last = last; // reset the last
22330             view.focusRow(rowIndex);
22331         }else{
22332             var isSelected = this.isSelected(rowIndex);
22333             if(e.button !== 0 && isSelected){
22334                 view.focusRow(rowIndex);
22335             }else if(e.ctrlKey && isSelected){
22336                 this.deselectRow(rowIndex);
22337             }else if(!isSelected){
22338                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22339                 view.focusRow(rowIndex);
22340             }
22341         }
22342         this.fireEvent("afterselectionchange", this);
22343     },
22344     // private
22345     handleDragableRowClick :  function(grid, rowIndex, e) 
22346     {
22347         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22348             this.selectRow(rowIndex, false);
22349             grid.view.focusRow(rowIndex);
22350              this.fireEvent("afterselectionchange", this);
22351         }
22352     },
22353     
22354     /**
22355      * Selects multiple rows.
22356      * @param {Array} rows Array of the indexes of the row to select
22357      * @param {Boolean} keepExisting (optional) True to keep existing selections
22358      */
22359     selectRows : function(rows, keepExisting){
22360         if(!keepExisting){
22361             this.clearSelections();
22362         }
22363         for(var i = 0, len = rows.length; i < len; i++){
22364             this.selectRow(rows[i], true);
22365         }
22366     },
22367
22368     /**
22369      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22370      * @param {Number} startRow The index of the first row in the range
22371      * @param {Number} endRow The index of the last row in the range
22372      * @param {Boolean} keepExisting (optional) True to retain existing selections
22373      */
22374     selectRange : function(startRow, endRow, keepExisting){
22375         if(this.locked) {
22376             return;
22377         }
22378         if(!keepExisting){
22379             this.clearSelections();
22380         }
22381         if(startRow <= endRow){
22382             for(var i = startRow; i <= endRow; i++){
22383                 this.selectRow(i, true);
22384             }
22385         }else{
22386             for(var i = startRow; i >= endRow; i--){
22387                 this.selectRow(i, true);
22388             }
22389         }
22390     },
22391
22392     /**
22393      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22394      * @param {Number} startRow The index of the first row in the range
22395      * @param {Number} endRow The index of the last row in the range
22396      */
22397     deselectRange : function(startRow, endRow, preventViewNotify){
22398         if(this.locked) {
22399             return;
22400         }
22401         for(var i = startRow; i <= endRow; i++){
22402             this.deselectRow(i, preventViewNotify);
22403         }
22404     },
22405
22406     /**
22407      * Selects a row.
22408      * @param {Number} row The index of the row to select
22409      * @param {Boolean} keepExisting (optional) True to keep existing selections
22410      */
22411     selectRow : function(index, keepExisting, preventViewNotify){
22412         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22413             return;
22414         }
22415         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22416             if(!keepExisting || this.singleSelect){
22417                 this.clearSelections();
22418             }
22419             var r = this.grid.dataSource.getAt(index);
22420             this.selections.add(r);
22421             this.last = this.lastActive = index;
22422             if(!preventViewNotify){
22423                 this.grid.getView().onRowSelect(index);
22424             }
22425             this.fireEvent("rowselect", this, index, r);
22426             this.fireEvent("selectionchange", this);
22427         }
22428     },
22429
22430     /**
22431      * Deselects a row.
22432      * @param {Number} row The index of the row to deselect
22433      */
22434     deselectRow : function(index, preventViewNotify){
22435         if(this.locked) {
22436             return;
22437         }
22438         if(this.last == index){
22439             this.last = false;
22440         }
22441         if(this.lastActive == index){
22442             this.lastActive = false;
22443         }
22444         var r = this.grid.dataSource.getAt(index);
22445         this.selections.remove(r);
22446         if(!preventViewNotify){
22447             this.grid.getView().onRowDeselect(index);
22448         }
22449         this.fireEvent("rowdeselect", this, index);
22450         this.fireEvent("selectionchange", this);
22451     },
22452
22453     // private
22454     restoreLast : function(){
22455         if(this._last){
22456             this.last = this._last;
22457         }
22458     },
22459
22460     // private
22461     acceptsNav : function(row, col, cm){
22462         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22463     },
22464
22465     // private
22466     onEditorKey : function(field, e){
22467         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22468         if(k == e.TAB){
22469             e.stopEvent();
22470             ed.completeEdit();
22471             if(e.shiftKey){
22472                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22473             }else{
22474                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22475             }
22476         }else if(k == e.ENTER && !e.ctrlKey){
22477             e.stopEvent();
22478             ed.completeEdit();
22479             if(e.shiftKey){
22480                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22481             }else{
22482                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22483             }
22484         }else if(k == e.ESC){
22485             ed.cancelEdit();
22486         }
22487         if(newCell){
22488             g.startEditing(newCell[0], newCell[1]);
22489         }
22490     }
22491 });/*
22492  * Based on:
22493  * Ext JS Library 1.1.1
22494  * Copyright(c) 2006-2007, Ext JS, LLC.
22495  *
22496  * Originally Released Under LGPL - original licence link has changed is not relivant.
22497  *
22498  * Fork - LGPL
22499  * <script type="text/javascript">
22500  */
22501  
22502 /**
22503  * @class Roo.bootstrap.PagingToolbar
22504  * @extends Roo.bootstrap.NavSimplebar
22505  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22506  * @constructor
22507  * Create a new PagingToolbar
22508  * @param {Object} config The config object
22509  * @param {Roo.data.Store} store
22510  */
22511 Roo.bootstrap.PagingToolbar = function(config)
22512 {
22513     // old args format still supported... - xtype is prefered..
22514         // created from xtype...
22515     
22516     this.ds = config.dataSource;
22517     
22518     if (config.store && !this.ds) {
22519         this.store= Roo.factory(config.store, Roo.data);
22520         this.ds = this.store;
22521         this.ds.xmodule = this.xmodule || false;
22522     }
22523     
22524     this.toolbarItems = [];
22525     if (config.items) {
22526         this.toolbarItems = config.items;
22527     }
22528     
22529     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22530     
22531     this.cursor = 0;
22532     
22533     if (this.ds) { 
22534         this.bind(this.ds);
22535     }
22536     
22537     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22538     
22539 };
22540
22541 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22542     /**
22543      * @cfg {Roo.data.Store} dataSource
22544      * The underlying data store providing the paged data
22545      */
22546     /**
22547      * @cfg {String/HTMLElement/Element} container
22548      * container The id or element that will contain the toolbar
22549      */
22550     /**
22551      * @cfg {Boolean} displayInfo
22552      * True to display the displayMsg (defaults to false)
22553      */
22554     /**
22555      * @cfg {Number} pageSize
22556      * The number of records to display per page (defaults to 20)
22557      */
22558     pageSize: 20,
22559     /**
22560      * @cfg {String} displayMsg
22561      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22562      */
22563     displayMsg : 'Displaying {0} - {1} of {2}',
22564     /**
22565      * @cfg {String} emptyMsg
22566      * The message to display when no records are found (defaults to "No data to display")
22567      */
22568     emptyMsg : 'No data to display',
22569     /**
22570      * Customizable piece of the default paging text (defaults to "Page")
22571      * @type String
22572      */
22573     beforePageText : "Page",
22574     /**
22575      * Customizable piece of the default paging text (defaults to "of %0")
22576      * @type String
22577      */
22578     afterPageText : "of {0}",
22579     /**
22580      * Customizable piece of the default paging text (defaults to "First Page")
22581      * @type String
22582      */
22583     firstText : "First Page",
22584     /**
22585      * Customizable piece of the default paging text (defaults to "Previous Page")
22586      * @type String
22587      */
22588     prevText : "Previous Page",
22589     /**
22590      * Customizable piece of the default paging text (defaults to "Next Page")
22591      * @type String
22592      */
22593     nextText : "Next Page",
22594     /**
22595      * Customizable piece of the default paging text (defaults to "Last Page")
22596      * @type String
22597      */
22598     lastText : "Last Page",
22599     /**
22600      * Customizable piece of the default paging text (defaults to "Refresh")
22601      * @type String
22602      */
22603     refreshText : "Refresh",
22604
22605     buttons : false,
22606     // private
22607     onRender : function(ct, position) 
22608     {
22609         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22610         this.navgroup.parentId = this.id;
22611         this.navgroup.onRender(this.el, null);
22612         // add the buttons to the navgroup
22613         
22614         if(this.displayInfo){
22615             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22616             this.displayEl = this.el.select('.x-paging-info', true).first();
22617 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22618 //            this.displayEl = navel.el.select('span',true).first();
22619         }
22620         
22621         var _this = this;
22622         
22623         if(this.buttons){
22624             Roo.each(_this.buttons, function(e){ // this might need to use render????
22625                Roo.factory(e).onRender(_this.el, null);
22626             });
22627         }
22628             
22629         Roo.each(_this.toolbarItems, function(e) {
22630             _this.navgroup.addItem(e);
22631         });
22632         
22633         
22634         this.first = this.navgroup.addItem({
22635             tooltip: this.firstText,
22636             cls: "prev",
22637             icon : 'fa fa-backward',
22638             disabled: true,
22639             preventDefault: true,
22640             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22641         });
22642         
22643         this.prev =  this.navgroup.addItem({
22644             tooltip: this.prevText,
22645             cls: "prev",
22646             icon : 'fa fa-step-backward',
22647             disabled: true,
22648             preventDefault: true,
22649             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22650         });
22651     //this.addSeparator();
22652         
22653         
22654         var field = this.navgroup.addItem( {
22655             tagtype : 'span',
22656             cls : 'x-paging-position',
22657             
22658             html : this.beforePageText  +
22659                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22660                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22661          } ); //?? escaped?
22662         
22663         this.field = field.el.select('input', true).first();
22664         this.field.on("keydown", this.onPagingKeydown, this);
22665         this.field.on("focus", function(){this.dom.select();});
22666     
22667     
22668         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22669         //this.field.setHeight(18);
22670         //this.addSeparator();
22671         this.next = this.navgroup.addItem({
22672             tooltip: this.nextText,
22673             cls: "next",
22674             html : ' <i class="fa fa-step-forward">',
22675             disabled: true,
22676             preventDefault: true,
22677             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22678         });
22679         this.last = this.navgroup.addItem({
22680             tooltip: this.lastText,
22681             icon : 'fa fa-forward',
22682             cls: "next",
22683             disabled: true,
22684             preventDefault: true,
22685             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22686         });
22687     //this.addSeparator();
22688         this.loading = this.navgroup.addItem({
22689             tooltip: this.refreshText,
22690             icon: 'fa fa-refresh',
22691             preventDefault: true,
22692             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22693         });
22694         
22695     },
22696
22697     // private
22698     updateInfo : function(){
22699         if(this.displayEl){
22700             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22701             var msg = count == 0 ?
22702                 this.emptyMsg :
22703                 String.format(
22704                     this.displayMsg,
22705                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22706                 );
22707             this.displayEl.update(msg);
22708         }
22709     },
22710
22711     // private
22712     onLoad : function(ds, r, o){
22713        this.cursor = o.params ? o.params.start : 0;
22714        var d = this.getPageData(),
22715             ap = d.activePage,
22716             ps = d.pages;
22717         
22718        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22719        this.field.dom.value = ap;
22720        this.first.setDisabled(ap == 1);
22721        this.prev.setDisabled(ap == 1);
22722        this.next.setDisabled(ap == ps);
22723        this.last.setDisabled(ap == ps);
22724        this.loading.enable();
22725        this.updateInfo();
22726     },
22727
22728     // private
22729     getPageData : function(){
22730         var total = this.ds.getTotalCount();
22731         return {
22732             total : total,
22733             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22734             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22735         };
22736     },
22737
22738     // private
22739     onLoadError : function(){
22740         this.loading.enable();
22741     },
22742
22743     // private
22744     onPagingKeydown : function(e){
22745         var k = e.getKey();
22746         var d = this.getPageData();
22747         if(k == e.RETURN){
22748             var v = this.field.dom.value, pageNum;
22749             if(!v || isNaN(pageNum = parseInt(v, 10))){
22750                 this.field.dom.value = d.activePage;
22751                 return;
22752             }
22753             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22754             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22755             e.stopEvent();
22756         }
22757         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))
22758         {
22759           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22760           this.field.dom.value = pageNum;
22761           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22762           e.stopEvent();
22763         }
22764         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22765         {
22766           var v = this.field.dom.value, pageNum; 
22767           var increment = (e.shiftKey) ? 10 : 1;
22768           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22769                 increment *= -1;
22770           }
22771           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22772             this.field.dom.value = d.activePage;
22773             return;
22774           }
22775           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22776           {
22777             this.field.dom.value = parseInt(v, 10) + increment;
22778             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22779             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22780           }
22781           e.stopEvent();
22782         }
22783     },
22784
22785     // private
22786     beforeLoad : function(){
22787         if(this.loading){
22788             this.loading.disable();
22789         }
22790     },
22791
22792     // private
22793     onClick : function(which){
22794         
22795         var ds = this.ds;
22796         if (!ds) {
22797             return;
22798         }
22799         
22800         switch(which){
22801             case "first":
22802                 ds.load({params:{start: 0, limit: this.pageSize}});
22803             break;
22804             case "prev":
22805                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22806             break;
22807             case "next":
22808                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22809             break;
22810             case "last":
22811                 var total = ds.getTotalCount();
22812                 var extra = total % this.pageSize;
22813                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22814                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22815             break;
22816             case "refresh":
22817                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22818             break;
22819         }
22820     },
22821
22822     /**
22823      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22824      * @param {Roo.data.Store} store The data store to unbind
22825      */
22826     unbind : function(ds){
22827         ds.un("beforeload", this.beforeLoad, this);
22828         ds.un("load", this.onLoad, this);
22829         ds.un("loadexception", this.onLoadError, this);
22830         ds.un("remove", this.updateInfo, this);
22831         ds.un("add", this.updateInfo, this);
22832         this.ds = undefined;
22833     },
22834
22835     /**
22836      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22837      * @param {Roo.data.Store} store The data store to bind
22838      */
22839     bind : function(ds){
22840         ds.on("beforeload", this.beforeLoad, this);
22841         ds.on("load", this.onLoad, this);
22842         ds.on("loadexception", this.onLoadError, this);
22843         ds.on("remove", this.updateInfo, this);
22844         ds.on("add", this.updateInfo, this);
22845         this.ds = ds;
22846     }
22847 });/*
22848  * - LGPL
22849  *
22850  * element
22851  * 
22852  */
22853
22854 /**
22855  * @class Roo.bootstrap.MessageBar
22856  * @extends Roo.bootstrap.Component
22857  * Bootstrap MessageBar class
22858  * @cfg {String} html contents of the MessageBar
22859  * @cfg {String} weight (info | success | warning | danger) default info
22860  * @cfg {String} beforeClass insert the bar before the given class
22861  * @cfg {Boolean} closable (true | false) default false
22862  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22863  * 
22864  * @constructor
22865  * Create a new Element
22866  * @param {Object} config The config object
22867  */
22868
22869 Roo.bootstrap.MessageBar = function(config){
22870     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22871 };
22872
22873 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22874     
22875     html: '',
22876     weight: 'info',
22877     closable: false,
22878     fixed: false,
22879     beforeClass: 'bootstrap-sticky-wrap',
22880     
22881     getAutoCreate : function(){
22882         
22883         var cfg = {
22884             tag: 'div',
22885             cls: 'alert alert-dismissable alert-' + this.weight,
22886             cn: [
22887                 {
22888                     tag: 'span',
22889                     cls: 'message',
22890                     html: this.html || ''
22891                 }
22892             ]
22893         };
22894         
22895         if(this.fixed){
22896             cfg.cls += ' alert-messages-fixed';
22897         }
22898         
22899         if(this.closable){
22900             cfg.cn.push({
22901                 tag: 'button',
22902                 cls: 'close',
22903                 html: 'x'
22904             });
22905         }
22906         
22907         return cfg;
22908     },
22909     
22910     onRender : function(ct, position)
22911     {
22912         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22913         
22914         if(!this.el){
22915             var cfg = Roo.apply({},  this.getAutoCreate());
22916             cfg.id = Roo.id();
22917             
22918             if (this.cls) {
22919                 cfg.cls += ' ' + this.cls;
22920             }
22921             if (this.style) {
22922                 cfg.style = this.style;
22923             }
22924             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22925             
22926             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22927         }
22928         
22929         this.el.select('>button.close').on('click', this.hide, this);
22930         
22931     },
22932     
22933     show : function()
22934     {
22935         if (!this.rendered) {
22936             this.render();
22937         }
22938         
22939         this.el.show();
22940         
22941         this.fireEvent('show', this);
22942         
22943     },
22944     
22945     hide : function()
22946     {
22947         if (!this.rendered) {
22948             this.render();
22949         }
22950         
22951         this.el.hide();
22952         
22953         this.fireEvent('hide', this);
22954     },
22955     
22956     update : function()
22957     {
22958 //        var e = this.el.dom.firstChild;
22959 //        
22960 //        if(this.closable){
22961 //            e = e.nextSibling;
22962 //        }
22963 //        
22964 //        e.data = this.html || '';
22965
22966         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22967     }
22968    
22969 });
22970
22971  
22972
22973      /*
22974  * - LGPL
22975  *
22976  * Graph
22977  * 
22978  */
22979
22980
22981 /**
22982  * @class Roo.bootstrap.Graph
22983  * @extends Roo.bootstrap.Component
22984  * Bootstrap Graph class
22985 > Prameters
22986  -sm {number} sm 4
22987  -md {number} md 5
22988  @cfg {String} graphtype  bar | vbar | pie
22989  @cfg {number} g_x coodinator | centre x (pie)
22990  @cfg {number} g_y coodinator | centre y (pie)
22991  @cfg {number} g_r radius (pie)
22992  @cfg {number} g_height height of the chart (respected by all elements in the set)
22993  @cfg {number} g_width width of the chart (respected by all elements in the set)
22994  @cfg {Object} title The title of the chart
22995     
22996  -{Array}  values
22997  -opts (object) options for the chart 
22998      o {
22999      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23000      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23001      o vgutter (number)
23002      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.
23003      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23004      o to
23005      o stretch (boolean)
23006      o }
23007  -opts (object) options for the pie
23008      o{
23009      o cut
23010      o startAngle (number)
23011      o endAngle (number)
23012      } 
23013  *
23014  * @constructor
23015  * Create a new Input
23016  * @param {Object} config The config object
23017  */
23018
23019 Roo.bootstrap.Graph = function(config){
23020     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23021     
23022     this.addEvents({
23023         // img events
23024         /**
23025          * @event click
23026          * The img click event for the img.
23027          * @param {Roo.EventObject} e
23028          */
23029         "click" : true
23030     });
23031 };
23032
23033 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23034     
23035     sm: 4,
23036     md: 5,
23037     graphtype: 'bar',
23038     g_height: 250,
23039     g_width: 400,
23040     g_x: 50,
23041     g_y: 50,
23042     g_r: 30,
23043     opts:{
23044         //g_colors: this.colors,
23045         g_type: 'soft',
23046         g_gutter: '20%'
23047
23048     },
23049     title : false,
23050
23051     getAutoCreate : function(){
23052         
23053         var cfg = {
23054             tag: 'div',
23055             html : null
23056         };
23057         
23058         
23059         return  cfg;
23060     },
23061
23062     onRender : function(ct,position){
23063         
23064         
23065         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23066         
23067         if (typeof(Raphael) == 'undefined') {
23068             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23069             return;
23070         }
23071         
23072         this.raphael = Raphael(this.el.dom);
23073         
23074                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23075                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23076                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23077                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23078                 /*
23079                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23080                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23081                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23082                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23083                 
23084                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23085                 r.barchart(330, 10, 300, 220, data1);
23086                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23087                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23088                 */
23089                 
23090                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23091                 // r.barchart(30, 30, 560, 250,  xdata, {
23092                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23093                 //     axis : "0 0 1 1",
23094                 //     axisxlabels :  xdata
23095                 //     //yvalues : cols,
23096                    
23097                 // });
23098 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23099 //        
23100 //        this.load(null,xdata,{
23101 //                axis : "0 0 1 1",
23102 //                axisxlabels :  xdata
23103 //                });
23104
23105     },
23106
23107     load : function(graphtype,xdata,opts)
23108     {
23109         this.raphael.clear();
23110         if(!graphtype) {
23111             graphtype = this.graphtype;
23112         }
23113         if(!opts){
23114             opts = this.opts;
23115         }
23116         var r = this.raphael,
23117             fin = function () {
23118                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23119             },
23120             fout = function () {
23121                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23122             },
23123             pfin = function() {
23124                 this.sector.stop();
23125                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23126
23127                 if (this.label) {
23128                     this.label[0].stop();
23129                     this.label[0].attr({ r: 7.5 });
23130                     this.label[1].attr({ "font-weight": 800 });
23131                 }
23132             },
23133             pfout = function() {
23134                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23135
23136                 if (this.label) {
23137                     this.label[0].animate({ r: 5 }, 500, "bounce");
23138                     this.label[1].attr({ "font-weight": 400 });
23139                 }
23140             };
23141
23142         switch(graphtype){
23143             case 'bar':
23144                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23145                 break;
23146             case 'hbar':
23147                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23148                 break;
23149             case 'pie':
23150 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23151 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23152 //            
23153                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23154                 
23155                 break;
23156
23157         }
23158         
23159         if(this.title){
23160             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23161         }
23162         
23163     },
23164     
23165     setTitle: function(o)
23166     {
23167         this.title = o;
23168     },
23169     
23170     initEvents: function() {
23171         
23172         if(!this.href){
23173             this.el.on('click', this.onClick, this);
23174         }
23175     },
23176     
23177     onClick : function(e)
23178     {
23179         Roo.log('img onclick');
23180         this.fireEvent('click', this, e);
23181     }
23182    
23183 });
23184
23185  
23186 /*
23187  * - LGPL
23188  *
23189  * numberBox
23190  * 
23191  */
23192 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23193
23194 /**
23195  * @class Roo.bootstrap.dash.NumberBox
23196  * @extends Roo.bootstrap.Component
23197  * Bootstrap NumberBox class
23198  * @cfg {String} headline Box headline
23199  * @cfg {String} content Box content
23200  * @cfg {String} icon Box icon
23201  * @cfg {String} footer Footer text
23202  * @cfg {String} fhref Footer href
23203  * 
23204  * @constructor
23205  * Create a new NumberBox
23206  * @param {Object} config The config object
23207  */
23208
23209
23210 Roo.bootstrap.dash.NumberBox = function(config){
23211     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23212     
23213 };
23214
23215 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23216     
23217     headline : '',
23218     content : '',
23219     icon : '',
23220     footer : '',
23221     fhref : '',
23222     ficon : '',
23223     
23224     getAutoCreate : function(){
23225         
23226         var cfg = {
23227             tag : 'div',
23228             cls : 'small-box ',
23229             cn : [
23230                 {
23231                     tag : 'div',
23232                     cls : 'inner',
23233                     cn :[
23234                         {
23235                             tag : 'h3',
23236                             cls : 'roo-headline',
23237                             html : this.headline
23238                         },
23239                         {
23240                             tag : 'p',
23241                             cls : 'roo-content',
23242                             html : this.content
23243                         }
23244                     ]
23245                 }
23246             ]
23247         };
23248         
23249         if(this.icon){
23250             cfg.cn.push({
23251                 tag : 'div',
23252                 cls : 'icon',
23253                 cn :[
23254                     {
23255                         tag : 'i',
23256                         cls : 'ion ' + this.icon
23257                     }
23258                 ]
23259             });
23260         }
23261         
23262         if(this.footer){
23263             var footer = {
23264                 tag : 'a',
23265                 cls : 'small-box-footer',
23266                 href : this.fhref || '#',
23267                 html : this.footer
23268             };
23269             
23270             cfg.cn.push(footer);
23271             
23272         }
23273         
23274         return  cfg;
23275     },
23276
23277     onRender : function(ct,position){
23278         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23279
23280
23281        
23282                 
23283     },
23284
23285     setHeadline: function (value)
23286     {
23287         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23288     },
23289     
23290     setFooter: function (value, href)
23291     {
23292         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23293         
23294         if(href){
23295             this.el.select('a.small-box-footer',true).first().attr('href', href);
23296         }
23297         
23298     },
23299
23300     setContent: function (value)
23301     {
23302         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23303     },
23304
23305     initEvents: function() 
23306     {   
23307         
23308     }
23309     
23310 });
23311
23312  
23313 /*
23314  * - LGPL
23315  *
23316  * TabBox
23317  * 
23318  */
23319 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23320
23321 /**
23322  * @class Roo.bootstrap.dash.TabBox
23323  * @extends Roo.bootstrap.Component
23324  * Bootstrap TabBox class
23325  * @cfg {String} title Title of the TabBox
23326  * @cfg {String} icon Icon of the TabBox
23327  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23328  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23329  * 
23330  * @constructor
23331  * Create a new TabBox
23332  * @param {Object} config The config object
23333  */
23334
23335
23336 Roo.bootstrap.dash.TabBox = function(config){
23337     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23338     this.addEvents({
23339         // raw events
23340         /**
23341          * @event addpane
23342          * When a pane is added
23343          * @param {Roo.bootstrap.dash.TabPane} pane
23344          */
23345         "addpane" : true,
23346         /**
23347          * @event activatepane
23348          * When a pane is activated
23349          * @param {Roo.bootstrap.dash.TabPane} pane
23350          */
23351         "activatepane" : true
23352         
23353          
23354     });
23355     
23356     this.panes = [];
23357 };
23358
23359 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23360
23361     title : '',
23362     icon : false,
23363     showtabs : true,
23364     tabScrollable : false,
23365     
23366     getChildContainer : function()
23367     {
23368         return this.el.select('.tab-content', true).first();
23369     },
23370     
23371     getAutoCreate : function(){
23372         
23373         var header = {
23374             tag: 'li',
23375             cls: 'pull-left header',
23376             html: this.title,
23377             cn : []
23378         };
23379         
23380         if(this.icon){
23381             header.cn.push({
23382                 tag: 'i',
23383                 cls: 'fa ' + this.icon
23384             });
23385         }
23386         
23387         var h = {
23388             tag: 'ul',
23389             cls: 'nav nav-tabs pull-right',
23390             cn: [
23391                 header
23392             ]
23393         };
23394         
23395         if(this.tabScrollable){
23396             h = {
23397                 tag: 'div',
23398                 cls: 'tab-header',
23399                 cn: [
23400                     {
23401                         tag: 'ul',
23402                         cls: 'nav nav-tabs pull-right',
23403                         cn: [
23404                             header
23405                         ]
23406                     }
23407                 ]
23408             };
23409         }
23410         
23411         var cfg = {
23412             tag: 'div',
23413             cls: 'nav-tabs-custom',
23414             cn: [
23415                 h,
23416                 {
23417                     tag: 'div',
23418                     cls: 'tab-content no-padding',
23419                     cn: []
23420                 }
23421             ]
23422         };
23423
23424         return  cfg;
23425     },
23426     initEvents : function()
23427     {
23428         //Roo.log('add add pane handler');
23429         this.on('addpane', this.onAddPane, this);
23430     },
23431      /**
23432      * Updates the box title
23433      * @param {String} html to set the title to.
23434      */
23435     setTitle : function(value)
23436     {
23437         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23438     },
23439     onAddPane : function(pane)
23440     {
23441         this.panes.push(pane);
23442         //Roo.log('addpane');
23443         //Roo.log(pane);
23444         // tabs are rendere left to right..
23445         if(!this.showtabs){
23446             return;
23447         }
23448         
23449         var ctr = this.el.select('.nav-tabs', true).first();
23450          
23451          
23452         var existing = ctr.select('.nav-tab',true);
23453         var qty = existing.getCount();;
23454         
23455         
23456         var tab = ctr.createChild({
23457             tag : 'li',
23458             cls : 'nav-tab' + (qty ? '' : ' active'),
23459             cn : [
23460                 {
23461                     tag : 'a',
23462                     href:'#',
23463                     html : pane.title
23464                 }
23465             ]
23466         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23467         pane.tab = tab;
23468         
23469         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23470         if (!qty) {
23471             pane.el.addClass('active');
23472         }
23473         
23474                 
23475     },
23476     onTabClick : function(ev,un,ob,pane)
23477     {
23478         //Roo.log('tab - prev default');
23479         ev.preventDefault();
23480         
23481         
23482         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23483         pane.tab.addClass('active');
23484         //Roo.log(pane.title);
23485         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23486         // technically we should have a deactivate event.. but maybe add later.
23487         // and it should not de-activate the selected tab...
23488         this.fireEvent('activatepane', pane);
23489         pane.el.addClass('active');
23490         pane.fireEvent('activate');
23491         
23492         
23493     },
23494     
23495     getActivePane : function()
23496     {
23497         var r = false;
23498         Roo.each(this.panes, function(p) {
23499             if(p.el.hasClass('active')){
23500                 r = p;
23501                 return false;
23502             }
23503             
23504             return;
23505         });
23506         
23507         return r;
23508     }
23509     
23510     
23511 });
23512
23513  
23514 /*
23515  * - LGPL
23516  *
23517  * Tab pane
23518  * 
23519  */
23520 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23521 /**
23522  * @class Roo.bootstrap.TabPane
23523  * @extends Roo.bootstrap.Component
23524  * Bootstrap TabPane class
23525  * @cfg {Boolean} active (false | true) Default false
23526  * @cfg {String} title title of panel
23527
23528  * 
23529  * @constructor
23530  * Create a new TabPane
23531  * @param {Object} config The config object
23532  */
23533
23534 Roo.bootstrap.dash.TabPane = function(config){
23535     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23536     
23537     this.addEvents({
23538         // raw events
23539         /**
23540          * @event activate
23541          * When a pane is activated
23542          * @param {Roo.bootstrap.dash.TabPane} pane
23543          */
23544         "activate" : true
23545          
23546     });
23547 };
23548
23549 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23550     
23551     active : false,
23552     title : '',
23553     
23554     // the tabBox that this is attached to.
23555     tab : false,
23556      
23557     getAutoCreate : function() 
23558     {
23559         var cfg = {
23560             tag: 'div',
23561             cls: 'tab-pane'
23562         };
23563         
23564         if(this.active){
23565             cfg.cls += ' active';
23566         }
23567         
23568         return cfg;
23569     },
23570     initEvents  : function()
23571     {
23572         //Roo.log('trigger add pane handler');
23573         this.parent().fireEvent('addpane', this)
23574     },
23575     
23576      /**
23577      * Updates the tab title 
23578      * @param {String} html to set the title to.
23579      */
23580     setTitle: function(str)
23581     {
23582         if (!this.tab) {
23583             return;
23584         }
23585         this.title = str;
23586         this.tab.select('a', true).first().dom.innerHTML = str;
23587         
23588     }
23589     
23590     
23591     
23592 });
23593
23594  
23595
23596
23597  /*
23598  * - LGPL
23599  *
23600  * menu
23601  * 
23602  */
23603 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23604
23605 /**
23606  * @class Roo.bootstrap.menu.Menu
23607  * @extends Roo.bootstrap.Component
23608  * Bootstrap Menu class - container for Menu
23609  * @cfg {String} html Text of the menu
23610  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23611  * @cfg {String} icon Font awesome icon
23612  * @cfg {String} pos Menu align to (top | bottom) default bottom
23613  * 
23614  * 
23615  * @constructor
23616  * Create a new Menu
23617  * @param {Object} config The config object
23618  */
23619
23620
23621 Roo.bootstrap.menu.Menu = function(config){
23622     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23623     
23624     this.addEvents({
23625         /**
23626          * @event beforeshow
23627          * Fires before this menu is displayed
23628          * @param {Roo.bootstrap.menu.Menu} this
23629          */
23630         beforeshow : true,
23631         /**
23632          * @event beforehide
23633          * Fires before this menu is hidden
23634          * @param {Roo.bootstrap.menu.Menu} this
23635          */
23636         beforehide : true,
23637         /**
23638          * @event show
23639          * Fires after this menu is displayed
23640          * @param {Roo.bootstrap.menu.Menu} this
23641          */
23642         show : true,
23643         /**
23644          * @event hide
23645          * Fires after this menu is hidden
23646          * @param {Roo.bootstrap.menu.Menu} this
23647          */
23648         hide : true,
23649         /**
23650          * @event click
23651          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23652          * @param {Roo.bootstrap.menu.Menu} this
23653          * @param {Roo.EventObject} e
23654          */
23655         click : true
23656     });
23657     
23658 };
23659
23660 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23661     
23662     submenu : false,
23663     html : '',
23664     weight : 'default',
23665     icon : false,
23666     pos : 'bottom',
23667     
23668     
23669     getChildContainer : function() {
23670         if(this.isSubMenu){
23671             return this.el;
23672         }
23673         
23674         return this.el.select('ul.dropdown-menu', true).first();  
23675     },
23676     
23677     getAutoCreate : function()
23678     {
23679         var text = [
23680             {
23681                 tag : 'span',
23682                 cls : 'roo-menu-text',
23683                 html : this.html
23684             }
23685         ];
23686         
23687         if(this.icon){
23688             text.unshift({
23689                 tag : 'i',
23690                 cls : 'fa ' + this.icon
23691             })
23692         }
23693         
23694         
23695         var cfg = {
23696             tag : 'div',
23697             cls : 'btn-group',
23698             cn : [
23699                 {
23700                     tag : 'button',
23701                     cls : 'dropdown-button btn btn-' + this.weight,
23702                     cn : text
23703                 },
23704                 {
23705                     tag : 'button',
23706                     cls : 'dropdown-toggle btn btn-' + this.weight,
23707                     cn : [
23708                         {
23709                             tag : 'span',
23710                             cls : 'caret'
23711                         }
23712                     ]
23713                 },
23714                 {
23715                     tag : 'ul',
23716                     cls : 'dropdown-menu'
23717                 }
23718             ]
23719             
23720         };
23721         
23722         if(this.pos == 'top'){
23723             cfg.cls += ' dropup';
23724         }
23725         
23726         if(this.isSubMenu){
23727             cfg = {
23728                 tag : 'ul',
23729                 cls : 'dropdown-menu'
23730             }
23731         }
23732         
23733         return cfg;
23734     },
23735     
23736     onRender : function(ct, position)
23737     {
23738         this.isSubMenu = ct.hasClass('dropdown-submenu');
23739         
23740         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23741     },
23742     
23743     initEvents : function() 
23744     {
23745         if(this.isSubMenu){
23746             return;
23747         }
23748         
23749         this.hidden = true;
23750         
23751         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23752         this.triggerEl.on('click', this.onTriggerPress, this);
23753         
23754         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23755         this.buttonEl.on('click', this.onClick, this);
23756         
23757     },
23758     
23759     list : function()
23760     {
23761         if(this.isSubMenu){
23762             return this.el;
23763         }
23764         
23765         return this.el.select('ul.dropdown-menu', true).first();
23766     },
23767     
23768     onClick : function(e)
23769     {
23770         this.fireEvent("click", this, e);
23771     },
23772     
23773     onTriggerPress  : function(e)
23774     {   
23775         if (this.isVisible()) {
23776             this.hide();
23777         } else {
23778             this.show();
23779         }
23780     },
23781     
23782     isVisible : function(){
23783         return !this.hidden;
23784     },
23785     
23786     show : function()
23787     {
23788         this.fireEvent("beforeshow", this);
23789         
23790         this.hidden = false;
23791         this.el.addClass('open');
23792         
23793         Roo.get(document).on("mouseup", this.onMouseUp, this);
23794         
23795         this.fireEvent("show", this);
23796         
23797         
23798     },
23799     
23800     hide : function()
23801     {
23802         this.fireEvent("beforehide", this);
23803         
23804         this.hidden = true;
23805         this.el.removeClass('open');
23806         
23807         Roo.get(document).un("mouseup", this.onMouseUp);
23808         
23809         this.fireEvent("hide", this);
23810     },
23811     
23812     onMouseUp : function()
23813     {
23814         this.hide();
23815     }
23816     
23817 });
23818
23819  
23820  /*
23821  * - LGPL
23822  *
23823  * menu item
23824  * 
23825  */
23826 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23827
23828 /**
23829  * @class Roo.bootstrap.menu.Item
23830  * @extends Roo.bootstrap.Component
23831  * Bootstrap MenuItem class
23832  * @cfg {Boolean} submenu (true | false) default false
23833  * @cfg {String} html text of the item
23834  * @cfg {String} href the link
23835  * @cfg {Boolean} disable (true | false) default false
23836  * @cfg {Boolean} preventDefault (true | false) default true
23837  * @cfg {String} icon Font awesome icon
23838  * @cfg {String} pos Submenu align to (left | right) default right 
23839  * 
23840  * 
23841  * @constructor
23842  * Create a new Item
23843  * @param {Object} config The config object
23844  */
23845
23846
23847 Roo.bootstrap.menu.Item = function(config){
23848     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23849     this.addEvents({
23850         /**
23851          * @event mouseover
23852          * Fires when the mouse is hovering over this menu
23853          * @param {Roo.bootstrap.menu.Item} this
23854          * @param {Roo.EventObject} e
23855          */
23856         mouseover : true,
23857         /**
23858          * @event mouseout
23859          * Fires when the mouse exits this menu
23860          * @param {Roo.bootstrap.menu.Item} this
23861          * @param {Roo.EventObject} e
23862          */
23863         mouseout : true,
23864         // raw events
23865         /**
23866          * @event click
23867          * The raw click event for the entire grid.
23868          * @param {Roo.EventObject} e
23869          */
23870         click : true
23871     });
23872 };
23873
23874 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23875     
23876     submenu : false,
23877     href : '',
23878     html : '',
23879     preventDefault: true,
23880     disable : false,
23881     icon : false,
23882     pos : 'right',
23883     
23884     getAutoCreate : function()
23885     {
23886         var text = [
23887             {
23888                 tag : 'span',
23889                 cls : 'roo-menu-item-text',
23890                 html : this.html
23891             }
23892         ];
23893         
23894         if(this.icon){
23895             text.unshift({
23896                 tag : 'i',
23897                 cls : 'fa ' + this.icon
23898             })
23899         }
23900         
23901         var cfg = {
23902             tag : 'li',
23903             cn : [
23904                 {
23905                     tag : 'a',
23906                     href : this.href || '#',
23907                     cn : text
23908                 }
23909             ]
23910         };
23911         
23912         if(this.disable){
23913             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23914         }
23915         
23916         if(this.submenu){
23917             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23918             
23919             if(this.pos == 'left'){
23920                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23921             }
23922         }
23923         
23924         return cfg;
23925     },
23926     
23927     initEvents : function() 
23928     {
23929         this.el.on('mouseover', this.onMouseOver, this);
23930         this.el.on('mouseout', this.onMouseOut, this);
23931         
23932         this.el.select('a', true).first().on('click', this.onClick, this);
23933         
23934     },
23935     
23936     onClick : function(e)
23937     {
23938         if(this.preventDefault){
23939             e.preventDefault();
23940         }
23941         
23942         this.fireEvent("click", this, e);
23943     },
23944     
23945     onMouseOver : function(e)
23946     {
23947         if(this.submenu && this.pos == 'left'){
23948             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23949         }
23950         
23951         this.fireEvent("mouseover", this, e);
23952     },
23953     
23954     onMouseOut : function(e)
23955     {
23956         this.fireEvent("mouseout", this, e);
23957     }
23958 });
23959
23960  
23961
23962  /*
23963  * - LGPL
23964  *
23965  * menu separator
23966  * 
23967  */
23968 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23969
23970 /**
23971  * @class Roo.bootstrap.menu.Separator
23972  * @extends Roo.bootstrap.Component
23973  * Bootstrap Separator class
23974  * 
23975  * @constructor
23976  * Create a new Separator
23977  * @param {Object} config The config object
23978  */
23979
23980
23981 Roo.bootstrap.menu.Separator = function(config){
23982     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23983 };
23984
23985 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23986     
23987     getAutoCreate : function(){
23988         var cfg = {
23989             tag : 'li',
23990             cls: 'divider'
23991         };
23992         
23993         return cfg;
23994     }
23995    
23996 });
23997
23998  
23999
24000  /*
24001  * - LGPL
24002  *
24003  * Tooltip
24004  * 
24005  */
24006
24007 /**
24008  * @class Roo.bootstrap.Tooltip
24009  * Bootstrap Tooltip class
24010  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24011  * to determine which dom element triggers the tooltip.
24012  * 
24013  * It needs to add support for additional attributes like tooltip-position
24014  * 
24015  * @constructor
24016  * Create a new Toolti
24017  * @param {Object} config The config object
24018  */
24019
24020 Roo.bootstrap.Tooltip = function(config){
24021     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24022 };
24023
24024 Roo.apply(Roo.bootstrap.Tooltip, {
24025     /**
24026      * @function init initialize tooltip monitoring.
24027      * @static
24028      */
24029     currentEl : false,
24030     currentTip : false,
24031     currentRegion : false,
24032     
24033     //  init : delay?
24034     
24035     init : function()
24036     {
24037         Roo.get(document).on('mouseover', this.enter ,this);
24038         Roo.get(document).on('mouseout', this.leave, this);
24039          
24040         
24041         this.currentTip = new Roo.bootstrap.Tooltip();
24042     },
24043     
24044     enter : function(ev)
24045     {
24046         var dom = ev.getTarget();
24047         
24048         //Roo.log(['enter',dom]);
24049         var el = Roo.fly(dom);
24050         if (this.currentEl) {
24051             //Roo.log(dom);
24052             //Roo.log(this.currentEl);
24053             //Roo.log(this.currentEl.contains(dom));
24054             if (this.currentEl == el) {
24055                 return;
24056             }
24057             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24058                 return;
24059             }
24060
24061         }
24062         
24063         if (this.currentTip.el) {
24064             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24065         }    
24066         //Roo.log(ev);
24067         var bindEl = el;
24068         
24069         // you can not look for children, as if el is the body.. then everythign is the child..
24070         if (!el.attr('tooltip')) { //
24071             if (!el.select("[tooltip]").elements.length) {
24072                 return;
24073             }
24074             // is the mouse over this child...?
24075             bindEl = el.select("[tooltip]").first();
24076             var xy = ev.getXY();
24077             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24078                 //Roo.log("not in region.");
24079                 return;
24080             }
24081             //Roo.log("child element over..");
24082             
24083         }
24084         this.currentEl = bindEl;
24085         this.currentTip.bind(bindEl);
24086         this.currentRegion = Roo.lib.Region.getRegion(dom);
24087         this.currentTip.enter();
24088         
24089     },
24090     leave : function(ev)
24091     {
24092         var dom = ev.getTarget();
24093         //Roo.log(['leave',dom]);
24094         if (!this.currentEl) {
24095             return;
24096         }
24097         
24098         
24099         if (dom != this.currentEl.dom) {
24100             return;
24101         }
24102         var xy = ev.getXY();
24103         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24104             return;
24105         }
24106         // only activate leave if mouse cursor is outside... bounding box..
24107         
24108         
24109         
24110         
24111         if (this.currentTip) {
24112             this.currentTip.leave();
24113         }
24114         //Roo.log('clear currentEl');
24115         this.currentEl = false;
24116         
24117         
24118     },
24119     alignment : {
24120         'left' : ['r-l', [-2,0], 'right'],
24121         'right' : ['l-r', [2,0], 'left'],
24122         'bottom' : ['t-b', [0,2], 'top'],
24123         'top' : [ 'b-t', [0,-2], 'bottom']
24124     }
24125     
24126 });
24127
24128
24129 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24130     
24131     
24132     bindEl : false,
24133     
24134     delay : null, // can be { show : 300 , hide: 500}
24135     
24136     timeout : null,
24137     
24138     hoverState : null, //???
24139     
24140     placement : 'bottom', 
24141     
24142     getAutoCreate : function(){
24143     
24144         var cfg = {
24145            cls : 'tooltip',
24146            role : 'tooltip',
24147            cn : [
24148                 {
24149                     cls : 'tooltip-arrow'
24150                 },
24151                 {
24152                     cls : 'tooltip-inner'
24153                 }
24154            ]
24155         };
24156         
24157         return cfg;
24158     },
24159     bind : function(el)
24160     {
24161         this.bindEl = el;
24162     },
24163       
24164     
24165     enter : function () {
24166        
24167         if (this.timeout != null) {
24168             clearTimeout(this.timeout);
24169         }
24170         
24171         this.hoverState = 'in';
24172          //Roo.log("enter - show");
24173         if (!this.delay || !this.delay.show) {
24174             this.show();
24175             return;
24176         }
24177         var _t = this;
24178         this.timeout = setTimeout(function () {
24179             if (_t.hoverState == 'in') {
24180                 _t.show();
24181             }
24182         }, this.delay.show);
24183     },
24184     leave : function()
24185     {
24186         clearTimeout(this.timeout);
24187     
24188         this.hoverState = 'out';
24189          if (!this.delay || !this.delay.hide) {
24190             this.hide();
24191             return;
24192         }
24193        
24194         var _t = this;
24195         this.timeout = setTimeout(function () {
24196             //Roo.log("leave - timeout");
24197             
24198             if (_t.hoverState == 'out') {
24199                 _t.hide();
24200                 Roo.bootstrap.Tooltip.currentEl = false;
24201             }
24202         }, delay);
24203     },
24204     
24205     show : function ()
24206     {
24207         if (!this.el) {
24208             this.render(document.body);
24209         }
24210         // set content.
24211         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24212         
24213         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24214         
24215         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24216         
24217         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24218         
24219         var placement = typeof this.placement == 'function' ?
24220             this.placement.call(this, this.el, on_el) :
24221             this.placement;
24222             
24223         var autoToken = /\s?auto?\s?/i;
24224         var autoPlace = autoToken.test(placement);
24225         if (autoPlace) {
24226             placement = placement.replace(autoToken, '') || 'top';
24227         }
24228         
24229         //this.el.detach()
24230         //this.el.setXY([0,0]);
24231         this.el.show();
24232         //this.el.dom.style.display='block';
24233         
24234         //this.el.appendTo(on_el);
24235         
24236         var p = this.getPosition();
24237         var box = this.el.getBox();
24238         
24239         if (autoPlace) {
24240             // fixme..
24241         }
24242         
24243         var align = Roo.bootstrap.Tooltip.alignment[placement];
24244         
24245         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24246         
24247         if(placement == 'top' || placement == 'bottom'){
24248             if(xy[0] < 0){
24249                 placement = 'right';
24250             }
24251             
24252             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24253                 placement = 'left';
24254             }
24255         }
24256         
24257         align = Roo.bootstrap.Tooltip.alignment[placement];
24258         
24259         this.el.alignTo(this.bindEl, align[0],align[1]);
24260         //var arrow = this.el.select('.arrow',true).first();
24261         //arrow.set(align[2], 
24262         
24263         this.el.addClass(placement);
24264         
24265         this.el.addClass('in fade');
24266         
24267         this.hoverState = null;
24268         
24269         if (this.el.hasClass('fade')) {
24270             // fade it?
24271         }
24272         
24273     },
24274     hide : function()
24275     {
24276          
24277         if (!this.el) {
24278             return;
24279         }
24280         //this.el.setXY([0,0]);
24281         this.el.removeClass('in');
24282         //this.el.hide();
24283         
24284     }
24285     
24286 });
24287  
24288
24289  /*
24290  * - LGPL
24291  *
24292  * Location Picker
24293  * 
24294  */
24295
24296 /**
24297  * @class Roo.bootstrap.LocationPicker
24298  * @extends Roo.bootstrap.Component
24299  * Bootstrap LocationPicker class
24300  * @cfg {Number} latitude Position when init default 0
24301  * @cfg {Number} longitude Position when init default 0
24302  * @cfg {Number} zoom default 15
24303  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24304  * @cfg {Boolean} mapTypeControl default false
24305  * @cfg {Boolean} disableDoubleClickZoom default false
24306  * @cfg {Boolean} scrollwheel default true
24307  * @cfg {Boolean} streetViewControl default false
24308  * @cfg {Number} radius default 0
24309  * @cfg {String} locationName
24310  * @cfg {Boolean} draggable default true
24311  * @cfg {Boolean} enableAutocomplete default false
24312  * @cfg {Boolean} enableReverseGeocode default true
24313  * @cfg {String} markerTitle
24314  * 
24315  * @constructor
24316  * Create a new LocationPicker
24317  * @param {Object} config The config object
24318  */
24319
24320
24321 Roo.bootstrap.LocationPicker = function(config){
24322     
24323     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24324     
24325     this.addEvents({
24326         /**
24327          * @event initial
24328          * Fires when the picker initialized.
24329          * @param {Roo.bootstrap.LocationPicker} this
24330          * @param {Google Location} location
24331          */
24332         initial : true,
24333         /**
24334          * @event positionchanged
24335          * Fires when the picker position changed.
24336          * @param {Roo.bootstrap.LocationPicker} this
24337          * @param {Google Location} location
24338          */
24339         positionchanged : true,
24340         /**
24341          * @event resize
24342          * Fires when the map resize.
24343          * @param {Roo.bootstrap.LocationPicker} this
24344          */
24345         resize : true,
24346         /**
24347          * @event show
24348          * Fires when the map show.
24349          * @param {Roo.bootstrap.LocationPicker} this
24350          */
24351         show : true,
24352         /**
24353          * @event hide
24354          * Fires when the map hide.
24355          * @param {Roo.bootstrap.LocationPicker} this
24356          */
24357         hide : true,
24358         /**
24359          * @event mapClick
24360          * Fires when click the map.
24361          * @param {Roo.bootstrap.LocationPicker} this
24362          * @param {Map event} e
24363          */
24364         mapClick : true,
24365         /**
24366          * @event mapRightClick
24367          * Fires when right click the map.
24368          * @param {Roo.bootstrap.LocationPicker} this
24369          * @param {Map event} e
24370          */
24371         mapRightClick : true,
24372         /**
24373          * @event markerClick
24374          * Fires when click the marker.
24375          * @param {Roo.bootstrap.LocationPicker} this
24376          * @param {Map event} e
24377          */
24378         markerClick : true,
24379         /**
24380          * @event markerRightClick
24381          * Fires when right click the marker.
24382          * @param {Roo.bootstrap.LocationPicker} this
24383          * @param {Map event} e
24384          */
24385         markerRightClick : true,
24386         /**
24387          * @event OverlayViewDraw
24388          * Fires when OverlayView Draw
24389          * @param {Roo.bootstrap.LocationPicker} this
24390          */
24391         OverlayViewDraw : true,
24392         /**
24393          * @event OverlayViewOnAdd
24394          * Fires when OverlayView Draw
24395          * @param {Roo.bootstrap.LocationPicker} this
24396          */
24397         OverlayViewOnAdd : true,
24398         /**
24399          * @event OverlayViewOnRemove
24400          * Fires when OverlayView Draw
24401          * @param {Roo.bootstrap.LocationPicker} this
24402          */
24403         OverlayViewOnRemove : true,
24404         /**
24405          * @event OverlayViewShow
24406          * Fires when OverlayView Draw
24407          * @param {Roo.bootstrap.LocationPicker} this
24408          * @param {Pixel} cpx
24409          */
24410         OverlayViewShow : true,
24411         /**
24412          * @event OverlayViewHide
24413          * Fires when OverlayView Draw
24414          * @param {Roo.bootstrap.LocationPicker} this
24415          */
24416         OverlayViewHide : true,
24417         /**
24418          * @event loadexception
24419          * Fires when load google lib failed.
24420          * @param {Roo.bootstrap.LocationPicker} this
24421          */
24422         loadexception : true
24423     });
24424         
24425 };
24426
24427 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24428     
24429     gMapContext: false,
24430     
24431     latitude: 0,
24432     longitude: 0,
24433     zoom: 15,
24434     mapTypeId: false,
24435     mapTypeControl: false,
24436     disableDoubleClickZoom: false,
24437     scrollwheel: true,
24438     streetViewControl: false,
24439     radius: 0,
24440     locationName: '',
24441     draggable: true,
24442     enableAutocomplete: false,
24443     enableReverseGeocode: true,
24444     markerTitle: '',
24445     
24446     getAutoCreate: function()
24447     {
24448
24449         var cfg = {
24450             tag: 'div',
24451             cls: 'roo-location-picker'
24452         };
24453         
24454         return cfg
24455     },
24456     
24457     initEvents: function(ct, position)
24458     {       
24459         if(!this.el.getWidth() || this.isApplied()){
24460             return;
24461         }
24462         
24463         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24464         
24465         this.initial();
24466     },
24467     
24468     initial: function()
24469     {
24470         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24471             this.fireEvent('loadexception', this);
24472             return;
24473         }
24474         
24475         if(!this.mapTypeId){
24476             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24477         }
24478         
24479         this.gMapContext = this.GMapContext();
24480         
24481         this.initOverlayView();
24482         
24483         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24484         
24485         var _this = this;
24486                 
24487         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24488             _this.setPosition(_this.gMapContext.marker.position);
24489         });
24490         
24491         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24492             _this.fireEvent('mapClick', this, event);
24493             
24494         });
24495
24496         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24497             _this.fireEvent('mapRightClick', this, event);
24498             
24499         });
24500         
24501         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24502             _this.fireEvent('markerClick', this, event);
24503             
24504         });
24505
24506         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24507             _this.fireEvent('markerRightClick', this, event);
24508             
24509         });
24510         
24511         this.setPosition(this.gMapContext.location);
24512         
24513         this.fireEvent('initial', this, this.gMapContext.location);
24514     },
24515     
24516     initOverlayView: function()
24517     {
24518         var _this = this;
24519         
24520         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24521             
24522             draw: function()
24523             {
24524                 _this.fireEvent('OverlayViewDraw', _this);
24525             },
24526             
24527             onAdd: function()
24528             {
24529                 _this.fireEvent('OverlayViewOnAdd', _this);
24530             },
24531             
24532             onRemove: function()
24533             {
24534                 _this.fireEvent('OverlayViewOnRemove', _this);
24535             },
24536             
24537             show: function(cpx)
24538             {
24539                 _this.fireEvent('OverlayViewShow', _this, cpx);
24540             },
24541             
24542             hide: function()
24543             {
24544                 _this.fireEvent('OverlayViewHide', _this);
24545             }
24546             
24547         });
24548     },
24549     
24550     fromLatLngToContainerPixel: function(event)
24551     {
24552         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24553     },
24554     
24555     isApplied: function() 
24556     {
24557         return this.getGmapContext() == false ? false : true;
24558     },
24559     
24560     getGmapContext: function() 
24561     {
24562         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24563     },
24564     
24565     GMapContext: function() 
24566     {
24567         var position = new google.maps.LatLng(this.latitude, this.longitude);
24568         
24569         var _map = new google.maps.Map(this.el.dom, {
24570             center: position,
24571             zoom: this.zoom,
24572             mapTypeId: this.mapTypeId,
24573             mapTypeControl: this.mapTypeControl,
24574             disableDoubleClickZoom: this.disableDoubleClickZoom,
24575             scrollwheel: this.scrollwheel,
24576             streetViewControl: this.streetViewControl,
24577             locationName: this.locationName,
24578             draggable: this.draggable,
24579             enableAutocomplete: this.enableAutocomplete,
24580             enableReverseGeocode: this.enableReverseGeocode
24581         });
24582         
24583         var _marker = new google.maps.Marker({
24584             position: position,
24585             map: _map,
24586             title: this.markerTitle,
24587             draggable: this.draggable
24588         });
24589         
24590         return {
24591             map: _map,
24592             marker: _marker,
24593             circle: null,
24594             location: position,
24595             radius: this.radius,
24596             locationName: this.locationName,
24597             addressComponents: {
24598                 formatted_address: null,
24599                 addressLine1: null,
24600                 addressLine2: null,
24601                 streetName: null,
24602                 streetNumber: null,
24603                 city: null,
24604                 district: null,
24605                 state: null,
24606                 stateOrProvince: null
24607             },
24608             settings: this,
24609             domContainer: this.el.dom,
24610             geodecoder: new google.maps.Geocoder()
24611         };
24612     },
24613     
24614     drawCircle: function(center, radius, options) 
24615     {
24616         if (this.gMapContext.circle != null) {
24617             this.gMapContext.circle.setMap(null);
24618         }
24619         if (radius > 0) {
24620             radius *= 1;
24621             options = Roo.apply({}, options, {
24622                 strokeColor: "#0000FF",
24623                 strokeOpacity: .35,
24624                 strokeWeight: 2,
24625                 fillColor: "#0000FF",
24626                 fillOpacity: .2
24627             });
24628             
24629             options.map = this.gMapContext.map;
24630             options.radius = radius;
24631             options.center = center;
24632             this.gMapContext.circle = new google.maps.Circle(options);
24633             return this.gMapContext.circle;
24634         }
24635         
24636         return null;
24637     },
24638     
24639     setPosition: function(location) 
24640     {
24641         this.gMapContext.location = location;
24642         this.gMapContext.marker.setPosition(location);
24643         this.gMapContext.map.panTo(location);
24644         this.drawCircle(location, this.gMapContext.radius, {});
24645         
24646         var _this = this;
24647         
24648         if (this.gMapContext.settings.enableReverseGeocode) {
24649             this.gMapContext.geodecoder.geocode({
24650                 latLng: this.gMapContext.location
24651             }, function(results, status) {
24652                 
24653                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24654                     _this.gMapContext.locationName = results[0].formatted_address;
24655                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24656                     
24657                     _this.fireEvent('positionchanged', this, location);
24658                 }
24659             });
24660             
24661             return;
24662         }
24663         
24664         this.fireEvent('positionchanged', this, location);
24665     },
24666     
24667     resize: function()
24668     {
24669         google.maps.event.trigger(this.gMapContext.map, "resize");
24670         
24671         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24672         
24673         this.fireEvent('resize', this);
24674     },
24675     
24676     setPositionByLatLng: function(latitude, longitude)
24677     {
24678         this.setPosition(new google.maps.LatLng(latitude, longitude));
24679     },
24680     
24681     getCurrentPosition: function() 
24682     {
24683         return {
24684             latitude: this.gMapContext.location.lat(),
24685             longitude: this.gMapContext.location.lng()
24686         };
24687     },
24688     
24689     getAddressName: function() 
24690     {
24691         return this.gMapContext.locationName;
24692     },
24693     
24694     getAddressComponents: function() 
24695     {
24696         return this.gMapContext.addressComponents;
24697     },
24698     
24699     address_component_from_google_geocode: function(address_components) 
24700     {
24701         var result = {};
24702         
24703         for (var i = 0; i < address_components.length; i++) {
24704             var component = address_components[i];
24705             if (component.types.indexOf("postal_code") >= 0) {
24706                 result.postalCode = component.short_name;
24707             } else if (component.types.indexOf("street_number") >= 0) {
24708                 result.streetNumber = component.short_name;
24709             } else if (component.types.indexOf("route") >= 0) {
24710                 result.streetName = component.short_name;
24711             } else if (component.types.indexOf("neighborhood") >= 0) {
24712                 result.city = component.short_name;
24713             } else if (component.types.indexOf("locality") >= 0) {
24714                 result.city = component.short_name;
24715             } else if (component.types.indexOf("sublocality") >= 0) {
24716                 result.district = component.short_name;
24717             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24718                 result.stateOrProvince = component.short_name;
24719             } else if (component.types.indexOf("country") >= 0) {
24720                 result.country = component.short_name;
24721             }
24722         }
24723         
24724         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24725         result.addressLine2 = "";
24726         return result;
24727     },
24728     
24729     setZoomLevel: function(zoom)
24730     {
24731         this.gMapContext.map.setZoom(zoom);
24732     },
24733     
24734     show: function()
24735     {
24736         if(!this.el){
24737             return;
24738         }
24739         
24740         this.el.show();
24741         
24742         this.resize();
24743         
24744         this.fireEvent('show', this);
24745     },
24746     
24747     hide: function()
24748     {
24749         if(!this.el){
24750             return;
24751         }
24752         
24753         this.el.hide();
24754         
24755         this.fireEvent('hide', this);
24756     }
24757     
24758 });
24759
24760 Roo.apply(Roo.bootstrap.LocationPicker, {
24761     
24762     OverlayView : function(map, options)
24763     {
24764         options = options || {};
24765         
24766         this.setMap(map);
24767     }
24768     
24769     
24770 });/*
24771  * - LGPL
24772  *
24773  * Alert
24774  * 
24775  */
24776
24777 /**
24778  * @class Roo.bootstrap.Alert
24779  * @extends Roo.bootstrap.Component
24780  * Bootstrap Alert class
24781  * @cfg {String} title The title of alert
24782  * @cfg {String} html The content of alert
24783  * @cfg {String} weight (  success | info | warning | danger )
24784  * @cfg {String} faicon font-awesomeicon
24785  * 
24786  * @constructor
24787  * Create a new alert
24788  * @param {Object} config The config object
24789  */
24790
24791
24792 Roo.bootstrap.Alert = function(config){
24793     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24794     
24795 };
24796
24797 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24798     
24799     title: '',
24800     html: '',
24801     weight: false,
24802     faicon: false,
24803     
24804     getAutoCreate : function()
24805     {
24806         
24807         var cfg = {
24808             tag : 'div',
24809             cls : 'alert',
24810             cn : [
24811                 {
24812                     tag : 'i',
24813                     cls : 'roo-alert-icon'
24814                     
24815                 },
24816                 {
24817                     tag : 'b',
24818                     cls : 'roo-alert-title',
24819                     html : this.title
24820                 },
24821                 {
24822                     tag : 'span',
24823                     cls : 'roo-alert-text',
24824                     html : this.html
24825                 }
24826             ]
24827         };
24828         
24829         if(this.faicon){
24830             cfg.cn[0].cls += ' fa ' + this.faicon;
24831         }
24832         
24833         if(this.weight){
24834             cfg.cls += ' alert-' + this.weight;
24835         }
24836         
24837         return cfg;
24838     },
24839     
24840     initEvents: function() 
24841     {
24842         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24843     },
24844     
24845     setTitle : function(str)
24846     {
24847         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24848     },
24849     
24850     setText : function(str)
24851     {
24852         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24853     },
24854     
24855     setWeight : function(weight)
24856     {
24857         if(this.weight){
24858             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24859         }
24860         
24861         this.weight = weight;
24862         
24863         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24864     },
24865     
24866     setIcon : function(icon)
24867     {
24868         if(this.faicon){
24869             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24870         }
24871         
24872         this.faicon = icon;
24873         
24874         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24875     },
24876     
24877     hide: function() 
24878     {
24879         this.el.hide();   
24880     },
24881     
24882     show: function() 
24883     {  
24884         this.el.show();   
24885     }
24886     
24887 });
24888
24889  
24890 /*
24891 * Licence: LGPL
24892 */
24893
24894 /**
24895  * @class Roo.bootstrap.UploadCropbox
24896  * @extends Roo.bootstrap.Component
24897  * Bootstrap UploadCropbox class
24898  * @cfg {String} emptyText show when image has been loaded
24899  * @cfg {String} rotateNotify show when image too small to rotate
24900  * @cfg {Number} errorTimeout default 3000
24901  * @cfg {Number} minWidth default 300
24902  * @cfg {Number} minHeight default 300
24903  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24904  * @cfg {Boolean} isDocument (true|false) default false
24905  * @cfg {String} url action url
24906  * @cfg {String} paramName default 'imageUpload'
24907  * @cfg {String} method default POST
24908  * @cfg {Boolean} loadMask (true|false) default true
24909  * @cfg {Boolean} loadingText default 'Loading...'
24910  * 
24911  * @constructor
24912  * Create a new UploadCropbox
24913  * @param {Object} config The config object
24914  */
24915
24916 Roo.bootstrap.UploadCropbox = function(config){
24917     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24918     
24919     this.addEvents({
24920         /**
24921          * @event beforeselectfile
24922          * Fire before select file
24923          * @param {Roo.bootstrap.UploadCropbox} this
24924          */
24925         "beforeselectfile" : true,
24926         /**
24927          * @event initial
24928          * Fire after initEvent
24929          * @param {Roo.bootstrap.UploadCropbox} this
24930          */
24931         "initial" : true,
24932         /**
24933          * @event crop
24934          * Fire after initEvent
24935          * @param {Roo.bootstrap.UploadCropbox} this
24936          * @param {String} data
24937          */
24938         "crop" : true,
24939         /**
24940          * @event prepare
24941          * Fire when preparing the file data
24942          * @param {Roo.bootstrap.UploadCropbox} this
24943          * @param {Object} file
24944          */
24945         "prepare" : true,
24946         /**
24947          * @event exception
24948          * Fire when get exception
24949          * @param {Roo.bootstrap.UploadCropbox} this
24950          * @param {XMLHttpRequest} xhr
24951          */
24952         "exception" : true,
24953         /**
24954          * @event beforeloadcanvas
24955          * Fire before load the canvas
24956          * @param {Roo.bootstrap.UploadCropbox} this
24957          * @param {String} src
24958          */
24959         "beforeloadcanvas" : true,
24960         /**
24961          * @event trash
24962          * Fire when trash image
24963          * @param {Roo.bootstrap.UploadCropbox} this
24964          */
24965         "trash" : true,
24966         /**
24967          * @event download
24968          * Fire when download the image
24969          * @param {Roo.bootstrap.UploadCropbox} this
24970          */
24971         "download" : true,
24972         /**
24973          * @event footerbuttonclick
24974          * Fire when footerbuttonclick
24975          * @param {Roo.bootstrap.UploadCropbox} this
24976          * @param {String} type
24977          */
24978         "footerbuttonclick" : true,
24979         /**
24980          * @event resize
24981          * Fire when resize
24982          * @param {Roo.bootstrap.UploadCropbox} this
24983          */
24984         "resize" : true,
24985         /**
24986          * @event rotate
24987          * Fire when rotate the image
24988          * @param {Roo.bootstrap.UploadCropbox} this
24989          * @param {String} pos
24990          */
24991         "rotate" : true,
24992         /**
24993          * @event inspect
24994          * Fire when inspect the file
24995          * @param {Roo.bootstrap.UploadCropbox} this
24996          * @param {Object} file
24997          */
24998         "inspect" : true,
24999         /**
25000          * @event upload
25001          * Fire when xhr upload the file
25002          * @param {Roo.bootstrap.UploadCropbox} this
25003          * @param {Object} data
25004          */
25005         "upload" : true,
25006         /**
25007          * @event arrange
25008          * Fire when arrange the file data
25009          * @param {Roo.bootstrap.UploadCropbox} this
25010          * @param {Object} formData
25011          */
25012         "arrange" : true
25013     });
25014     
25015     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25016 };
25017
25018 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25019     
25020     emptyText : 'Click to upload image',
25021     rotateNotify : 'Image is too small to rotate',
25022     errorTimeout : 3000,
25023     scale : 0,
25024     baseScale : 1,
25025     rotate : 0,
25026     dragable : false,
25027     pinching : false,
25028     mouseX : 0,
25029     mouseY : 0,
25030     cropData : false,
25031     minWidth : 300,
25032     minHeight : 300,
25033     file : false,
25034     exif : {},
25035     baseRotate : 1,
25036     cropType : 'image/jpeg',
25037     buttons : false,
25038     canvasLoaded : false,
25039     isDocument : false,
25040     method : 'POST',
25041     paramName : 'imageUpload',
25042     loadMask : true,
25043     loadingText : 'Loading...',
25044     maskEl : false,
25045     
25046     getAutoCreate : function()
25047     {
25048         var cfg = {
25049             tag : 'div',
25050             cls : 'roo-upload-cropbox',
25051             cn : [
25052                 {
25053                     tag : 'input',
25054                     cls : 'roo-upload-cropbox-selector',
25055                     type : 'file'
25056                 },
25057                 {
25058                     tag : 'div',
25059                     cls : 'roo-upload-cropbox-body',
25060                     style : 'cursor:pointer',
25061                     cn : [
25062                         {
25063                             tag : 'div',
25064                             cls : 'roo-upload-cropbox-preview'
25065                         },
25066                         {
25067                             tag : 'div',
25068                             cls : 'roo-upload-cropbox-thumb'
25069                         },
25070                         {
25071                             tag : 'div',
25072                             cls : 'roo-upload-cropbox-empty-notify',
25073                             html : this.emptyText
25074                         },
25075                         {
25076                             tag : 'div',
25077                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25078                             html : this.rotateNotify
25079                         }
25080                     ]
25081                 },
25082                 {
25083                     tag : 'div',
25084                     cls : 'roo-upload-cropbox-footer',
25085                     cn : {
25086                         tag : 'div',
25087                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25088                         cn : []
25089                     }
25090                 }
25091             ]
25092         };
25093         
25094         return cfg;
25095     },
25096     
25097     onRender : function(ct, position)
25098     {
25099         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25100         
25101         if (this.buttons.length) {
25102             
25103             Roo.each(this.buttons, function(bb) {
25104                 
25105                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25106                 
25107                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25108                 
25109             }, this);
25110         }
25111         
25112         if(this.loadMask){
25113             this.maskEl = this.el;
25114         }
25115     },
25116     
25117     initEvents : function()
25118     {
25119         this.urlAPI = (window.createObjectURL && window) || 
25120                                 (window.URL && URL.revokeObjectURL && URL) || 
25121                                 (window.webkitURL && webkitURL);
25122                         
25123         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25124         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25125         
25126         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25127         this.selectorEl.hide();
25128         
25129         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25130         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25131         
25132         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25133         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25134         this.thumbEl.hide();
25135         
25136         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25137         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25138         
25139         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25140         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25141         this.errorEl.hide();
25142         
25143         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25144         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25145         this.footerEl.hide();
25146         
25147         this.setThumbBoxSize();
25148         
25149         this.bind();
25150         
25151         this.resize();
25152         
25153         this.fireEvent('initial', this);
25154     },
25155
25156     bind : function()
25157     {
25158         var _this = this;
25159         
25160         window.addEventListener("resize", function() { _this.resize(); } );
25161         
25162         this.bodyEl.on('click', this.beforeSelectFile, this);
25163         
25164         if(Roo.isTouch){
25165             this.bodyEl.on('touchstart', this.onTouchStart, this);
25166             this.bodyEl.on('touchmove', this.onTouchMove, this);
25167             this.bodyEl.on('touchend', this.onTouchEnd, this);
25168         }
25169         
25170         if(!Roo.isTouch){
25171             this.bodyEl.on('mousedown', this.onMouseDown, this);
25172             this.bodyEl.on('mousemove', this.onMouseMove, this);
25173             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25174             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25175             Roo.get(document).on('mouseup', this.onMouseUp, this);
25176         }
25177         
25178         this.selectorEl.on('change', this.onFileSelected, this);
25179     },
25180     
25181     reset : function()
25182     {    
25183         this.scale = 0;
25184         this.baseScale = 1;
25185         this.rotate = 0;
25186         this.baseRotate = 1;
25187         this.dragable = false;
25188         this.pinching = false;
25189         this.mouseX = 0;
25190         this.mouseY = 0;
25191         this.cropData = false;
25192         this.notifyEl.dom.innerHTML = this.emptyText;
25193         
25194         this.selectorEl.dom.value = '';
25195         
25196     },
25197     
25198     resize : function()
25199     {
25200         if(this.fireEvent('resize', this) != false){
25201             this.setThumbBoxPosition();
25202             this.setCanvasPosition();
25203         }
25204     },
25205     
25206     onFooterButtonClick : function(e, el, o, type)
25207     {
25208         switch (type) {
25209             case 'rotate-left' :
25210                 this.onRotateLeft(e);
25211                 break;
25212             case 'rotate-right' :
25213                 this.onRotateRight(e);
25214                 break;
25215             case 'picture' :
25216                 this.beforeSelectFile(e);
25217                 break;
25218             case 'trash' :
25219                 this.trash(e);
25220                 break;
25221             case 'crop' :
25222                 this.crop(e);
25223                 break;
25224             case 'download' :
25225                 this.download(e);
25226                 break;
25227             default :
25228                 break;
25229         }
25230         
25231         this.fireEvent('footerbuttonclick', this, type);
25232     },
25233     
25234     beforeSelectFile : function(e)
25235     {
25236         e.preventDefault();
25237         
25238         if(this.fireEvent('beforeselectfile', this) != false){
25239             this.selectorEl.dom.click();
25240         }
25241     },
25242     
25243     onFileSelected : function(e)
25244     {
25245         e.preventDefault();
25246         
25247         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25248             return;
25249         }
25250         
25251         var file = this.selectorEl.dom.files[0];
25252         
25253         if(this.fireEvent('inspect', this, file) != false){
25254             this.prepare(file);
25255         }
25256         
25257     },
25258     
25259     trash : function(e)
25260     {
25261         this.fireEvent('trash', this);
25262     },
25263     
25264     download : function(e)
25265     {
25266         this.fireEvent('download', this);
25267     },
25268     
25269     loadCanvas : function(src)
25270     {   
25271         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25272             
25273             this.reset();
25274             
25275             this.imageEl = document.createElement('img');
25276             
25277             var _this = this;
25278             
25279             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25280             
25281             this.imageEl.src = src;
25282         }
25283     },
25284     
25285     onLoadCanvas : function()
25286     {   
25287         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25288         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25289         
25290         this.bodyEl.un('click', this.beforeSelectFile, this);
25291         
25292         this.notifyEl.hide();
25293         this.thumbEl.show();
25294         this.footerEl.show();
25295         
25296         this.baseRotateLevel();
25297         
25298         if(this.isDocument){
25299             this.setThumbBoxSize();
25300         }
25301         
25302         this.setThumbBoxPosition();
25303         
25304         this.baseScaleLevel();
25305         
25306         this.draw();
25307         
25308         this.resize();
25309         
25310         this.canvasLoaded = true;
25311         
25312         if(this.loadMask){
25313             this.maskEl.unmask();
25314         }
25315         
25316     },
25317     
25318     setCanvasPosition : function()
25319     {   
25320         if(!this.canvasEl){
25321             return;
25322         }
25323         
25324         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25325         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25326         
25327         this.previewEl.setLeft(pw);
25328         this.previewEl.setTop(ph);
25329         
25330     },
25331     
25332     onMouseDown : function(e)
25333     {   
25334         e.stopEvent();
25335         
25336         this.dragable = true;
25337         this.pinching = false;
25338         
25339         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25340             this.dragable = false;
25341             return;
25342         }
25343         
25344         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25345         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25346         
25347     },
25348     
25349     onMouseMove : function(e)
25350     {   
25351         e.stopEvent();
25352         
25353         if(!this.canvasLoaded){
25354             return;
25355         }
25356         
25357         if (!this.dragable){
25358             return;
25359         }
25360         
25361         var minX = Math.ceil(this.thumbEl.getLeft(true));
25362         var minY = Math.ceil(this.thumbEl.getTop(true));
25363         
25364         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25365         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25366         
25367         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25368         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25369         
25370         x = x - this.mouseX;
25371         y = y - this.mouseY;
25372         
25373         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25374         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25375         
25376         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25377         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25378         
25379         this.previewEl.setLeft(bgX);
25380         this.previewEl.setTop(bgY);
25381         
25382         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25383         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25384     },
25385     
25386     onMouseUp : function(e)
25387     {   
25388         e.stopEvent();
25389         
25390         this.dragable = false;
25391     },
25392     
25393     onMouseWheel : function(e)
25394     {   
25395         e.stopEvent();
25396         
25397         this.startScale = this.scale;
25398         
25399         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25400         
25401         if(!this.zoomable()){
25402             this.scale = this.startScale;
25403             return;
25404         }
25405         
25406         this.draw();
25407         
25408         return;
25409     },
25410     
25411     zoomable : function()
25412     {
25413         var minScale = this.thumbEl.getWidth() / this.minWidth;
25414         
25415         if(this.minWidth < this.minHeight){
25416             minScale = this.thumbEl.getHeight() / this.minHeight;
25417         }
25418         
25419         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25420         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25421         
25422         if(
25423                 this.isDocument &&
25424                 (this.rotate == 0 || this.rotate == 180) && 
25425                 (
25426                     width > this.imageEl.OriginWidth || 
25427                     height > this.imageEl.OriginHeight ||
25428                     (width < this.minWidth && height < this.minHeight)
25429                 )
25430         ){
25431             return false;
25432         }
25433         
25434         if(
25435                 this.isDocument &&
25436                 (this.rotate == 90 || this.rotate == 270) && 
25437                 (
25438                     width > this.imageEl.OriginWidth || 
25439                     height > this.imageEl.OriginHeight ||
25440                     (width < this.minHeight && height < this.minWidth)
25441                 )
25442         ){
25443             return false;
25444         }
25445         
25446         if(
25447                 !this.isDocument &&
25448                 (this.rotate == 0 || this.rotate == 180) && 
25449                 (
25450                     width < this.minWidth || 
25451                     width > this.imageEl.OriginWidth || 
25452                     height < this.minHeight || 
25453                     height > this.imageEl.OriginHeight
25454                 )
25455         ){
25456             return false;
25457         }
25458         
25459         if(
25460                 !this.isDocument &&
25461                 (this.rotate == 90 || this.rotate == 270) && 
25462                 (
25463                     width < this.minHeight || 
25464                     width > this.imageEl.OriginWidth || 
25465                     height < this.minWidth || 
25466                     height > this.imageEl.OriginHeight
25467                 )
25468         ){
25469             return false;
25470         }
25471         
25472         return true;
25473         
25474     },
25475     
25476     onRotateLeft : function(e)
25477     {   
25478         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25479             
25480             var minScale = this.thumbEl.getWidth() / this.minWidth;
25481             
25482             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25483             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25484             
25485             this.startScale = this.scale;
25486             
25487             while (this.getScaleLevel() < minScale){
25488             
25489                 this.scale = this.scale + 1;
25490                 
25491                 if(!this.zoomable()){
25492                     break;
25493                 }
25494                 
25495                 if(
25496                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25497                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25498                 ){
25499                     continue;
25500                 }
25501                 
25502                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25503
25504                 this.draw();
25505                 
25506                 return;
25507             }
25508             
25509             this.scale = this.startScale;
25510             
25511             this.onRotateFail();
25512             
25513             return false;
25514         }
25515         
25516         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25517
25518         if(this.isDocument){
25519             this.setThumbBoxSize();
25520             this.setThumbBoxPosition();
25521             this.setCanvasPosition();
25522         }
25523         
25524         this.draw();
25525         
25526         this.fireEvent('rotate', this, 'left');
25527         
25528     },
25529     
25530     onRotateRight : function(e)
25531     {
25532         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25533             
25534             var minScale = this.thumbEl.getWidth() / this.minWidth;
25535         
25536             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25537             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25538             
25539             this.startScale = this.scale;
25540             
25541             while (this.getScaleLevel() < minScale){
25542             
25543                 this.scale = this.scale + 1;
25544                 
25545                 if(!this.zoomable()){
25546                     break;
25547                 }
25548                 
25549                 if(
25550                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25551                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25552                 ){
25553                     continue;
25554                 }
25555                 
25556                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25557
25558                 this.draw();
25559                 
25560                 return;
25561             }
25562             
25563             this.scale = this.startScale;
25564             
25565             this.onRotateFail();
25566             
25567             return false;
25568         }
25569         
25570         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25571
25572         if(this.isDocument){
25573             this.setThumbBoxSize();
25574             this.setThumbBoxPosition();
25575             this.setCanvasPosition();
25576         }
25577         
25578         this.draw();
25579         
25580         this.fireEvent('rotate', this, 'right');
25581     },
25582     
25583     onRotateFail : function()
25584     {
25585         this.errorEl.show(true);
25586         
25587         var _this = this;
25588         
25589         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25590     },
25591     
25592     draw : function()
25593     {
25594         this.previewEl.dom.innerHTML = '';
25595         
25596         var canvasEl = document.createElement("canvas");
25597         
25598         var contextEl = canvasEl.getContext("2d");
25599         
25600         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25601         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25602         var center = this.imageEl.OriginWidth / 2;
25603         
25604         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25605             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25606             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25607             center = this.imageEl.OriginHeight / 2;
25608         }
25609         
25610         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25611         
25612         contextEl.translate(center, center);
25613         contextEl.rotate(this.rotate * Math.PI / 180);
25614
25615         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25616         
25617         this.canvasEl = document.createElement("canvas");
25618         
25619         this.contextEl = this.canvasEl.getContext("2d");
25620         
25621         switch (this.rotate) {
25622             case 0 :
25623                 
25624                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25625                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25626                 
25627                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25628                 
25629                 break;
25630             case 90 : 
25631                 
25632                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25633                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25634                 
25635                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25636                     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);
25637                     break;
25638                 }
25639                 
25640                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25641                 
25642                 break;
25643             case 180 :
25644                 
25645                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25646                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25647                 
25648                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25649                     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);
25650                     break;
25651                 }
25652                 
25653                 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);
25654                 
25655                 break;
25656             case 270 :
25657                 
25658                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25659                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25660         
25661                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25662                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25663                     break;
25664                 }
25665                 
25666                 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);
25667                 
25668                 break;
25669             default : 
25670                 break;
25671         }
25672         
25673         this.previewEl.appendChild(this.canvasEl);
25674         
25675         this.setCanvasPosition();
25676     },
25677     
25678     crop : function()
25679     {
25680         if(!this.canvasLoaded){
25681             return;
25682         }
25683         
25684         var imageCanvas = document.createElement("canvas");
25685         
25686         var imageContext = imageCanvas.getContext("2d");
25687         
25688         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25689         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25690         
25691         var center = imageCanvas.width / 2;
25692         
25693         imageContext.translate(center, center);
25694         
25695         imageContext.rotate(this.rotate * Math.PI / 180);
25696         
25697         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25698         
25699         var canvas = document.createElement("canvas");
25700         
25701         var context = canvas.getContext("2d");
25702                 
25703         canvas.width = this.minWidth;
25704         canvas.height = this.minHeight;
25705
25706         switch (this.rotate) {
25707             case 0 :
25708                 
25709                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25710                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25711                 
25712                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25713                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25714                 
25715                 var targetWidth = this.minWidth - 2 * x;
25716                 var targetHeight = this.minHeight - 2 * y;
25717                 
25718                 var scale = 1;
25719                 
25720                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25721                     scale = targetWidth / width;
25722                 }
25723                 
25724                 if(x > 0 && y == 0){
25725                     scale = targetHeight / height;
25726                 }
25727                 
25728                 if(x > 0 && y > 0){
25729                     scale = targetWidth / width;
25730                     
25731                     if(width < height){
25732                         scale = targetHeight / height;
25733                     }
25734                 }
25735                 
25736                 context.scale(scale, scale);
25737                 
25738                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25739                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25740
25741                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25742                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25743
25744                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25745                 
25746                 break;
25747             case 90 : 
25748                 
25749                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25750                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25751                 
25752                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25753                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25754                 
25755                 var targetWidth = this.minWidth - 2 * x;
25756                 var targetHeight = this.minHeight - 2 * y;
25757                 
25758                 var scale = 1;
25759                 
25760                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25761                     scale = targetWidth / width;
25762                 }
25763                 
25764                 if(x > 0 && y == 0){
25765                     scale = targetHeight / height;
25766                 }
25767                 
25768                 if(x > 0 && y > 0){
25769                     scale = targetWidth / width;
25770                     
25771                     if(width < height){
25772                         scale = targetHeight / height;
25773                     }
25774                 }
25775                 
25776                 context.scale(scale, scale);
25777                 
25778                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25779                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25780
25781                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25782                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25783                 
25784                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25785                 
25786                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25787                 
25788                 break;
25789             case 180 :
25790                 
25791                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25792                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25793                 
25794                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25795                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25796                 
25797                 var targetWidth = this.minWidth - 2 * x;
25798                 var targetHeight = this.minHeight - 2 * y;
25799                 
25800                 var scale = 1;
25801                 
25802                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25803                     scale = targetWidth / width;
25804                 }
25805                 
25806                 if(x > 0 && y == 0){
25807                     scale = targetHeight / height;
25808                 }
25809                 
25810                 if(x > 0 && y > 0){
25811                     scale = targetWidth / width;
25812                     
25813                     if(width < height){
25814                         scale = targetHeight / height;
25815                     }
25816                 }
25817                 
25818                 context.scale(scale, scale);
25819                 
25820                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25821                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25822
25823                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25824                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25825
25826                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25827                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25828                 
25829                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25830                 
25831                 break;
25832             case 270 :
25833                 
25834                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25835                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25836                 
25837                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25838                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25839                 
25840                 var targetWidth = this.minWidth - 2 * x;
25841                 var targetHeight = this.minHeight - 2 * y;
25842                 
25843                 var scale = 1;
25844                 
25845                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25846                     scale = targetWidth / width;
25847                 }
25848                 
25849                 if(x > 0 && y == 0){
25850                     scale = targetHeight / height;
25851                 }
25852                 
25853                 if(x > 0 && y > 0){
25854                     scale = targetWidth / width;
25855                     
25856                     if(width < height){
25857                         scale = targetHeight / height;
25858                     }
25859                 }
25860                 
25861                 context.scale(scale, scale);
25862                 
25863                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25864                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25865
25866                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25867                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25868                 
25869                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25870                 
25871                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25872                 
25873                 break;
25874             default : 
25875                 break;
25876         }
25877         
25878         this.cropData = canvas.toDataURL(this.cropType);
25879         
25880         if(this.fireEvent('crop', this, this.cropData) !== false){
25881             this.process(this.file, this.cropData);
25882         }
25883         
25884         return;
25885         
25886     },
25887     
25888     setThumbBoxSize : function()
25889     {
25890         var width, height;
25891         
25892         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25893             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25894             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25895             
25896             this.minWidth = width;
25897             this.minHeight = height;
25898             
25899             if(this.rotate == 90 || this.rotate == 270){
25900                 this.minWidth = height;
25901                 this.minHeight = width;
25902             }
25903         }
25904         
25905         height = 300;
25906         width = Math.ceil(this.minWidth * height / this.minHeight);
25907         
25908         if(this.minWidth > this.minHeight){
25909             width = 300;
25910             height = Math.ceil(this.minHeight * width / this.minWidth);
25911         }
25912         
25913         this.thumbEl.setStyle({
25914             width : width + 'px',
25915             height : height + 'px'
25916         });
25917
25918         return;
25919             
25920     },
25921     
25922     setThumbBoxPosition : function()
25923     {
25924         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25925         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25926         
25927         this.thumbEl.setLeft(x);
25928         this.thumbEl.setTop(y);
25929         
25930     },
25931     
25932     baseRotateLevel : function()
25933     {
25934         this.baseRotate = 1;
25935         
25936         if(
25937                 typeof(this.exif) != 'undefined' &&
25938                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25939                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25940         ){
25941             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25942         }
25943         
25944         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25945         
25946     },
25947     
25948     baseScaleLevel : function()
25949     {
25950         var width, height;
25951         
25952         if(this.isDocument){
25953             
25954             if(this.baseRotate == 6 || this.baseRotate == 8){
25955             
25956                 height = this.thumbEl.getHeight();
25957                 this.baseScale = height / this.imageEl.OriginWidth;
25958
25959                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25960                     width = this.thumbEl.getWidth();
25961                     this.baseScale = width / this.imageEl.OriginHeight;
25962                 }
25963
25964                 return;
25965             }
25966
25967             height = this.thumbEl.getHeight();
25968             this.baseScale = height / this.imageEl.OriginHeight;
25969
25970             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25971                 width = this.thumbEl.getWidth();
25972                 this.baseScale = width / this.imageEl.OriginWidth;
25973             }
25974
25975             return;
25976         }
25977         
25978         if(this.baseRotate == 6 || this.baseRotate == 8){
25979             
25980             width = this.thumbEl.getHeight();
25981             this.baseScale = width / this.imageEl.OriginHeight;
25982             
25983             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25984                 height = this.thumbEl.getWidth();
25985                 this.baseScale = height / this.imageEl.OriginHeight;
25986             }
25987             
25988             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25989                 height = this.thumbEl.getWidth();
25990                 this.baseScale = height / this.imageEl.OriginHeight;
25991                 
25992                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25993                     width = this.thumbEl.getHeight();
25994                     this.baseScale = width / this.imageEl.OriginWidth;
25995                 }
25996             }
25997             
25998             return;
25999         }
26000         
26001         width = this.thumbEl.getWidth();
26002         this.baseScale = width / this.imageEl.OriginWidth;
26003         
26004         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26005             height = this.thumbEl.getHeight();
26006             this.baseScale = height / this.imageEl.OriginHeight;
26007         }
26008         
26009         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26010             
26011             height = this.thumbEl.getHeight();
26012             this.baseScale = height / this.imageEl.OriginHeight;
26013             
26014             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26015                 width = this.thumbEl.getWidth();
26016                 this.baseScale = width / this.imageEl.OriginWidth;
26017             }
26018             
26019         }
26020         
26021         return;
26022     },
26023     
26024     getScaleLevel : function()
26025     {
26026         return this.baseScale * Math.pow(1.1, this.scale);
26027     },
26028     
26029     onTouchStart : function(e)
26030     {
26031         if(!this.canvasLoaded){
26032             this.beforeSelectFile(e);
26033             return;
26034         }
26035         
26036         var touches = e.browserEvent.touches;
26037         
26038         if(!touches){
26039             return;
26040         }
26041         
26042         if(touches.length == 1){
26043             this.onMouseDown(e);
26044             return;
26045         }
26046         
26047         if(touches.length != 2){
26048             return;
26049         }
26050         
26051         var coords = [];
26052         
26053         for(var i = 0, finger; finger = touches[i]; i++){
26054             coords.push(finger.pageX, finger.pageY);
26055         }
26056         
26057         var x = Math.pow(coords[0] - coords[2], 2);
26058         var y = Math.pow(coords[1] - coords[3], 2);
26059         
26060         this.startDistance = Math.sqrt(x + y);
26061         
26062         this.startScale = this.scale;
26063         
26064         this.pinching = true;
26065         this.dragable = false;
26066         
26067     },
26068     
26069     onTouchMove : function(e)
26070     {
26071         if(!this.pinching && !this.dragable){
26072             return;
26073         }
26074         
26075         var touches = e.browserEvent.touches;
26076         
26077         if(!touches){
26078             return;
26079         }
26080         
26081         if(this.dragable){
26082             this.onMouseMove(e);
26083             return;
26084         }
26085         
26086         var coords = [];
26087         
26088         for(var i = 0, finger; finger = touches[i]; i++){
26089             coords.push(finger.pageX, finger.pageY);
26090         }
26091         
26092         var x = Math.pow(coords[0] - coords[2], 2);
26093         var y = Math.pow(coords[1] - coords[3], 2);
26094         
26095         this.endDistance = Math.sqrt(x + y);
26096         
26097         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26098         
26099         if(!this.zoomable()){
26100             this.scale = this.startScale;
26101             return;
26102         }
26103         
26104         this.draw();
26105         
26106     },
26107     
26108     onTouchEnd : function(e)
26109     {
26110         this.pinching = false;
26111         this.dragable = false;
26112         
26113     },
26114     
26115     process : function(file, crop)
26116     {
26117         if(this.loadMask){
26118             this.maskEl.mask(this.loadingText);
26119         }
26120         
26121         this.xhr = new XMLHttpRequest();
26122         
26123         file.xhr = this.xhr;
26124
26125         this.xhr.open(this.method, this.url, true);
26126         
26127         var headers = {
26128             "Accept": "application/json",
26129             "Cache-Control": "no-cache",
26130             "X-Requested-With": "XMLHttpRequest"
26131         };
26132         
26133         for (var headerName in headers) {
26134             var headerValue = headers[headerName];
26135             if (headerValue) {
26136                 this.xhr.setRequestHeader(headerName, headerValue);
26137             }
26138         }
26139         
26140         var _this = this;
26141         
26142         this.xhr.onload = function()
26143         {
26144             _this.xhrOnLoad(_this.xhr);
26145         }
26146         
26147         this.xhr.onerror = function()
26148         {
26149             _this.xhrOnError(_this.xhr);
26150         }
26151         
26152         var formData = new FormData();
26153
26154         formData.append('returnHTML', 'NO');
26155         
26156         if(crop){
26157             formData.append('crop', crop);
26158         }
26159         
26160         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26161             formData.append(this.paramName, file, file.name);
26162         }
26163         
26164         if(typeof(file.filename) != 'undefined'){
26165             formData.append('filename', file.filename);
26166         }
26167         
26168         if(typeof(file.mimetype) != 'undefined'){
26169             formData.append('mimetype', file.mimetype);
26170         }
26171         
26172         if(this.fireEvent('arrange', this, formData) != false){
26173             this.xhr.send(formData);
26174         };
26175     },
26176     
26177     xhrOnLoad : function(xhr)
26178     {
26179         if(this.loadMask){
26180             this.maskEl.unmask();
26181         }
26182         
26183         if (xhr.readyState !== 4) {
26184             this.fireEvent('exception', this, xhr);
26185             return;
26186         }
26187
26188         var response = Roo.decode(xhr.responseText);
26189         
26190         if(!response.success){
26191             this.fireEvent('exception', this, xhr);
26192             return;
26193         }
26194         
26195         var response = Roo.decode(xhr.responseText);
26196         
26197         this.fireEvent('upload', this, response);
26198         
26199     },
26200     
26201     xhrOnError : function()
26202     {
26203         if(this.loadMask){
26204             this.maskEl.unmask();
26205         }
26206         
26207         Roo.log('xhr on error');
26208         
26209         var response = Roo.decode(xhr.responseText);
26210           
26211         Roo.log(response);
26212         
26213     },
26214     
26215     prepare : function(file)
26216     {   
26217         if(this.loadMask){
26218             this.maskEl.mask(this.loadingText);
26219         }
26220         
26221         this.file = false;
26222         this.exif = {};
26223         
26224         if(typeof(file) === 'string'){
26225             this.loadCanvas(file);
26226             return;
26227         }
26228         
26229         if(!file || !this.urlAPI){
26230             return;
26231         }
26232         
26233         this.file = file;
26234         this.cropType = file.type;
26235         
26236         var _this = this;
26237         
26238         if(this.fireEvent('prepare', this, this.file) != false){
26239             
26240             var reader = new FileReader();
26241             
26242             reader.onload = function (e) {
26243                 if (e.target.error) {
26244                     Roo.log(e.target.error);
26245                     return;
26246                 }
26247                 
26248                 var buffer = e.target.result,
26249                     dataView = new DataView(buffer),
26250                     offset = 2,
26251                     maxOffset = dataView.byteLength - 4,
26252                     markerBytes,
26253                     markerLength;
26254                 
26255                 if (dataView.getUint16(0) === 0xffd8) {
26256                     while (offset < maxOffset) {
26257                         markerBytes = dataView.getUint16(offset);
26258                         
26259                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26260                             markerLength = dataView.getUint16(offset + 2) + 2;
26261                             if (offset + markerLength > dataView.byteLength) {
26262                                 Roo.log('Invalid meta data: Invalid segment size.');
26263                                 break;
26264                             }
26265                             
26266                             if(markerBytes == 0xffe1){
26267                                 _this.parseExifData(
26268                                     dataView,
26269                                     offset,
26270                                     markerLength
26271                                 );
26272                             }
26273                             
26274                             offset += markerLength;
26275                             
26276                             continue;
26277                         }
26278                         
26279                         break;
26280                     }
26281                     
26282                 }
26283                 
26284                 var url = _this.urlAPI.createObjectURL(_this.file);
26285                 
26286                 _this.loadCanvas(url);
26287                 
26288                 return;
26289             }
26290             
26291             reader.readAsArrayBuffer(this.file);
26292             
26293         }
26294         
26295     },
26296     
26297     parseExifData : function(dataView, offset, length)
26298     {
26299         var tiffOffset = offset + 10,
26300             littleEndian,
26301             dirOffset;
26302     
26303         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26304             // No Exif data, might be XMP data instead
26305             return;
26306         }
26307         
26308         // Check for the ASCII code for "Exif" (0x45786966):
26309         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26310             // No Exif data, might be XMP data instead
26311             return;
26312         }
26313         if (tiffOffset + 8 > dataView.byteLength) {
26314             Roo.log('Invalid Exif data: Invalid segment size.');
26315             return;
26316         }
26317         // Check for the two null bytes:
26318         if (dataView.getUint16(offset + 8) !== 0x0000) {
26319             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26320             return;
26321         }
26322         // Check the byte alignment:
26323         switch (dataView.getUint16(tiffOffset)) {
26324         case 0x4949:
26325             littleEndian = true;
26326             break;
26327         case 0x4D4D:
26328             littleEndian = false;
26329             break;
26330         default:
26331             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26332             return;
26333         }
26334         // Check for the TIFF tag marker (0x002A):
26335         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26336             Roo.log('Invalid Exif data: Missing TIFF marker.');
26337             return;
26338         }
26339         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26340         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26341         
26342         this.parseExifTags(
26343             dataView,
26344             tiffOffset,
26345             tiffOffset + dirOffset,
26346             littleEndian
26347         );
26348     },
26349     
26350     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26351     {
26352         var tagsNumber,
26353             dirEndOffset,
26354             i;
26355         if (dirOffset + 6 > dataView.byteLength) {
26356             Roo.log('Invalid Exif data: Invalid directory offset.');
26357             return;
26358         }
26359         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26360         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26361         if (dirEndOffset + 4 > dataView.byteLength) {
26362             Roo.log('Invalid Exif data: Invalid directory size.');
26363             return;
26364         }
26365         for (i = 0; i < tagsNumber; i += 1) {
26366             this.parseExifTag(
26367                 dataView,
26368                 tiffOffset,
26369                 dirOffset + 2 + 12 * i, // tag offset
26370                 littleEndian
26371             );
26372         }
26373         // Return the offset to the next directory:
26374         return dataView.getUint32(dirEndOffset, littleEndian);
26375     },
26376     
26377     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26378     {
26379         var tag = dataView.getUint16(offset, littleEndian);
26380         
26381         this.exif[tag] = this.getExifValue(
26382             dataView,
26383             tiffOffset,
26384             offset,
26385             dataView.getUint16(offset + 2, littleEndian), // tag type
26386             dataView.getUint32(offset + 4, littleEndian), // tag length
26387             littleEndian
26388         );
26389     },
26390     
26391     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26392     {
26393         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26394             tagSize,
26395             dataOffset,
26396             values,
26397             i,
26398             str,
26399             c;
26400     
26401         if (!tagType) {
26402             Roo.log('Invalid Exif data: Invalid tag type.');
26403             return;
26404         }
26405         
26406         tagSize = tagType.size * length;
26407         // Determine if the value is contained in the dataOffset bytes,
26408         // or if the value at the dataOffset is a pointer to the actual data:
26409         dataOffset = tagSize > 4 ?
26410                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26411         if (dataOffset + tagSize > dataView.byteLength) {
26412             Roo.log('Invalid Exif data: Invalid data offset.');
26413             return;
26414         }
26415         if (length === 1) {
26416             return tagType.getValue(dataView, dataOffset, littleEndian);
26417         }
26418         values = [];
26419         for (i = 0; i < length; i += 1) {
26420             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26421         }
26422         
26423         if (tagType.ascii) {
26424             str = '';
26425             // Concatenate the chars:
26426             for (i = 0; i < values.length; i += 1) {
26427                 c = values[i];
26428                 // Ignore the terminating NULL byte(s):
26429                 if (c === '\u0000') {
26430                     break;
26431                 }
26432                 str += c;
26433             }
26434             return str;
26435         }
26436         return values;
26437     }
26438     
26439 });
26440
26441 Roo.apply(Roo.bootstrap.UploadCropbox, {
26442     tags : {
26443         'Orientation': 0x0112
26444     },
26445     
26446     Orientation: {
26447             1: 0, //'top-left',
26448 //            2: 'top-right',
26449             3: 180, //'bottom-right',
26450 //            4: 'bottom-left',
26451 //            5: 'left-top',
26452             6: 90, //'right-top',
26453 //            7: 'right-bottom',
26454             8: 270 //'left-bottom'
26455     },
26456     
26457     exifTagTypes : {
26458         // byte, 8-bit unsigned int:
26459         1: {
26460             getValue: function (dataView, dataOffset) {
26461                 return dataView.getUint8(dataOffset);
26462             },
26463             size: 1
26464         },
26465         // ascii, 8-bit byte:
26466         2: {
26467             getValue: function (dataView, dataOffset) {
26468                 return String.fromCharCode(dataView.getUint8(dataOffset));
26469             },
26470             size: 1,
26471             ascii: true
26472         },
26473         // short, 16 bit int:
26474         3: {
26475             getValue: function (dataView, dataOffset, littleEndian) {
26476                 return dataView.getUint16(dataOffset, littleEndian);
26477             },
26478             size: 2
26479         },
26480         // long, 32 bit int:
26481         4: {
26482             getValue: function (dataView, dataOffset, littleEndian) {
26483                 return dataView.getUint32(dataOffset, littleEndian);
26484             },
26485             size: 4
26486         },
26487         // rational = two long values, first is numerator, second is denominator:
26488         5: {
26489             getValue: function (dataView, dataOffset, littleEndian) {
26490                 return dataView.getUint32(dataOffset, littleEndian) /
26491                     dataView.getUint32(dataOffset + 4, littleEndian);
26492             },
26493             size: 8
26494         },
26495         // slong, 32 bit signed int:
26496         9: {
26497             getValue: function (dataView, dataOffset, littleEndian) {
26498                 return dataView.getInt32(dataOffset, littleEndian);
26499             },
26500             size: 4
26501         },
26502         // srational, two slongs, first is numerator, second is denominator:
26503         10: {
26504             getValue: function (dataView, dataOffset, littleEndian) {
26505                 return dataView.getInt32(dataOffset, littleEndian) /
26506                     dataView.getInt32(dataOffset + 4, littleEndian);
26507             },
26508             size: 8
26509         }
26510     },
26511     
26512     footer : {
26513         STANDARD : [
26514             {
26515                 tag : 'div',
26516                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26517                 action : 'rotate-left',
26518                 cn : [
26519                     {
26520                         tag : 'button',
26521                         cls : 'btn btn-default',
26522                         html : '<i class="fa fa-undo"></i>'
26523                     }
26524                 ]
26525             },
26526             {
26527                 tag : 'div',
26528                 cls : 'btn-group roo-upload-cropbox-picture',
26529                 action : 'picture',
26530                 cn : [
26531                     {
26532                         tag : 'button',
26533                         cls : 'btn btn-default',
26534                         html : '<i class="fa fa-picture-o"></i>'
26535                     }
26536                 ]
26537             },
26538             {
26539                 tag : 'div',
26540                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26541                 action : 'rotate-right',
26542                 cn : [
26543                     {
26544                         tag : 'button',
26545                         cls : 'btn btn-default',
26546                         html : '<i class="fa fa-repeat"></i>'
26547                     }
26548                 ]
26549             }
26550         ],
26551         DOCUMENT : [
26552             {
26553                 tag : 'div',
26554                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26555                 action : 'rotate-left',
26556                 cn : [
26557                     {
26558                         tag : 'button',
26559                         cls : 'btn btn-default',
26560                         html : '<i class="fa fa-undo"></i>'
26561                     }
26562                 ]
26563             },
26564             {
26565                 tag : 'div',
26566                 cls : 'btn-group roo-upload-cropbox-download',
26567                 action : 'download',
26568                 cn : [
26569                     {
26570                         tag : 'button',
26571                         cls : 'btn btn-default',
26572                         html : '<i class="fa fa-download"></i>'
26573                     }
26574                 ]
26575             },
26576             {
26577                 tag : 'div',
26578                 cls : 'btn-group roo-upload-cropbox-crop',
26579                 action : 'crop',
26580                 cn : [
26581                     {
26582                         tag : 'button',
26583                         cls : 'btn btn-default',
26584                         html : '<i class="fa fa-crop"></i>'
26585                     }
26586                 ]
26587             },
26588             {
26589                 tag : 'div',
26590                 cls : 'btn-group roo-upload-cropbox-trash',
26591                 action : 'trash',
26592                 cn : [
26593                     {
26594                         tag : 'button',
26595                         cls : 'btn btn-default',
26596                         html : '<i class="fa fa-trash"></i>'
26597                     }
26598                 ]
26599             },
26600             {
26601                 tag : 'div',
26602                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26603                 action : 'rotate-right',
26604                 cn : [
26605                     {
26606                         tag : 'button',
26607                         cls : 'btn btn-default',
26608                         html : '<i class="fa fa-repeat"></i>'
26609                     }
26610                 ]
26611             }
26612         ],
26613         ROTATOR : [
26614             {
26615                 tag : 'div',
26616                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26617                 action : 'rotate-left',
26618                 cn : [
26619                     {
26620                         tag : 'button',
26621                         cls : 'btn btn-default',
26622                         html : '<i class="fa fa-undo"></i>'
26623                     }
26624                 ]
26625             },
26626             {
26627                 tag : 'div',
26628                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26629                 action : 'rotate-right',
26630                 cn : [
26631                     {
26632                         tag : 'button',
26633                         cls : 'btn btn-default',
26634                         html : '<i class="fa fa-repeat"></i>'
26635                     }
26636                 ]
26637             }
26638         ]
26639     }
26640 });
26641
26642 /*
26643 * Licence: LGPL
26644 */
26645
26646 /**
26647  * @class Roo.bootstrap.DocumentManager
26648  * @extends Roo.bootstrap.Component
26649  * Bootstrap DocumentManager class
26650  * @cfg {String} paramName default 'imageUpload'
26651  * @cfg {String} method default POST
26652  * @cfg {String} url action url
26653  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26654  * @cfg {Boolean} multiple multiple upload default true
26655  * @cfg {Number} thumbSize default 300
26656  * @cfg {String} fieldLabel
26657  * @cfg {Number} labelWidth default 4
26658  * @cfg {String} labelAlign (left|top) default left
26659  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26660  * 
26661  * @constructor
26662  * Create a new DocumentManager
26663  * @param {Object} config The config object
26664  */
26665
26666 Roo.bootstrap.DocumentManager = function(config){
26667     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26668     
26669     this.addEvents({
26670         /**
26671          * @event initial
26672          * Fire when initial the DocumentManager
26673          * @param {Roo.bootstrap.DocumentManager} this
26674          */
26675         "initial" : true,
26676         /**
26677          * @event inspect
26678          * inspect selected file
26679          * @param {Roo.bootstrap.DocumentManager} this
26680          * @param {File} file
26681          */
26682         "inspect" : true,
26683         /**
26684          * @event exception
26685          * Fire when xhr load exception
26686          * @param {Roo.bootstrap.DocumentManager} this
26687          * @param {XMLHttpRequest} xhr
26688          */
26689         "exception" : true,
26690         /**
26691          * @event prepare
26692          * prepare the form data
26693          * @param {Roo.bootstrap.DocumentManager} this
26694          * @param {Object} formData
26695          */
26696         "prepare" : true,
26697         /**
26698          * @event remove
26699          * Fire when remove the file
26700          * @param {Roo.bootstrap.DocumentManager} this
26701          * @param {Object} file
26702          */
26703         "remove" : true,
26704         /**
26705          * @event refresh
26706          * Fire after refresh the file
26707          * @param {Roo.bootstrap.DocumentManager} this
26708          */
26709         "refresh" : true,
26710         /**
26711          * @event click
26712          * Fire after click the image
26713          * @param {Roo.bootstrap.DocumentManager} this
26714          * @param {Object} file
26715          */
26716         "click" : true,
26717         /**
26718          * @event edit
26719          * Fire when upload a image and editable set to true
26720          * @param {Roo.bootstrap.DocumentManager} this
26721          * @param {Object} file
26722          */
26723         "edit" : true,
26724         /**
26725          * @event beforeselectfile
26726          * Fire before select file
26727          * @param {Roo.bootstrap.DocumentManager} this
26728          */
26729         "beforeselectfile" : true,
26730         /**
26731          * @event process
26732          * Fire before process file
26733          * @param {Roo.bootstrap.DocumentManager} this
26734          * @param {Object} file
26735          */
26736         "process" : true
26737         
26738     });
26739 };
26740
26741 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26742     
26743     boxes : 0,
26744     inputName : '',
26745     thumbSize : 300,
26746     multiple : true,
26747     files : [],
26748     method : 'POST',
26749     url : '',
26750     paramName : 'imageUpload',
26751     fieldLabel : '',
26752     labelWidth : 4,
26753     labelAlign : 'left',
26754     editable : true,
26755     delegates : [],
26756     
26757     
26758     xhr : false, 
26759     
26760     getAutoCreate : function()
26761     {   
26762         var managerWidget = {
26763             tag : 'div',
26764             cls : 'roo-document-manager',
26765             cn : [
26766                 {
26767                     tag : 'input',
26768                     cls : 'roo-document-manager-selector',
26769                     type : 'file'
26770                 },
26771                 {
26772                     tag : 'div',
26773                     cls : 'roo-document-manager-uploader',
26774                     cn : [
26775                         {
26776                             tag : 'div',
26777                             cls : 'roo-document-manager-upload-btn',
26778                             html : '<i class="fa fa-plus"></i>'
26779                         }
26780                     ]
26781                     
26782                 }
26783             ]
26784         };
26785         
26786         var content = [
26787             {
26788                 tag : 'div',
26789                 cls : 'column col-md-12',
26790                 cn : managerWidget
26791             }
26792         ];
26793         
26794         if(this.fieldLabel.length){
26795             
26796             content = [
26797                 {
26798                     tag : 'div',
26799                     cls : 'column col-md-12',
26800                     html : this.fieldLabel
26801                 },
26802                 {
26803                     tag : 'div',
26804                     cls : 'column col-md-12',
26805                     cn : managerWidget
26806                 }
26807             ];
26808
26809             if(this.labelAlign == 'left'){
26810                 content = [
26811                     {
26812                         tag : 'div',
26813                         cls : 'column col-md-' + this.labelWidth,
26814                         html : this.fieldLabel
26815                     },
26816                     {
26817                         tag : 'div',
26818                         cls : 'column col-md-' + (12 - this.labelWidth),
26819                         cn : managerWidget
26820                     }
26821                 ];
26822                 
26823             }
26824         }
26825         
26826         var cfg = {
26827             tag : 'div',
26828             cls : 'row clearfix',
26829             cn : content
26830         };
26831         
26832         return cfg;
26833         
26834     },
26835     
26836     initEvents : function()
26837     {
26838         this.managerEl = this.el.select('.roo-document-manager', true).first();
26839         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26840         
26841         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26842         this.selectorEl.hide();
26843         
26844         if(this.multiple){
26845             this.selectorEl.attr('multiple', 'multiple');
26846         }
26847         
26848         this.selectorEl.on('change', this.onFileSelected, this);
26849         
26850         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26851         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26852         
26853         this.uploader.on('click', this.onUploaderClick, this);
26854         
26855         this.renderProgressDialog();
26856         
26857         var _this = this;
26858         
26859         window.addEventListener("resize", function() { _this.refresh(); } );
26860         
26861         this.fireEvent('initial', this);
26862     },
26863     
26864     renderProgressDialog : function()
26865     {
26866         var _this = this;
26867         
26868         this.progressDialog = new Roo.bootstrap.Modal({
26869             cls : 'roo-document-manager-progress-dialog',
26870             allow_close : false,
26871             title : '',
26872             buttons : [
26873                 {
26874                     name  :'cancel',
26875                     weight : 'danger',
26876                     html : 'Cancel'
26877                 }
26878             ], 
26879             listeners : { 
26880                 btnclick : function() {
26881                     _this.uploadCancel();
26882                     this.hide();
26883                 }
26884             }
26885         });
26886          
26887         this.progressDialog.render(Roo.get(document.body));
26888          
26889         this.progress = new Roo.bootstrap.Progress({
26890             cls : 'roo-document-manager-progress',
26891             active : true,
26892             striped : true
26893         });
26894         
26895         this.progress.render(this.progressDialog.getChildContainer());
26896         
26897         this.progressBar = new Roo.bootstrap.ProgressBar({
26898             cls : 'roo-document-manager-progress-bar',
26899             aria_valuenow : 0,
26900             aria_valuemin : 0,
26901             aria_valuemax : 12,
26902             panel : 'success'
26903         });
26904         
26905         this.progressBar.render(this.progress.getChildContainer());
26906     },
26907     
26908     onUploaderClick : function(e)
26909     {
26910         e.preventDefault();
26911      
26912         if(this.fireEvent('beforeselectfile', this) != false){
26913             this.selectorEl.dom.click();
26914         }
26915         
26916     },
26917     
26918     onFileSelected : function(e)
26919     {
26920         e.preventDefault();
26921         
26922         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26923             return;
26924         }
26925         
26926         Roo.each(this.selectorEl.dom.files, function(file){
26927             if(this.fireEvent('inspect', this, file) != false){
26928                 this.files.push(file);
26929             }
26930         }, this);
26931         
26932         this.queue();
26933         
26934     },
26935     
26936     queue : function()
26937     {
26938         this.selectorEl.dom.value = '';
26939         
26940         if(!this.files.length){
26941             return;
26942         }
26943         
26944         if(this.boxes > 0 && this.files.length > this.boxes){
26945             this.files = this.files.slice(0, this.boxes);
26946         }
26947         
26948         this.uploader.show();
26949         
26950         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26951             this.uploader.hide();
26952         }
26953         
26954         var _this = this;
26955         
26956         var files = [];
26957         
26958         var docs = [];
26959         
26960         Roo.each(this.files, function(file){
26961             
26962             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26963                 var f = this.renderPreview(file);
26964                 files.push(f);
26965                 return;
26966             }
26967             
26968             if(file.type.indexOf('image') != -1){
26969                 this.delegates.push(
26970                     (function(){
26971                         _this.process(file);
26972                     }).createDelegate(this)
26973                 );
26974         
26975                 return;
26976             }
26977             
26978             docs.push(
26979                 (function(){
26980                     _this.process(file);
26981                 }).createDelegate(this)
26982             );
26983             
26984         }, this);
26985         
26986         this.files = files;
26987         
26988         this.delegates = this.delegates.concat(docs);
26989         
26990         if(!this.delegates.length){
26991             this.refresh();
26992             return;
26993         }
26994         
26995         this.progressBar.aria_valuemax = this.delegates.length;
26996         
26997         this.arrange();
26998         
26999         return;
27000     },
27001     
27002     arrange : function()
27003     {
27004         if(!this.delegates.length){
27005             this.progressDialog.hide();
27006             this.refresh();
27007             return;
27008         }
27009         
27010         var delegate = this.delegates.shift();
27011         
27012         this.progressDialog.show();
27013         
27014         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27015         
27016         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27017         
27018         delegate();
27019     },
27020     
27021     refresh : function()
27022     {
27023         this.uploader.show();
27024         
27025         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27026             this.uploader.hide();
27027         }
27028         
27029         Roo.isTouch ? this.closable(false) : this.closable(true);
27030         
27031         this.fireEvent('refresh', this);
27032     },
27033     
27034     onRemove : function(e, el, o)
27035     {
27036         e.preventDefault();
27037         
27038         this.fireEvent('remove', this, o);
27039         
27040     },
27041     
27042     remove : function(o)
27043     {
27044         var files = [];
27045         
27046         Roo.each(this.files, function(file){
27047             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27048                 files.push(file);
27049                 return;
27050             }
27051
27052             o.target.remove();
27053
27054         }, this);
27055         
27056         this.files = files;
27057         
27058         this.refresh();
27059     },
27060     
27061     clear : function()
27062     {
27063         Roo.each(this.files, function(file){
27064             if(!file.target){
27065                 return;
27066             }
27067             
27068             file.target.remove();
27069
27070         }, this);
27071         
27072         this.files = [];
27073         
27074         this.refresh();
27075     },
27076     
27077     onClick : function(e, el, o)
27078     {
27079         e.preventDefault();
27080         
27081         this.fireEvent('click', this, o);
27082         
27083     },
27084     
27085     closable : function(closable)
27086     {
27087         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27088             
27089             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27090             
27091             if(closable){
27092                 el.show();
27093                 return;
27094             }
27095             
27096             el.hide();
27097             
27098         }, this);
27099     },
27100     
27101     xhrOnLoad : function(xhr)
27102     {
27103         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27104             el.remove();
27105         }, this);
27106         
27107         if (xhr.readyState !== 4) {
27108             this.arrange();
27109             this.fireEvent('exception', this, xhr);
27110             return;
27111         }
27112
27113         var response = Roo.decode(xhr.responseText);
27114         
27115         if(!response.success){
27116             this.arrange();
27117             this.fireEvent('exception', this, xhr);
27118             return;
27119         }
27120         
27121         var file = this.renderPreview(response.data);
27122         
27123         this.files.push(file);
27124         
27125         this.arrange();
27126         
27127     },
27128     
27129     xhrOnError : function(xhr)
27130     {
27131         Roo.log('xhr on error');
27132         
27133         var response = Roo.decode(xhr.responseText);
27134           
27135         Roo.log(response);
27136         
27137         this.arrange();
27138     },
27139     
27140     process : function(file)
27141     {
27142         if(this.fireEvent('process', this, file) !== false){
27143             if(this.editable && file.type.indexOf('image') != -1){
27144                 this.fireEvent('edit', this, file);
27145                 return;
27146             }
27147
27148             this.uploadStart(file, false);
27149
27150             return;
27151         }
27152         
27153     },
27154     
27155     uploadStart : function(file, crop)
27156     {
27157         this.xhr = new XMLHttpRequest();
27158         
27159         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27160             this.arrange();
27161             return;
27162         }
27163         
27164         file.xhr = this.xhr;
27165             
27166         this.managerEl.createChild({
27167             tag : 'div',
27168             cls : 'roo-document-manager-loading',
27169             cn : [
27170                 {
27171                     tag : 'div',
27172                     tooltip : file.name,
27173                     cls : 'roo-document-manager-thumb',
27174                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27175                 }
27176             ]
27177
27178         });
27179
27180         this.xhr.open(this.method, this.url, true);
27181         
27182         var headers = {
27183             "Accept": "application/json",
27184             "Cache-Control": "no-cache",
27185             "X-Requested-With": "XMLHttpRequest"
27186         };
27187         
27188         for (var headerName in headers) {
27189             var headerValue = headers[headerName];
27190             if (headerValue) {
27191                 this.xhr.setRequestHeader(headerName, headerValue);
27192             }
27193         }
27194         
27195         var _this = this;
27196         
27197         this.xhr.onload = function()
27198         {
27199             _this.xhrOnLoad(_this.xhr);
27200         }
27201         
27202         this.xhr.onerror = function()
27203         {
27204             _this.xhrOnError(_this.xhr);
27205         }
27206         
27207         var formData = new FormData();
27208
27209         formData.append('returnHTML', 'NO');
27210         
27211         if(crop){
27212             formData.append('crop', crop);
27213         }
27214         
27215         formData.append(this.paramName, file, file.name);
27216         
27217         if(this.fireEvent('prepare', this, formData) != false){
27218             this.xhr.send(formData);
27219         };
27220     },
27221     
27222     uploadCancel : function()
27223     {
27224         if (this.xhr) {
27225             this.xhr.abort();
27226         }
27227         
27228         
27229         this.delegates = [];
27230         
27231         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27232             el.remove();
27233         }, this);
27234         
27235         this.arrange();
27236     },
27237     
27238     renderPreview : function(file)
27239     {
27240         if(typeof(file.target) != 'undefined' && file.target){
27241             return file;
27242         }
27243         
27244         var previewEl = this.managerEl.createChild({
27245             tag : 'div',
27246             cls : 'roo-document-manager-preview',
27247             cn : [
27248                 {
27249                     tag : 'div',
27250                     tooltip : file.filename,
27251                     cls : 'roo-document-manager-thumb',
27252                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27253                 },
27254                 {
27255                     tag : 'button',
27256                     cls : 'close',
27257                     html : '<i class="fa fa-times-circle"></i>'
27258                 }
27259             ]
27260         });
27261
27262         var close = previewEl.select('button.close', true).first();
27263
27264         close.on('click', this.onRemove, this, file);
27265
27266         file.target = previewEl;
27267
27268         var image = previewEl.select('img', true).first();
27269         
27270         var _this = this;
27271         
27272         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27273         
27274         image.on('click', this.onClick, this, file);
27275         
27276         return file;
27277         
27278     },
27279     
27280     onPreviewLoad : function(file, image)
27281     {
27282         if(typeof(file.target) == 'undefined' || !file.target){
27283             return;
27284         }
27285         
27286         var width = image.dom.naturalWidth || image.dom.width;
27287         var height = image.dom.naturalHeight || image.dom.height;
27288         
27289         if(width > height){
27290             file.target.addClass('wide');
27291             return;
27292         }
27293         
27294         file.target.addClass('tall');
27295         return;
27296         
27297     },
27298     
27299     uploadFromSource : function(file, crop)
27300     {
27301         this.xhr = new XMLHttpRequest();
27302         
27303         this.managerEl.createChild({
27304             tag : 'div',
27305             cls : 'roo-document-manager-loading',
27306             cn : [
27307                 {
27308                     tag : 'div',
27309                     tooltip : file.name,
27310                     cls : 'roo-document-manager-thumb',
27311                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27312                 }
27313             ]
27314
27315         });
27316
27317         this.xhr.open(this.method, this.url, true);
27318         
27319         var headers = {
27320             "Accept": "application/json",
27321             "Cache-Control": "no-cache",
27322             "X-Requested-With": "XMLHttpRequest"
27323         };
27324         
27325         for (var headerName in headers) {
27326             var headerValue = headers[headerName];
27327             if (headerValue) {
27328                 this.xhr.setRequestHeader(headerName, headerValue);
27329             }
27330         }
27331         
27332         var _this = this;
27333         
27334         this.xhr.onload = function()
27335         {
27336             _this.xhrOnLoad(_this.xhr);
27337         }
27338         
27339         this.xhr.onerror = function()
27340         {
27341             _this.xhrOnError(_this.xhr);
27342         }
27343         
27344         var formData = new FormData();
27345
27346         formData.append('returnHTML', 'NO');
27347         
27348         formData.append('crop', crop);
27349         
27350         if(typeof(file.filename) != 'undefined'){
27351             formData.append('filename', file.filename);
27352         }
27353         
27354         if(typeof(file.mimetype) != 'undefined'){
27355             formData.append('mimetype', file.mimetype);
27356         }
27357         
27358         if(this.fireEvent('prepare', this, formData) != false){
27359             this.xhr.send(formData);
27360         };
27361     }
27362 });
27363
27364 /*
27365 * Licence: LGPL
27366 */
27367
27368 /**
27369  * @class Roo.bootstrap.DocumentViewer
27370  * @extends Roo.bootstrap.Component
27371  * Bootstrap DocumentViewer class
27372  * 
27373  * @constructor
27374  * Create a new DocumentViewer
27375  * @param {Object} config The config object
27376  */
27377
27378 Roo.bootstrap.DocumentViewer = function(config){
27379     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27380     
27381     this.addEvents({
27382         /**
27383          * @event initial
27384          * Fire after initEvent
27385          * @param {Roo.bootstrap.DocumentViewer} this
27386          */
27387         "initial" : true,
27388         /**
27389          * @event click
27390          * Fire after click
27391          * @param {Roo.bootstrap.DocumentViewer} this
27392          */
27393         "click" : true,
27394         /**
27395          * @event trash
27396          * Fire after trash button
27397          * @param {Roo.bootstrap.DocumentViewer} this
27398          */
27399         "trash" : true
27400         
27401     });
27402 };
27403
27404 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27405     
27406     getAutoCreate : function()
27407     {
27408         var cfg = {
27409             tag : 'div',
27410             cls : 'roo-document-viewer',
27411             cn : [
27412                 {
27413                     tag : 'div',
27414                     cls : 'roo-document-viewer-body',
27415                     cn : [
27416                         {
27417                             tag : 'div',
27418                             cls : 'roo-document-viewer-thumb',
27419                             cn : [
27420                                 {
27421                                     tag : 'img',
27422                                     cls : 'roo-document-viewer-image'
27423                                 }
27424                             ]
27425                         }
27426                     ]
27427                 },
27428                 {
27429                     tag : 'div',
27430                     cls : 'roo-document-viewer-footer',
27431                     cn : {
27432                         tag : 'div',
27433                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27434                         cn : [
27435                             {
27436                                 tag : 'div',
27437                                 cls : 'btn-group',
27438                                 cn : [
27439                                     {
27440                                         tag : 'button',
27441                                         cls : 'btn btn-default roo-document-viewer-trash',
27442                                         html : '<i class="fa fa-trash"></i>'
27443                                     }
27444                                 ]
27445                             }
27446                         ]
27447                     }
27448                 }
27449             ]
27450         };
27451         
27452         return cfg;
27453     },
27454     
27455     initEvents : function()
27456     {
27457         
27458         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27459         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27460         
27461         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27462         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27463         
27464         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27465         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27466         
27467         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27468         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27469         
27470         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27471         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27472         
27473         this.bodyEl.on('click', this.onClick, this);
27474         
27475         this.trashBtn.on('click', this.onTrash, this);
27476         
27477     },
27478     
27479     initial : function()
27480     {
27481 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27482         
27483         
27484         this.fireEvent('initial', this);
27485         
27486     },
27487     
27488     onClick : function(e)
27489     {
27490         e.preventDefault();
27491         
27492         this.fireEvent('click', this);
27493     },
27494     
27495     onTrash : function(e)
27496     {
27497         e.preventDefault();
27498         
27499         this.fireEvent('trash', this);
27500     }
27501     
27502 });
27503 /*
27504  * - LGPL
27505  *
27506  * nav progress bar
27507  * 
27508  */
27509
27510 /**
27511  * @class Roo.bootstrap.NavProgressBar
27512  * @extends Roo.bootstrap.Component
27513  * Bootstrap NavProgressBar class
27514  * 
27515  * @constructor
27516  * Create a new nav progress bar
27517  * @param {Object} config The config object
27518  */
27519
27520 Roo.bootstrap.NavProgressBar = function(config){
27521     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27522
27523     this.bullets = this.bullets || [];
27524    
27525 //    Roo.bootstrap.NavProgressBar.register(this);
27526      this.addEvents({
27527         /**
27528              * @event changed
27529              * Fires when the active item changes
27530              * @param {Roo.bootstrap.NavProgressBar} this
27531              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27532              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27533          */
27534         'changed': true
27535      });
27536     
27537 };
27538
27539 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27540     
27541     bullets : [],
27542     barItems : [],
27543     
27544     getAutoCreate : function()
27545     {
27546         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27547         
27548         cfg = {
27549             tag : 'div',
27550             cls : 'roo-navigation-bar-group',
27551             cn : [
27552                 {
27553                     tag : 'div',
27554                     cls : 'roo-navigation-top-bar'
27555                 },
27556                 {
27557                     tag : 'div',
27558                     cls : 'roo-navigation-bullets-bar',
27559                     cn : [
27560                         {
27561                             tag : 'ul',
27562                             cls : 'roo-navigation-bar'
27563                         }
27564                     ]
27565                 },
27566                 
27567                 {
27568                     tag : 'div',
27569                     cls : 'roo-navigation-bottom-bar'
27570                 }
27571             ]
27572             
27573         };
27574         
27575         return cfg;
27576         
27577     },
27578     
27579     initEvents: function() 
27580     {
27581         
27582     },
27583     
27584     onRender : function(ct, position) 
27585     {
27586         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27587         
27588         if(this.bullets.length){
27589             Roo.each(this.bullets, function(b){
27590                this.addItem(b);
27591             }, this);
27592         }
27593         
27594         this.format();
27595         
27596     },
27597     
27598     addItem : function(cfg)
27599     {
27600         var item = new Roo.bootstrap.NavProgressItem(cfg);
27601         
27602         item.parentId = this.id;
27603         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27604         
27605         if(cfg.html){
27606             var top = new Roo.bootstrap.Element({
27607                 tag : 'div',
27608                 cls : 'roo-navigation-bar-text'
27609             });
27610             
27611             var bottom = new Roo.bootstrap.Element({
27612                 tag : 'div',
27613                 cls : 'roo-navigation-bar-text'
27614             });
27615             
27616             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27617             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27618             
27619             var topText = new Roo.bootstrap.Element({
27620                 tag : 'span',
27621                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27622             });
27623             
27624             var bottomText = new Roo.bootstrap.Element({
27625                 tag : 'span',
27626                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27627             });
27628             
27629             topText.onRender(top.el, null);
27630             bottomText.onRender(bottom.el, null);
27631             
27632             item.topEl = top;
27633             item.bottomEl = bottom;
27634         }
27635         
27636         this.barItems.push(item);
27637         
27638         return item;
27639     },
27640     
27641     getActive : function()
27642     {
27643         var active = false;
27644         
27645         Roo.each(this.barItems, function(v){
27646             
27647             if (!v.isActive()) {
27648                 return;
27649             }
27650             
27651             active = v;
27652             return false;
27653             
27654         });
27655         
27656         return active;
27657     },
27658     
27659     setActiveItem : function(item)
27660     {
27661         var prev = false;
27662         
27663         Roo.each(this.barItems, function(v){
27664             if (v.rid == item.rid) {
27665                 return ;
27666             }
27667             
27668             if (v.isActive()) {
27669                 v.setActive(false);
27670                 prev = v;
27671             }
27672         });
27673
27674         item.setActive(true);
27675         
27676         this.fireEvent('changed', this, item, prev);
27677     },
27678     
27679     getBarItem: function(rid)
27680     {
27681         var ret = false;
27682         
27683         Roo.each(this.barItems, function(e) {
27684             if (e.rid != rid) {
27685                 return;
27686             }
27687             
27688             ret =  e;
27689             return false;
27690         });
27691         
27692         return ret;
27693     },
27694     
27695     indexOfItem : function(item)
27696     {
27697         var index = false;
27698         
27699         Roo.each(this.barItems, function(v, i){
27700             
27701             if (v.rid != item.rid) {
27702                 return;
27703             }
27704             
27705             index = i;
27706             return false
27707         });
27708         
27709         return index;
27710     },
27711     
27712     setActiveNext : function()
27713     {
27714         var i = this.indexOfItem(this.getActive());
27715         
27716         if (i > this.barItems.length) {
27717             return;
27718         }
27719         
27720         this.setActiveItem(this.barItems[i+1]);
27721     },
27722     
27723     setActivePrev : function()
27724     {
27725         var i = this.indexOfItem(this.getActive());
27726         
27727         if (i  < 1) {
27728             return;
27729         }
27730         
27731         this.setActiveItem(this.barItems[i-1]);
27732     },
27733     
27734     format : function()
27735     {
27736         if(!this.barItems.length){
27737             return;
27738         }
27739      
27740         var width = 100 / this.barItems.length;
27741         
27742         Roo.each(this.barItems, function(i){
27743             i.el.setStyle('width', width + '%');
27744             i.topEl.el.setStyle('width', width + '%');
27745             i.bottomEl.el.setStyle('width', width + '%');
27746         }, this);
27747         
27748     }
27749     
27750 });
27751 /*
27752  * - LGPL
27753  *
27754  * Nav Progress Item
27755  * 
27756  */
27757
27758 /**
27759  * @class Roo.bootstrap.NavProgressItem
27760  * @extends Roo.bootstrap.Component
27761  * Bootstrap NavProgressItem class
27762  * @cfg {String} rid the reference id
27763  * @cfg {Boolean} active (true|false) Is item active default false
27764  * @cfg {Boolean} disabled (true|false) Is item active default false
27765  * @cfg {String} html
27766  * @cfg {String} position (top|bottom) text position default bottom
27767  * @cfg {String} icon show icon instead of number
27768  * 
27769  * @constructor
27770  * Create a new NavProgressItem
27771  * @param {Object} config The config object
27772  */
27773 Roo.bootstrap.NavProgressItem = function(config){
27774     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27775     this.addEvents({
27776         // raw events
27777         /**
27778          * @event click
27779          * The raw click event for the entire grid.
27780          * @param {Roo.bootstrap.NavProgressItem} this
27781          * @param {Roo.EventObject} e
27782          */
27783         "click" : true
27784     });
27785    
27786 };
27787
27788 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27789     
27790     rid : '',
27791     active : false,
27792     disabled : false,
27793     html : '',
27794     position : 'bottom',
27795     icon : false,
27796     
27797     getAutoCreate : function()
27798     {
27799         var iconCls = 'roo-navigation-bar-item-icon';
27800         
27801         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27802         
27803         var cfg = {
27804             tag: 'li',
27805             cls: 'roo-navigation-bar-item',
27806             cn : [
27807                 {
27808                     tag : 'i',
27809                     cls : iconCls
27810                 }
27811             ]
27812         };
27813         
27814         if(this.active){
27815             cfg.cls += ' active';
27816         }
27817         if(this.disabled){
27818             cfg.cls += ' disabled';
27819         }
27820         
27821         return cfg;
27822     },
27823     
27824     disable : function()
27825     {
27826         this.setDisabled(true);
27827     },
27828     
27829     enable : function()
27830     {
27831         this.setDisabled(false);
27832     },
27833     
27834     initEvents: function() 
27835     {
27836         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27837         
27838         this.iconEl.on('click', this.onClick, this);
27839     },
27840     
27841     onClick : function(e)
27842     {
27843         e.preventDefault();
27844         
27845         if(this.disabled){
27846             return;
27847         }
27848         
27849         if(this.fireEvent('click', this, e) === false){
27850             return;
27851         };
27852         
27853         this.parent().setActiveItem(this);
27854     },
27855     
27856     isActive: function () 
27857     {
27858         return this.active;
27859     },
27860     
27861     setActive : function(state)
27862     {
27863         if(this.active == state){
27864             return;
27865         }
27866         
27867         this.active = state;
27868         
27869         if (state) {
27870             this.el.addClass('active');
27871             return;
27872         }
27873         
27874         this.el.removeClass('active');
27875         
27876         return;
27877     },
27878     
27879     setDisabled : function(state)
27880     {
27881         if(this.disabled == state){
27882             return;
27883         }
27884         
27885         this.disabled = state;
27886         
27887         if (state) {
27888             this.el.addClass('disabled');
27889             return;
27890         }
27891         
27892         this.el.removeClass('disabled');
27893     },
27894     
27895     tooltipEl : function()
27896     {
27897         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27898     }
27899 });
27900  
27901
27902  /*
27903  * - LGPL
27904  *
27905  * FieldLabel
27906  * 
27907  */
27908
27909 /**
27910  * @class Roo.bootstrap.FieldLabel
27911  * @extends Roo.bootstrap.Component
27912  * Bootstrap FieldLabel class
27913  * @cfg {String} html contents of the element
27914  * @cfg {String} tag tag of the element default label
27915  * @cfg {String} cls class of the element
27916  * @cfg {String} target label target 
27917  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27918  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27919  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27920  * @cfg {String} iconTooltip default "This field is required"
27921  * 
27922  * @constructor
27923  * Create a new FieldLabel
27924  * @param {Object} config The config object
27925  */
27926
27927 Roo.bootstrap.FieldLabel = function(config){
27928     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27929     
27930     this.addEvents({
27931             /**
27932              * @event invalid
27933              * Fires after the field has been marked as invalid.
27934              * @param {Roo.form.FieldLabel} this
27935              * @param {String} msg The validation message
27936              */
27937             invalid : true,
27938             /**
27939              * @event valid
27940              * Fires after the field has been validated with no errors.
27941              * @param {Roo.form.FieldLabel} this
27942              */
27943             valid : true
27944         });
27945 };
27946
27947 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27948     
27949     tag: 'label',
27950     cls: '',
27951     html: '',
27952     target: '',
27953     allowBlank : true,
27954     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27955     validClass : 'text-success fa fa-lg fa-check',
27956     iconTooltip : 'This field is required',
27957     
27958     getAutoCreate : function(){
27959         
27960         var cfg = {
27961             tag : this.tag,
27962             cls : 'roo-bootstrap-field-label ' + this.cls,
27963             for : this.target,
27964             cn : [
27965                 {
27966                     tag : 'i',
27967                     cls : '',
27968                     tooltip : this.iconTooltip
27969                 },
27970                 {
27971                     tag : 'span',
27972                     html : this.html
27973                 }
27974             ] 
27975         };
27976         
27977         return cfg;
27978     },
27979     
27980     initEvents: function() 
27981     {
27982         Roo.bootstrap.Element.superclass.initEvents.call(this);
27983         
27984         this.iconEl = this.el.select('i', true).first();
27985         
27986         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27987         
27988         Roo.bootstrap.FieldLabel.register(this);
27989     },
27990     
27991     /**
27992      * Mark this field as valid
27993      */
27994     markValid : function()
27995     {
27996         this.iconEl.show();
27997         
27998         this.iconEl.removeClass(this.invalidClass);
27999         
28000         this.iconEl.addClass(this.validClass);
28001         
28002         this.fireEvent('valid', this);
28003     },
28004     
28005     /**
28006      * Mark this field as invalid
28007      * @param {String} msg The validation message
28008      */
28009     markInvalid : function(msg)
28010     {
28011         this.iconEl.show();
28012         
28013         this.iconEl.removeClass(this.validClass);
28014         
28015         this.iconEl.addClass(this.invalidClass);
28016         
28017         this.fireEvent('invalid', this, msg);
28018     }
28019     
28020    
28021 });
28022
28023 Roo.apply(Roo.bootstrap.FieldLabel, {
28024     
28025     groups: {},
28026     
28027      /**
28028     * register a FieldLabel Group
28029     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28030     */
28031     register : function(label)
28032     {
28033         if(this.groups.hasOwnProperty(label.target)){
28034             return;
28035         }
28036      
28037         this.groups[label.target] = label;
28038         
28039     },
28040     /**
28041     * fetch a FieldLabel Group based on the target
28042     * @param {string} target
28043     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28044     */
28045     get: function(target) {
28046         if (typeof(this.groups[target]) == 'undefined') {
28047             return false;
28048         }
28049         
28050         return this.groups[target] ;
28051     }
28052 });
28053
28054  
28055
28056  /*
28057  * - LGPL
28058  *
28059  * page DateSplitField.
28060  * 
28061  */
28062
28063
28064 /**
28065  * @class Roo.bootstrap.DateSplitField
28066  * @extends Roo.bootstrap.Component
28067  * Bootstrap DateSplitField class
28068  * @cfg {string} fieldLabel - the label associated
28069  * @cfg {Number} labelWidth set the width of label (0-12)
28070  * @cfg {String} labelAlign (top|left)
28071  * @cfg {Boolean} dayAllowBlank (true|false) default false
28072  * @cfg {Boolean} monthAllowBlank (true|false) default false
28073  * @cfg {Boolean} yearAllowBlank (true|false) default false
28074  * @cfg {string} dayPlaceholder 
28075  * @cfg {string} monthPlaceholder
28076  * @cfg {string} yearPlaceholder
28077  * @cfg {string} dayFormat default 'd'
28078  * @cfg {string} monthFormat default 'm'
28079  * @cfg {string} yearFormat default 'Y'
28080
28081  *     
28082  * @constructor
28083  * Create a new DateSplitField
28084  * @param {Object} config The config object
28085  */
28086
28087 Roo.bootstrap.DateSplitField = function(config){
28088     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28089     
28090     this.addEvents({
28091         // raw events
28092          /**
28093          * @event years
28094          * getting the data of years
28095          * @param {Roo.bootstrap.DateSplitField} this
28096          * @param {Object} years
28097          */
28098         "years" : true,
28099         /**
28100          * @event days
28101          * getting the data of days
28102          * @param {Roo.bootstrap.DateSplitField} this
28103          * @param {Object} days
28104          */
28105         "days" : true,
28106         /**
28107          * @event invalid
28108          * Fires after the field has been marked as invalid.
28109          * @param {Roo.form.Field} this
28110          * @param {String} msg The validation message
28111          */
28112         invalid : true,
28113        /**
28114          * @event valid
28115          * Fires after the field has been validated with no errors.
28116          * @param {Roo.form.Field} this
28117          */
28118         valid : true
28119     });
28120 };
28121
28122 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28123     
28124     fieldLabel : '',
28125     labelAlign : 'top',
28126     labelWidth : 3,
28127     dayAllowBlank : false,
28128     monthAllowBlank : false,
28129     yearAllowBlank : false,
28130     dayPlaceholder : '',
28131     monthPlaceholder : '',
28132     yearPlaceholder : '',
28133     dayFormat : 'd',
28134     monthFormat : 'm',
28135     yearFormat : 'Y',
28136     isFormField : true,
28137     
28138     getAutoCreate : function()
28139     {
28140         var cfg = {
28141             tag : 'div',
28142             cls : 'row roo-date-split-field-group',
28143             cn : [
28144                 {
28145                     tag : 'input',
28146                     type : 'hidden',
28147                     cls : 'form-hidden-field roo-date-split-field-group-value',
28148                     name : this.name
28149                 }
28150             ]
28151         };
28152         
28153         if(this.fieldLabel){
28154             cfg.cn.push({
28155                 tag : 'div',
28156                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28157                 cn : [
28158                     {
28159                         tag : 'label',
28160                         html : this.fieldLabel
28161                     }
28162                 ]
28163             });
28164         }
28165         
28166         Roo.each(['day', 'month', 'year'], function(t){
28167             cfg.cn.push({
28168                 tag : 'div',
28169                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28170             });
28171         }, this);
28172         
28173         return cfg;
28174     },
28175     
28176     inputEl: function ()
28177     {
28178         return this.el.select('.roo-date-split-field-group-value', true).first();
28179     },
28180     
28181     onRender : function(ct, position) 
28182     {
28183         var _this = this;
28184         
28185         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28186         
28187         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28188         
28189         this.dayField = new Roo.bootstrap.ComboBox({
28190             allowBlank : this.dayAllowBlank,
28191             alwaysQuery : true,
28192             displayField : 'value',
28193             editable : false,
28194             fieldLabel : '',
28195             forceSelection : true,
28196             mode : 'local',
28197             placeholder : this.dayPlaceholder,
28198             selectOnFocus : true,
28199             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28200             triggerAction : 'all',
28201             typeAhead : true,
28202             valueField : 'value',
28203             store : new Roo.data.SimpleStore({
28204                 data : (function() {    
28205                     var days = [];
28206                     _this.fireEvent('days', _this, days);
28207                     return days;
28208                 })(),
28209                 fields : [ 'value' ]
28210             }),
28211             listeners : {
28212                 select : function (_self, record, index)
28213                 {
28214                     _this.setValue(_this.getValue());
28215                 }
28216             }
28217         });
28218
28219         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28220         
28221         this.monthField = new Roo.bootstrap.MonthField({
28222             after : '<i class=\"fa fa-calendar\"></i>',
28223             allowBlank : this.monthAllowBlank,
28224             placeholder : this.monthPlaceholder,
28225             readOnly : true,
28226             listeners : {
28227                 render : function (_self)
28228                 {
28229                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28230                         e.preventDefault();
28231                         _self.focus();
28232                     });
28233                 },
28234                 select : function (_self, oldvalue, newvalue)
28235                 {
28236                     _this.setValue(_this.getValue());
28237                 }
28238             }
28239         });
28240         
28241         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28242         
28243         this.yearField = new Roo.bootstrap.ComboBox({
28244             allowBlank : this.yearAllowBlank,
28245             alwaysQuery : true,
28246             displayField : 'value',
28247             editable : false,
28248             fieldLabel : '',
28249             forceSelection : true,
28250             mode : 'local',
28251             placeholder : this.yearPlaceholder,
28252             selectOnFocus : true,
28253             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28254             triggerAction : 'all',
28255             typeAhead : true,
28256             valueField : 'value',
28257             store : new Roo.data.SimpleStore({
28258                 data : (function() {
28259                     var years = [];
28260                     _this.fireEvent('years', _this, years);
28261                     return years;
28262                 })(),
28263                 fields : [ 'value' ]
28264             }),
28265             listeners : {
28266                 select : function (_self, record, index)
28267                 {
28268                     _this.setValue(_this.getValue());
28269                 }
28270             }
28271         });
28272
28273         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28274     },
28275     
28276     setValue : function(v, format)
28277     {
28278         this.inputEl.dom.value = v;
28279         
28280         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28281         
28282         var d = Date.parseDate(v, f);
28283         
28284         if(!d){
28285             this.validate();
28286             return;
28287         }
28288         
28289         this.setDay(d.format(this.dayFormat));
28290         this.setMonth(d.format(this.monthFormat));
28291         this.setYear(d.format(this.yearFormat));
28292         
28293         this.validate();
28294         
28295         return;
28296     },
28297     
28298     setDay : function(v)
28299     {
28300         this.dayField.setValue(v);
28301         this.inputEl.dom.value = this.getValue();
28302         this.validate();
28303         return;
28304     },
28305     
28306     setMonth : function(v)
28307     {
28308         this.monthField.setValue(v, true);
28309         this.inputEl.dom.value = this.getValue();
28310         this.validate();
28311         return;
28312     },
28313     
28314     setYear : function(v)
28315     {
28316         this.yearField.setValue(v);
28317         this.inputEl.dom.value = this.getValue();
28318         this.validate();
28319         return;
28320     },
28321     
28322     getDay : function()
28323     {
28324         return this.dayField.getValue();
28325     },
28326     
28327     getMonth : function()
28328     {
28329         return this.monthField.getValue();
28330     },
28331     
28332     getYear : function()
28333     {
28334         return this.yearField.getValue();
28335     },
28336     
28337     getValue : function()
28338     {
28339         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28340         
28341         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28342         
28343         return date;
28344     },
28345     
28346     reset : function()
28347     {
28348         this.setDay('');
28349         this.setMonth('');
28350         this.setYear('');
28351         this.inputEl.dom.value = '';
28352         this.validate();
28353         return;
28354     },
28355     
28356     validate : function()
28357     {
28358         var d = this.dayField.validate();
28359         var m = this.monthField.validate();
28360         var y = this.yearField.validate();
28361         
28362         var valid = true;
28363         
28364         if(
28365                 (!this.dayAllowBlank && !d) ||
28366                 (!this.monthAllowBlank && !m) ||
28367                 (!this.yearAllowBlank && !y)
28368         ){
28369             valid = false;
28370         }
28371         
28372         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28373             return valid;
28374         }
28375         
28376         if(valid){
28377             this.markValid();
28378             return valid;
28379         }
28380         
28381         this.markInvalid();
28382         
28383         return valid;
28384     },
28385     
28386     markValid : function()
28387     {
28388         
28389         var label = this.el.select('label', true).first();
28390         var icon = this.el.select('i.fa-star', true).first();
28391
28392         if(label && icon){
28393             icon.remove();
28394         }
28395         
28396         this.fireEvent('valid', this);
28397     },
28398     
28399      /**
28400      * Mark this field as invalid
28401      * @param {String} msg The validation message
28402      */
28403     markInvalid : function(msg)
28404     {
28405         
28406         var label = this.el.select('label', true).first();
28407         var icon = this.el.select('i.fa-star', true).first();
28408
28409         if(label && !icon){
28410             this.el.select('.roo-date-split-field-label', true).createChild({
28411                 tag : 'i',
28412                 cls : 'text-danger fa fa-lg fa-star',
28413                 tooltip : 'This field is required',
28414                 style : 'margin-right:5px;'
28415             }, label, true);
28416         }
28417         
28418         this.fireEvent('invalid', this, msg);
28419     },
28420     
28421     clearInvalid : function()
28422     {
28423         var label = this.el.select('label', true).first();
28424         var icon = this.el.select('i.fa-star', true).first();
28425
28426         if(label && icon){
28427             icon.remove();
28428         }
28429         
28430         this.fireEvent('valid', this);
28431     },
28432     
28433     getName: function()
28434     {
28435         return this.name;
28436     }
28437     
28438 });
28439
28440  /**
28441  *
28442  * This is based on 
28443  * http://masonry.desandro.com
28444  *
28445  * The idea is to render all the bricks based on vertical width...
28446  *
28447  * The original code extends 'outlayer' - we might need to use that....
28448  * 
28449  */
28450
28451
28452 /**
28453  * @class Roo.bootstrap.LayoutMasonry
28454  * @extends Roo.bootstrap.Component
28455  * Bootstrap Layout Masonry class
28456  * 
28457  * @constructor
28458  * Create a new Element
28459  * @param {Object} config The config object
28460  */
28461
28462 Roo.bootstrap.LayoutMasonry = function(config){
28463     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28464     
28465     this.bricks = [];
28466     
28467 };
28468
28469 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28470     
28471     /**
28472      * @cfg {Boolean} isLayoutInstant = no animation?
28473      */   
28474     isLayoutInstant : false, // needed?
28475    
28476     /**
28477      * @cfg {Number} boxWidth  width of the columns
28478      */   
28479     boxWidth : 450,
28480     
28481       /**
28482      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28483      */   
28484     boxHeight : 0,
28485     
28486     /**
28487      * @cfg {Number} padWidth padding below box..
28488      */   
28489     padWidth : 10, 
28490     
28491     /**
28492      * @cfg {Number} gutter gutter width..
28493      */   
28494     gutter : 10,
28495     
28496      /**
28497      * @cfg {Number} maxCols maximum number of columns
28498      */   
28499     
28500     maxCols: 0,
28501     
28502     /**
28503      * @cfg {Boolean} isAutoInitial defalut true
28504      */   
28505     isAutoInitial : true, 
28506     
28507     containerWidth: 0,
28508     
28509     /**
28510      * @cfg {Boolean} isHorizontal defalut false
28511      */   
28512     isHorizontal : false, 
28513
28514     currentSize : null,
28515     
28516     tag: 'div',
28517     
28518     cls: '',
28519     
28520     bricks: null, //CompositeElement
28521     
28522     cols : 1,
28523     
28524     _isLayoutInited : false,
28525     
28526 //    isAlternative : false, // only use for vertical layout...
28527     
28528     /**
28529      * @cfg {Number} alternativePadWidth padding below box..
28530      */   
28531     alternativePadWidth : 50, 
28532     
28533     getAutoCreate : function(){
28534         
28535         var cfg = {
28536             tag: this.tag,
28537             cls: 'blog-masonary-wrapper ' + this.cls,
28538             cn : {
28539                 cls : 'mas-boxes masonary'
28540             }
28541         };
28542         
28543         return cfg;
28544     },
28545     
28546     getChildContainer: function( )
28547     {
28548         if (this.boxesEl) {
28549             return this.boxesEl;
28550         }
28551         
28552         this.boxesEl = this.el.select('.mas-boxes').first();
28553         
28554         return this.boxesEl;
28555     },
28556     
28557     
28558     initEvents : function()
28559     {
28560         var _this = this;
28561         
28562         if(this.isAutoInitial){
28563             Roo.log('hook children rendered');
28564             this.on('childrenrendered', function() {
28565                 Roo.log('children rendered');
28566                 _this.initial();
28567             } ,this);
28568         }
28569     },
28570     
28571     initial : function()
28572     {
28573         this.currentSize = this.el.getBox(true);
28574         
28575         Roo.EventManager.onWindowResize(this.resize, this); 
28576
28577         if(!this.isAutoInitial){
28578             this.layout();
28579             return;
28580         }
28581         
28582         this.layout();
28583         
28584         return;
28585         //this.layout.defer(500,this);
28586         
28587     },
28588     
28589     resize : function()
28590     {
28591         Roo.log('resize');
28592         
28593         var cs = this.el.getBox(true);
28594         
28595         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28596             Roo.log("no change in with or X");
28597             return;
28598         }
28599         
28600         this.currentSize = cs;
28601         
28602         this.layout();
28603         
28604     },
28605     
28606     layout : function()
28607     {   
28608         this._resetLayout();
28609         
28610         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28611         
28612         this.layoutItems( isInstant );
28613       
28614         this._isLayoutInited = true;
28615         
28616     },
28617     
28618     _resetLayout : function()
28619     {
28620         if(this.isHorizontal){
28621             this.horizontalMeasureColumns();
28622             return;
28623         }
28624         
28625         this.verticalMeasureColumns();
28626         
28627     },
28628     
28629     verticalMeasureColumns : function()
28630     {
28631         this.getContainerWidth();
28632         
28633 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28634 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28635 //            return;
28636 //        }
28637         
28638         var boxWidth = this.boxWidth + this.padWidth;
28639         
28640         if(this.containerWidth < this.boxWidth){
28641             boxWidth = this.containerWidth
28642         }
28643         
28644         var containerWidth = this.containerWidth;
28645         
28646         var cols = Math.floor(containerWidth / boxWidth);
28647         
28648         this.cols = Math.max( cols, 1 );
28649         
28650         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28651         
28652         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28653         
28654         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28655         
28656         this.colWidth = boxWidth + avail - this.padWidth;
28657         
28658         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28659         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28660     },
28661     
28662     horizontalMeasureColumns : function()
28663     {
28664         this.getContainerWidth();
28665         
28666         var boxWidth = this.boxWidth;
28667         
28668         if(this.containerWidth < boxWidth){
28669             boxWidth = this.containerWidth;
28670         }
28671         
28672         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28673         
28674         this.el.setHeight(boxWidth);
28675         
28676     },
28677     
28678     getContainerWidth : function()
28679     {
28680         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28681     },
28682     
28683     layoutItems : function( isInstant )
28684     {
28685         var items = Roo.apply([], this.bricks);
28686         
28687         if(this.isHorizontal){
28688             this._horizontalLayoutItems( items , isInstant );
28689             return;
28690         }
28691         
28692 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28693 //            this._verticalAlternativeLayoutItems( items , isInstant );
28694 //            return;
28695 //        }
28696         
28697         this._verticalLayoutItems( items , isInstant );
28698         
28699     },
28700     
28701     _verticalLayoutItems : function ( items , isInstant)
28702     {
28703         if ( !items || !items.length ) {
28704             return;
28705         }
28706         
28707         var standard = [
28708             ['xs', 'xs', 'xs', 'tall'],
28709             ['xs', 'xs', 'tall'],
28710             ['xs', 'xs', 'sm'],
28711             ['xs', 'xs', 'xs'],
28712             ['xs', 'tall'],
28713             ['xs', 'sm'],
28714             ['xs', 'xs'],
28715             ['xs'],
28716             
28717             ['sm', 'xs', 'xs'],
28718             ['sm', 'xs'],
28719             ['sm'],
28720             
28721             ['tall', 'xs', 'xs', 'xs'],
28722             ['tall', 'xs', 'xs'],
28723             ['tall', 'xs'],
28724             ['tall']
28725             
28726         ];
28727         
28728         var queue = [];
28729         
28730         var boxes = [];
28731         
28732         var box = [];
28733         
28734         Roo.each(items, function(item, k){
28735             
28736             switch (item.size) {
28737                 // these layouts take up a full box,
28738                 case 'md' :
28739                 case 'md-left' :
28740                 case 'md-right' :
28741                 case 'wide' :
28742                     
28743                     if(box.length){
28744                         boxes.push(box);
28745                         box = [];
28746                     }
28747                     
28748                     boxes.push([item]);
28749                     
28750                     break;
28751                     
28752                 case 'xs' :
28753                 case 'sm' :
28754                 case 'tall' :
28755                     
28756                     box.push(item);
28757                     
28758                     break;
28759                 default :
28760                     break;
28761                     
28762             }
28763             
28764         }, this);
28765         
28766         if(box.length){
28767             boxes.push(box);
28768             box = [];
28769         }
28770         
28771         var filterPattern = function(box, length)
28772         {
28773             if(!box.length){
28774                 return;
28775             }
28776             
28777             var match = false;
28778             
28779             var pattern = box.slice(0, length);
28780             
28781             var format = [];
28782             
28783             Roo.each(pattern, function(i){
28784                 format.push(i.size);
28785             }, this);
28786             
28787             Roo.each(standard, function(s){
28788                 
28789                 if(String(s) != String(format)){
28790                     return;
28791                 }
28792                 
28793                 match = true;
28794                 return false;
28795                 
28796             }, this);
28797             
28798             if(!match && length == 1){
28799                 return;
28800             }
28801             
28802             if(!match){
28803                 filterPattern(box, length - 1);
28804                 return;
28805             }
28806                 
28807             queue.push(pattern);
28808
28809             box = box.slice(length, box.length);
28810
28811             filterPattern(box, 4);
28812
28813             return;
28814             
28815         }
28816         
28817         Roo.each(boxes, function(box, k){
28818             
28819             if(!box.length){
28820                 return;
28821             }
28822             
28823             if(box.length == 1){
28824                 queue.push(box);
28825                 return;
28826             }
28827             
28828             filterPattern(box, 4);
28829             
28830         }, this);
28831         
28832         this._processVerticalLayoutQueue( queue, isInstant );
28833         
28834     },
28835     
28836 //    _verticalAlternativeLayoutItems : function( items , isInstant )
28837 //    {
28838 //        if ( !items || !items.length ) {
28839 //            return;
28840 //        }
28841 //
28842 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
28843 //        
28844 //    },
28845     
28846     _horizontalLayoutItems : function ( items , isInstant)
28847     {
28848         if ( !items || !items.length || items.length < 3) {
28849             return;
28850         }
28851         
28852         items.reverse();
28853         
28854         var eItems = items.slice(0, 3);
28855         
28856         items = items.slice(3, items.length);
28857         
28858         var standard = [
28859             ['xs', 'xs', 'xs', 'wide'],
28860             ['xs', 'xs', 'wide'],
28861             ['xs', 'xs', 'sm'],
28862             ['xs', 'xs', 'xs'],
28863             ['xs', 'wide'],
28864             ['xs', 'sm'],
28865             ['xs', 'xs'],
28866             ['xs'],
28867             
28868             ['sm', 'xs', 'xs'],
28869             ['sm', 'xs'],
28870             ['sm'],
28871             
28872             ['wide', 'xs', 'xs', 'xs'],
28873             ['wide', 'xs', 'xs'],
28874             ['wide', 'xs'],
28875             ['wide'],
28876             
28877             ['wide-thin']
28878         ];
28879         
28880         var queue = [];
28881         
28882         var boxes = [];
28883         
28884         var box = [];
28885         
28886         Roo.each(items, function(item, k){
28887             
28888             switch (item.size) {
28889                 case 'md' :
28890                 case 'md-left' :
28891                 case 'md-right' :
28892                 case 'tall' :
28893                     
28894                     if(box.length){
28895                         boxes.push(box);
28896                         box = [];
28897                     }
28898                     
28899                     boxes.push([item]);
28900                     
28901                     break;
28902                     
28903                 case 'xs' :
28904                 case 'sm' :
28905                 case 'wide' :
28906                 case 'wide-thin' :
28907                     
28908                     box.push(item);
28909                     
28910                     break;
28911                 default :
28912                     break;
28913                     
28914             }
28915             
28916         }, this);
28917         
28918         if(box.length){
28919             boxes.push(box);
28920             box = [];
28921         }
28922         
28923         var filterPattern = function(box, length)
28924         {
28925             if(!box.length){
28926                 return;
28927             }
28928             
28929             var match = false;
28930             
28931             var pattern = box.slice(0, length);
28932             
28933             var format = [];
28934             
28935             Roo.each(pattern, function(i){
28936                 format.push(i.size);
28937             }, this);
28938             
28939             Roo.each(standard, function(s){
28940                 
28941                 if(String(s) != String(format)){
28942                     return;
28943                 }
28944                 
28945                 match = true;
28946                 return false;
28947                 
28948             }, this);
28949             
28950             if(!match && length == 1){
28951                 return;
28952             }
28953             
28954             if(!match){
28955                 filterPattern(box, length - 1);
28956                 return;
28957             }
28958                 
28959             queue.push(pattern);
28960
28961             box = box.slice(length, box.length);
28962
28963             filterPattern(box, 4);
28964
28965             return;
28966             
28967         }
28968         
28969         Roo.each(boxes, function(box, k){
28970             
28971             if(!box.length){
28972                 return;
28973             }
28974             
28975             if(box.length == 1){
28976                 queue.push(box);
28977                 return;
28978             }
28979             
28980             filterPattern(box, 4);
28981             
28982         }, this);
28983         
28984         
28985         var prune = [];
28986         
28987         var pos = this.el.getBox(true);
28988         
28989         var minX = pos.x;
28990         
28991         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
28992         
28993         var hit_end = false;
28994         
28995         Roo.each(queue, function(box){
28996             
28997             if(hit_end){
28998                 
28999                 Roo.each(box, function(b){
29000                 
29001                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29002                     b.el.hide();
29003
29004                 }, this);
29005
29006                 return;
29007             }
29008             
29009             var mx = 0;
29010             
29011             Roo.each(box, function(b){
29012                 
29013                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29014                 b.el.show();
29015
29016                 mx = Math.max(mx, b.x);
29017                 
29018             }, this);
29019             
29020             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29021             
29022             if(maxX < minX){
29023                 
29024                 Roo.each(box, function(b){
29025                 
29026                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29027                     b.el.hide();
29028                     
29029                 }, this);
29030                 
29031                 hit_end = true;
29032                 
29033                 return;
29034             }
29035             
29036             prune.push(box);
29037             
29038         }, this);
29039         
29040         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29041     },
29042     
29043     /** Sets position of item in DOM
29044     * @param {Element} item
29045     * @param {Number} x - horizontal position
29046     * @param {Number} y - vertical position
29047     * @param {Boolean} isInstant - disables transitions
29048     */
29049     _processVerticalLayoutQueue : function( queue, isInstant )
29050     {
29051         var pos = this.el.getBox(true);
29052         var x = pos.x;
29053         var y = pos.y;
29054         var maxY = [];
29055         
29056         for (var i = 0; i < this.cols; i++){
29057             maxY[i] = pos.y;
29058         }
29059         
29060         Roo.each(queue, function(box, k){
29061             
29062             var col = k % this.cols;
29063             
29064             Roo.each(box, function(b,kk){
29065                 
29066                 b.el.position('absolute');
29067                 
29068                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29069                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29070                 
29071                 if(b.size == 'md-left' || b.size == 'md-right'){
29072                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29073                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29074                 }
29075                 
29076                 b.el.setWidth(width);
29077                 b.el.setHeight(height);
29078                 // iframe?
29079                 b.el.select('iframe',true).setSize(width,height);
29080                 
29081             }, this);
29082             
29083             for (var i = 0; i < this.cols; i++){
29084                 
29085                 if(maxY[i] < maxY[col]){
29086                     col = i;
29087                     continue;
29088                 }
29089                 
29090                 col = Math.min(col, i);
29091                 
29092             }
29093             
29094             x = pos.x + col * (this.colWidth + this.padWidth);
29095             
29096             y = maxY[col];
29097             
29098             var positions = [];
29099             
29100             switch (box.length){
29101                 case 1 :
29102                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29103                     break;
29104                 case 2 :
29105                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29106                     break;
29107                 case 3 :
29108                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29109                     break;
29110                 case 4 :
29111                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29112                     break;
29113                 default :
29114                     break;
29115             }
29116             
29117             Roo.each(box, function(b,kk){
29118                 
29119                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29120                 
29121                 var sz = b.el.getSize();
29122                 
29123                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29124                 
29125             }, this);
29126             
29127         }, this);
29128         
29129         var mY = 0;
29130         
29131         for (var i = 0; i < this.cols; i++){
29132             mY = Math.max(mY, maxY[i]);
29133         }
29134         
29135         this.el.setHeight(mY - pos.y);
29136         
29137     },
29138     
29139 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29140 //    {
29141 //        var pos = this.el.getBox(true);
29142 //        var x = pos.x;
29143 //        var y = pos.y;
29144 //        var maxX = pos.right;
29145 //        
29146 //        var maxHeight = 0;
29147 //        
29148 //        Roo.each(items, function(item, k){
29149 //            
29150 //            var c = k % 2;
29151 //            
29152 //            item.el.position('absolute');
29153 //                
29154 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29155 //
29156 //            item.el.setWidth(width);
29157 //
29158 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29159 //
29160 //            item.el.setHeight(height);
29161 //            
29162 //            if(c == 0){
29163 //                item.el.setXY([x, y], isInstant ? false : true);
29164 //            } else {
29165 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29166 //            }
29167 //            
29168 //            y = y + height + this.alternativePadWidth;
29169 //            
29170 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29171 //            
29172 //        }, this);
29173 //        
29174 //        this.el.setHeight(maxHeight);
29175 //        
29176 //    },
29177     
29178     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29179     {
29180         var pos = this.el.getBox(true);
29181         
29182         var minX = pos.x;
29183         var minY = pos.y;
29184         
29185         var maxX = pos.right;
29186         
29187         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29188         
29189         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29190         
29191         Roo.each(queue, function(box, k){
29192             
29193             Roo.each(box, function(b, kk){
29194                 
29195                 b.el.position('absolute');
29196                 
29197                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29198                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29199                 
29200                 if(b.size == 'md-left' || b.size == 'md-right'){
29201                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29202                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29203                 }
29204                 
29205                 b.el.setWidth(width);
29206                 b.el.setHeight(height);
29207                 
29208             }, this);
29209             
29210             if(!box.length){
29211                 return;
29212             }
29213             
29214             var positions = [];
29215             
29216             switch (box.length){
29217                 case 1 :
29218                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29219                     break;
29220                 case 2 :
29221                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29222                     break;
29223                 case 3 :
29224                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29225                     break;
29226                 case 4 :
29227                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29228                     break;
29229                 default :
29230                     break;
29231             }
29232             
29233             Roo.each(box, function(b,kk){
29234                 
29235                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29236                 
29237                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29238                 
29239             }, this);
29240             
29241         }, this);
29242         
29243     },
29244     
29245     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29246     {
29247         Roo.each(eItems, function(b,k){
29248             
29249             b.size = (k == 0) ? 'sm' : 'xs';
29250             b.x = (k == 0) ? 2 : 1;
29251             b.y = (k == 0) ? 2 : 1;
29252             
29253             b.el.position('absolute');
29254             
29255             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29256                 
29257             b.el.setWidth(width);
29258             
29259             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29260             
29261             b.el.setHeight(height);
29262             
29263         }, this);
29264
29265         var positions = [];
29266         
29267         positions.push({
29268             x : maxX - this.unitWidth * 2 - this.gutter,
29269             y : minY
29270         });
29271         
29272         positions.push({
29273             x : maxX - this.unitWidth,
29274             y : minY + (this.unitWidth + this.gutter) * 2
29275         });
29276         
29277         positions.push({
29278             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29279             y : minY
29280         });
29281         
29282         Roo.each(eItems, function(b,k){
29283             
29284             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29285
29286         }, this);
29287         
29288     },
29289     
29290     getVerticalOneBoxColPositions : function(x, y, box)
29291     {
29292         var pos = [];
29293         
29294         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29295         
29296         if(box[0].size == 'md-left'){
29297             rand = 0;
29298         }
29299         
29300         if(box[0].size == 'md-right'){
29301             rand = 1;
29302         }
29303         
29304         pos.push({
29305             x : x + (this.unitWidth + this.gutter) * rand,
29306             y : y
29307         });
29308         
29309         return pos;
29310     },
29311     
29312     getVerticalTwoBoxColPositions : function(x, y, box)
29313     {
29314         var pos = [];
29315         
29316         if(box[0].size == 'xs'){
29317             
29318             pos.push({
29319                 x : x,
29320                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29321             });
29322
29323             pos.push({
29324                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29325                 y : y
29326             });
29327             
29328             return pos;
29329             
29330         }
29331         
29332         pos.push({
29333             x : x,
29334             y : y
29335         });
29336
29337         pos.push({
29338             x : x + (this.unitWidth + this.gutter) * 2,
29339             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29340         });
29341         
29342         return pos;
29343         
29344     },
29345     
29346     getVerticalThreeBoxColPositions : function(x, y, box)
29347     {
29348         var pos = [];
29349         
29350         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29351             
29352             pos.push({
29353                 x : x,
29354                 y : y
29355             });
29356
29357             pos.push({
29358                 x : x + (this.unitWidth + this.gutter) * 1,
29359                 y : y
29360             });
29361             
29362             pos.push({
29363                 x : x + (this.unitWidth + this.gutter) * 2,
29364                 y : y
29365             });
29366             
29367             return pos;
29368             
29369         }
29370         
29371         if(box[0].size == 'xs' && box[1].size == 'xs'){
29372             
29373             pos.push({
29374                 x : x,
29375                 y : y
29376             });
29377
29378             pos.push({
29379                 x : x,
29380                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29381             });
29382             
29383             pos.push({
29384                 x : x + (this.unitWidth + this.gutter) * 1,
29385                 y : y
29386             });
29387             
29388             return pos;
29389             
29390         }
29391         
29392         pos.push({
29393             x : x,
29394             y : y
29395         });
29396
29397         pos.push({
29398             x : x + (this.unitWidth + this.gutter) * 2,
29399             y : y
29400         });
29401
29402         pos.push({
29403             x : x + (this.unitWidth + this.gutter) * 2,
29404             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29405         });
29406             
29407         return pos;
29408         
29409     },
29410     
29411     getVerticalFourBoxColPositions : function(x, y, box)
29412     {
29413         var pos = [];
29414         
29415         if(box[0].size == 'xs'){
29416             
29417             pos.push({
29418                 x : x,
29419                 y : y
29420             });
29421
29422             pos.push({
29423                 x : x,
29424                 y : y + (this.unitHeight + this.gutter) * 1
29425             });
29426             
29427             pos.push({
29428                 x : x,
29429                 y : y + (this.unitHeight + this.gutter) * 2
29430             });
29431             
29432             pos.push({
29433                 x : x + (this.unitWidth + this.gutter) * 1,
29434                 y : y
29435             });
29436             
29437             return pos;
29438             
29439         }
29440         
29441         pos.push({
29442             x : x,
29443             y : y
29444         });
29445
29446         pos.push({
29447             x : x + (this.unitWidth + this.gutter) * 2,
29448             y : y
29449         });
29450
29451         pos.push({
29452             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29453             y : y + (this.unitHeight + this.gutter) * 1
29454         });
29455
29456         pos.push({
29457             x : x + (this.unitWidth + this.gutter) * 2,
29458             y : y + (this.unitWidth + this.gutter) * 2
29459         });
29460
29461         return pos;
29462         
29463     },
29464     
29465     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29466     {
29467         var pos = [];
29468         
29469         if(box[0].size == 'md-left'){
29470             pos.push({
29471                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29472                 y : minY
29473             });
29474             
29475             return pos;
29476         }
29477         
29478         if(box[0].size == 'md-right'){
29479             pos.push({
29480                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29481                 y : minY + (this.unitWidth + this.gutter) * 1
29482             });
29483             
29484             return pos;
29485         }
29486         
29487         var rand = Math.floor(Math.random() * (4 - box[0].y));
29488         
29489         pos.push({
29490             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29491             y : minY + (this.unitWidth + this.gutter) * rand
29492         });
29493         
29494         return pos;
29495         
29496     },
29497     
29498     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29499     {
29500         var pos = [];
29501         
29502         if(box[0].size == 'xs'){
29503             
29504             pos.push({
29505                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29506                 y : minY
29507             });
29508
29509             pos.push({
29510                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29511                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29512             });
29513             
29514             return pos;
29515             
29516         }
29517         
29518         pos.push({
29519             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29520             y : minY
29521         });
29522
29523         pos.push({
29524             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29525             y : minY + (this.unitWidth + this.gutter) * 2
29526         });
29527         
29528         return pos;
29529         
29530     },
29531     
29532     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29533     {
29534         var pos = [];
29535         
29536         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29537             
29538             pos.push({
29539                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29540                 y : minY
29541             });
29542
29543             pos.push({
29544                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29545                 y : minY + (this.unitWidth + this.gutter) * 1
29546             });
29547             
29548             pos.push({
29549                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29550                 y : minY + (this.unitWidth + this.gutter) * 2
29551             });
29552             
29553             return pos;
29554             
29555         }
29556         
29557         if(box[0].size == 'xs' && box[1].size == 'xs'){
29558             
29559             pos.push({
29560                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29561                 y : minY
29562             });
29563
29564             pos.push({
29565                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29566                 y : minY
29567             });
29568             
29569             pos.push({
29570                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29571                 y : minY + (this.unitWidth + this.gutter) * 1
29572             });
29573             
29574             return pos;
29575             
29576         }
29577         
29578         pos.push({
29579             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29580             y : minY
29581         });
29582
29583         pos.push({
29584             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29585             y : minY + (this.unitWidth + this.gutter) * 2
29586         });
29587
29588         pos.push({
29589             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29590             y : minY + (this.unitWidth + this.gutter) * 2
29591         });
29592             
29593         return pos;
29594         
29595     },
29596     
29597     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29598     {
29599         var pos = [];
29600         
29601         if(box[0].size == 'xs'){
29602             
29603             pos.push({
29604                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29605                 y : minY
29606             });
29607
29608             pos.push({
29609                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29610                 y : minY
29611             });
29612             
29613             pos.push({
29614                 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),
29615                 y : minY
29616             });
29617             
29618             pos.push({
29619                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29620                 y : minY + (this.unitWidth + this.gutter) * 1
29621             });
29622             
29623             return pos;
29624             
29625         }
29626         
29627         pos.push({
29628             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29629             y : minY
29630         });
29631         
29632         pos.push({
29633             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29634             y : minY + (this.unitWidth + this.gutter) * 2
29635         });
29636         
29637         pos.push({
29638             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29639             y : minY + (this.unitWidth + this.gutter) * 2
29640         });
29641         
29642         pos.push({
29643             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),
29644             y : minY + (this.unitWidth + this.gutter) * 2
29645         });
29646
29647         return pos;
29648         
29649     }
29650     
29651 });
29652
29653  
29654
29655  /**
29656  *
29657  * This is based on 
29658  * http://masonry.desandro.com
29659  *
29660  * The idea is to render all the bricks based on vertical width...
29661  *
29662  * The original code extends 'outlayer' - we might need to use that....
29663  * 
29664  */
29665
29666
29667 /**
29668  * @class Roo.bootstrap.LayoutMasonryAuto
29669  * @extends Roo.bootstrap.Component
29670  * Bootstrap Layout Masonry class
29671  * 
29672  * @constructor
29673  * Create a new Element
29674  * @param {Object} config The config object
29675  */
29676
29677 Roo.bootstrap.LayoutMasonryAuto = function(config){
29678     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29679 };
29680
29681 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
29682     
29683       /**
29684      * @cfg {Boolean} isFitWidth  - resize the width..
29685      */   
29686     isFitWidth : false,  // options..
29687     /**
29688      * @cfg {Boolean} isOriginLeft = left align?
29689      */   
29690     isOriginLeft : true,
29691     /**
29692      * @cfg {Boolean} isOriginTop = top align?
29693      */   
29694     isOriginTop : false,
29695     /**
29696      * @cfg {Boolean} isLayoutInstant = no animation?
29697      */   
29698     isLayoutInstant : false, // needed?
29699     /**
29700      * @cfg {Boolean} isResizingContainer = not sure if this is used..
29701      */   
29702     isResizingContainer : true,
29703     /**
29704      * @cfg {Number} columnWidth  width of the columns 
29705      */   
29706     
29707     columnWidth : 0,
29708     
29709     /**
29710      * @cfg {Number} maxCols maximum number of columns
29711      */   
29712     
29713     maxCols: 0,
29714     /**
29715      * @cfg {Number} padHeight padding below box..
29716      */   
29717     
29718     padHeight : 10, 
29719     
29720     /**
29721      * @cfg {Boolean} isAutoInitial defalut true
29722      */   
29723     
29724     isAutoInitial : true, 
29725     
29726     // private?
29727     gutter : 0,
29728     
29729     containerWidth: 0,
29730     initialColumnWidth : 0,
29731     currentSize : null,
29732     
29733     colYs : null, // array.
29734     maxY : 0,
29735     padWidth: 10,
29736     
29737     
29738     tag: 'div',
29739     cls: '',
29740     bricks: null, //CompositeElement
29741     cols : 0, // array?
29742     // element : null, // wrapped now this.el
29743     _isLayoutInited : null, 
29744     
29745     
29746     getAutoCreate : function(){
29747         
29748         var cfg = {
29749             tag: this.tag,
29750             cls: 'blog-masonary-wrapper ' + this.cls,
29751             cn : {
29752                 cls : 'mas-boxes masonary'
29753             }
29754         };
29755         
29756         return cfg;
29757     },
29758     
29759     getChildContainer: function( )
29760     {
29761         if (this.boxesEl) {
29762             return this.boxesEl;
29763         }
29764         
29765         this.boxesEl = this.el.select('.mas-boxes').first();
29766         
29767         return this.boxesEl;
29768     },
29769     
29770     
29771     initEvents : function()
29772     {
29773         var _this = this;
29774         
29775         if(this.isAutoInitial){
29776             Roo.log('hook children rendered');
29777             this.on('childrenrendered', function() {
29778                 Roo.log('children rendered');
29779                 _this.initial();
29780             } ,this);
29781         }
29782         
29783     },
29784     
29785     initial : function()
29786     {
29787         this.reloadItems();
29788
29789         this.currentSize = this.el.getBox(true);
29790
29791         /// was window resize... - let's see if this works..
29792         Roo.EventManager.onWindowResize(this.resize, this); 
29793
29794         if(!this.isAutoInitial){
29795             this.layout();
29796             return;
29797         }
29798         
29799         this.layout.defer(500,this);
29800     },
29801     
29802     reloadItems: function()
29803     {
29804         this.bricks = this.el.select('.masonry-brick', true);
29805         
29806         this.bricks.each(function(b) {
29807             //Roo.log(b.getSize());
29808             if (!b.attr('originalwidth')) {
29809                 b.attr('originalwidth',  b.getSize().width);
29810             }
29811             
29812         });
29813         
29814         Roo.log(this.bricks.elements.length);
29815     },
29816     
29817     resize : function()
29818     {
29819         Roo.log('resize');
29820         var cs = this.el.getBox(true);
29821         
29822         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29823             Roo.log("no change in with or X");
29824             return;
29825         }
29826         this.currentSize = cs;
29827         this.layout();
29828     },
29829     
29830     layout : function()
29831     {
29832          Roo.log('layout');
29833         this._resetLayout();
29834         //this._manageStamps();
29835       
29836         // don't animate first layout
29837         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29838         this.layoutItems( isInstant );
29839       
29840         // flag for initalized
29841         this._isLayoutInited = true;
29842     },
29843     
29844     layoutItems : function( isInstant )
29845     {
29846         //var items = this._getItemsForLayout( this.items );
29847         // original code supports filtering layout items.. we just ignore it..
29848         
29849         this._layoutItems( this.bricks , isInstant );
29850       
29851         this._postLayout();
29852     },
29853     _layoutItems : function ( items , isInstant)
29854     {
29855        //this.fireEvent( 'layout', this, items );
29856     
29857
29858         if ( !items || !items.elements.length ) {
29859           // no items, emit event with empty array
29860             return;
29861         }
29862
29863         var queue = [];
29864         items.each(function(item) {
29865             Roo.log("layout item");
29866             Roo.log(item);
29867             // get x/y object from method
29868             var position = this._getItemLayoutPosition( item );
29869             // enqueue
29870             position.item = item;
29871             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29872             queue.push( position );
29873         }, this);
29874       
29875         this._processLayoutQueue( queue );
29876     },
29877     /** Sets position of item in DOM
29878     * @param {Element} item
29879     * @param {Number} x - horizontal position
29880     * @param {Number} y - vertical position
29881     * @param {Boolean} isInstant - disables transitions
29882     */
29883     _processLayoutQueue : function( queue )
29884     {
29885         for ( var i=0, len = queue.length; i < len; i++ ) {
29886             var obj = queue[i];
29887             obj.item.position('absolute');
29888             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29889         }
29890     },
29891       
29892     
29893     /**
29894     * Any logic you want to do after each layout,
29895     * i.e. size the container
29896     */
29897     _postLayout : function()
29898     {
29899         this.resizeContainer();
29900     },
29901     
29902     resizeContainer : function()
29903     {
29904         if ( !this.isResizingContainer ) {
29905             return;
29906         }
29907         var size = this._getContainerSize();
29908         if ( size ) {
29909             this.el.setSize(size.width,size.height);
29910             this.boxesEl.setSize(size.width,size.height);
29911         }
29912     },
29913     
29914     
29915     
29916     _resetLayout : function()
29917     {
29918         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
29919         this.colWidth = this.el.getWidth();
29920         //this.gutter = this.el.getWidth(); 
29921         
29922         this.measureColumns();
29923
29924         // reset column Y
29925         var i = this.cols;
29926         this.colYs = [];
29927         while (i--) {
29928             this.colYs.push( 0 );
29929         }
29930     
29931         this.maxY = 0;
29932     },
29933
29934     measureColumns : function()
29935     {
29936         this.getContainerWidth();
29937       // if columnWidth is 0, default to outerWidth of first item
29938         if ( !this.columnWidth ) {
29939             var firstItem = this.bricks.first();
29940             Roo.log(firstItem);
29941             this.columnWidth  = this.containerWidth;
29942             if (firstItem && firstItem.attr('originalwidth') ) {
29943                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
29944             }
29945             // columnWidth fall back to item of first element
29946             Roo.log("set column width?");
29947                         this.initialColumnWidth = this.columnWidth  ;
29948
29949             // if first elem has no width, default to size of container
29950             
29951         }
29952         
29953         
29954         if (this.initialColumnWidth) {
29955             this.columnWidth = this.initialColumnWidth;
29956         }
29957         
29958         
29959             
29960         // column width is fixed at the top - however if container width get's smaller we should
29961         // reduce it...
29962         
29963         // this bit calcs how man columns..
29964             
29965         var columnWidth = this.columnWidth += this.gutter;
29966       
29967         // calculate columns
29968         var containerWidth = this.containerWidth + this.gutter;
29969         
29970         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
29971         // fix rounding errors, typically with gutters
29972         var excess = columnWidth - containerWidth % columnWidth;
29973         
29974         
29975         // if overshoot is less than a pixel, round up, otherwise floor it
29976         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
29977         cols = Math[ mathMethod ]( cols );
29978         this.cols = Math.max( cols, 1 );
29979         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29980         
29981          // padding positioning..
29982         var totalColWidth = this.cols * this.columnWidth;
29983         var padavail = this.containerWidth - totalColWidth;
29984         // so for 2 columns - we need 3 'pads'
29985         
29986         var padNeeded = (1+this.cols) * this.padWidth;
29987         
29988         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
29989         
29990         this.columnWidth += padExtra
29991         //this.padWidth = Math.floor(padavail /  ( this.cols));
29992         
29993         // adjust colum width so that padding is fixed??
29994         
29995         // we have 3 columns ... total = width * 3
29996         // we have X left over... that should be used by 
29997         
29998         //if (this.expandC) {
29999             
30000         //}
30001         
30002         
30003         
30004     },
30005     
30006     getContainerWidth : function()
30007     {
30008        /* // container is parent if fit width
30009         var container = this.isFitWidth ? this.element.parentNode : this.element;
30010         // check that this.size and size are there
30011         // IE8 triggers resize on body size change, so they might not be
30012         
30013         var size = getSize( container );  //FIXME
30014         this.containerWidth = size && size.innerWidth; //FIXME
30015         */
30016          
30017         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30018         
30019     },
30020     
30021     _getItemLayoutPosition : function( item )  // what is item?
30022     {
30023         // we resize the item to our columnWidth..
30024       
30025         item.setWidth(this.columnWidth);
30026         item.autoBoxAdjust  = false;
30027         
30028         var sz = item.getSize();
30029  
30030         // how many columns does this brick span
30031         var remainder = this.containerWidth % this.columnWidth;
30032         
30033         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30034         // round if off by 1 pixel, otherwise use ceil
30035         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30036         colSpan = Math.min( colSpan, this.cols );
30037         
30038         // normally this should be '1' as we dont' currently allow multi width columns..
30039         
30040         var colGroup = this._getColGroup( colSpan );
30041         // get the minimum Y value from the columns
30042         var minimumY = Math.min.apply( Math, colGroup );
30043         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30044         
30045         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30046          
30047         // position the brick
30048         var position = {
30049             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30050             y: this.currentSize.y + minimumY + this.padHeight
30051         };
30052         
30053         Roo.log(position);
30054         // apply setHeight to necessary columns
30055         var setHeight = minimumY + sz.height + this.padHeight;
30056         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30057         
30058         var setSpan = this.cols + 1 - colGroup.length;
30059         for ( var i = 0; i < setSpan; i++ ) {
30060           this.colYs[ shortColIndex + i ] = setHeight ;
30061         }
30062       
30063         return position;
30064     },
30065     
30066     /**
30067      * @param {Number} colSpan - number of columns the element spans
30068      * @returns {Array} colGroup
30069      */
30070     _getColGroup : function( colSpan )
30071     {
30072         if ( colSpan < 2 ) {
30073           // if brick spans only one column, use all the column Ys
30074           return this.colYs;
30075         }
30076       
30077         var colGroup = [];
30078         // how many different places could this brick fit horizontally
30079         var groupCount = this.cols + 1 - colSpan;
30080         // for each group potential horizontal position
30081         for ( var i = 0; i < groupCount; i++ ) {
30082           // make an array of colY values for that one group
30083           var groupColYs = this.colYs.slice( i, i + colSpan );
30084           // and get the max value of the array
30085           colGroup[i] = Math.max.apply( Math, groupColYs );
30086         }
30087         return colGroup;
30088     },
30089     /*
30090     _manageStamp : function( stamp )
30091     {
30092         var stampSize =  stamp.getSize();
30093         var offset = stamp.getBox();
30094         // get the columns that this stamp affects
30095         var firstX = this.isOriginLeft ? offset.x : offset.right;
30096         var lastX = firstX + stampSize.width;
30097         var firstCol = Math.floor( firstX / this.columnWidth );
30098         firstCol = Math.max( 0, firstCol );
30099         
30100         var lastCol = Math.floor( lastX / this.columnWidth );
30101         // lastCol should not go over if multiple of columnWidth #425
30102         lastCol -= lastX % this.columnWidth ? 0 : 1;
30103         lastCol = Math.min( this.cols - 1, lastCol );
30104         
30105         // set colYs to bottom of the stamp
30106         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30107             stampSize.height;
30108             
30109         for ( var i = firstCol; i <= lastCol; i++ ) {
30110           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30111         }
30112     },
30113     */
30114     
30115     _getContainerSize : function()
30116     {
30117         this.maxY = Math.max.apply( Math, this.colYs );
30118         var size = {
30119             height: this.maxY
30120         };
30121       
30122         if ( this.isFitWidth ) {
30123             size.width = this._getContainerFitWidth();
30124         }
30125       
30126         return size;
30127     },
30128     
30129     _getContainerFitWidth : function()
30130     {
30131         var unusedCols = 0;
30132         // count unused columns
30133         var i = this.cols;
30134         while ( --i ) {
30135           if ( this.colYs[i] !== 0 ) {
30136             break;
30137           }
30138           unusedCols++;
30139         }
30140         // fit container to columns that have been used
30141         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30142     },
30143     
30144     needsResizeLayout : function()
30145     {
30146         var previousWidth = this.containerWidth;
30147         this.getContainerWidth();
30148         return previousWidth !== this.containerWidth;
30149     }
30150  
30151 });
30152
30153  
30154
30155  /*
30156  * - LGPL
30157  *
30158  * element
30159  * 
30160  */
30161
30162 /**
30163  * @class Roo.bootstrap.MasonryBrick
30164  * @extends Roo.bootstrap.Component
30165  * Bootstrap MasonryBrick class
30166  * 
30167  * @constructor
30168  * Create a new MasonryBrick
30169  * @param {Object} config The config object
30170  */
30171
30172 Roo.bootstrap.MasonryBrick = function(config){
30173     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30174     
30175     this.addEvents({
30176         // raw events
30177         /**
30178          * @event click
30179          * When a MasonryBrick is clcik
30180          * @param {Roo.bootstrap.MasonryBrick} this
30181          * @param {Roo.EventObject} e
30182          */
30183         "click" : true
30184     });
30185 };
30186
30187 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30188     
30189     /**
30190      * @cfg {String} title
30191      */   
30192     title : '',
30193     /**
30194      * @cfg {String} html
30195      */   
30196     html : '',
30197     /**
30198      * @cfg {String} bgimage
30199      */   
30200     bgimage : '',
30201     /**
30202      * @cfg {String} videourl
30203      */   
30204     videourl : '',
30205     /**
30206      * @cfg {String} cls
30207      */   
30208     cls : '',
30209     /**
30210      * @cfg {String} href
30211      */   
30212     href : '',
30213     /**
30214      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30215      */   
30216     size : 'xs',
30217     
30218     /**
30219      * @cfg {String} (center|bottom) placetitle
30220      */   
30221     placetitle : '',
30222     
30223     getAutoCreate : function()
30224     {
30225         var cls = 'masonry-brick';
30226         
30227         if(this.href.length){
30228             cls += ' masonry-brick-link';
30229         }
30230         
30231         if(this.bgimage.length){
30232             cls += ' masonry-brick-image';
30233         }
30234         
30235         if(this.size){
30236             cls += ' masonry-' + this.size + '-brick';
30237         }
30238         
30239         if(this.placetitle.length){
30240             
30241             switch (this.placetitle) {
30242                 case 'center' :
30243                     cls += ' masonry-center-title';
30244                     break;
30245                 case 'bottom' :
30246                     cls += ' masonry-bottom-title';
30247                     break;
30248                 default:
30249                     break;
30250             }
30251             
30252         } else {
30253             if(!this.html.length && !this.bgimage.length){
30254                 cls += ' masonry-center-title';
30255             }
30256
30257             if(!this.html.length && this.bgimage.length){
30258                 cls += ' masonry-bottom-title';
30259             }
30260         }
30261         
30262         if(this.cls){
30263             cls += ' ' + this.cls;
30264         }
30265         
30266         var cfg = {
30267             tag: (this.href.length) ? 'a' : 'div',
30268             cls: cls,
30269             cn: [
30270                 {
30271                     tag: 'div',
30272                     cls: 'masonry-brick-paragraph',
30273                     cn: []
30274                 }
30275             ]
30276         };
30277         
30278         if(this.href.length){
30279             cfg.href = this.href;
30280         }
30281         
30282         var cn = cfg.cn[0].cn;
30283         
30284         if(this.title.length){
30285             cn.push({
30286                 tag: 'h4',
30287                 cls: 'masonry-brick-title',
30288                 html: this.title
30289             });
30290         }
30291         
30292         if(this.html.length){
30293             cn.push({
30294                 tag: 'p',
30295                 cls: 'masonry-brick-text',
30296                 html: this.html
30297             });
30298         }  
30299         if (!this.title.length && !this.html.length) {
30300             cfg.cn[0].cls += ' hide';
30301         }
30302         
30303         if(this.bgimage.length){
30304             cfg.cn.push({
30305                 tag: 'img',
30306                 cls: 'masonry-brick-image-view',
30307                 src: this.bgimage
30308             });
30309         }
30310         if(this.videourl.length){
30311             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30312             // youtube support only?
30313             cfg.cn.push({
30314                 tag: 'iframe',
30315                 cls: 'masonry-brick-image-view',
30316                 src: vurl,
30317                 frameborder : 0,
30318                 allowfullscreen : true
30319             });
30320             
30321             
30322         }
30323         return cfg;
30324         
30325     },
30326     
30327     initEvents: function() 
30328     {
30329         switch (this.size) {
30330             case 'xs' :
30331 //                this.intSize = 1;
30332                 this.x = 1;
30333                 this.y = 1;
30334                 break;
30335             case 'sm' :
30336 //                this.intSize = 2;
30337                 this.x = 2;
30338                 this.y = 2;
30339                 break;
30340             case 'md' :
30341             case 'md-left' :
30342             case 'md-right' :
30343 //                this.intSize = 3;
30344                 this.x = 3;
30345                 this.y = 3;
30346                 break;
30347             case 'tall' :
30348 //                this.intSize = 3;
30349                 this.x = 2;
30350                 this.y = 3;
30351                 break;
30352             case 'wide' :
30353 //                this.intSize = 3;
30354                 this.x = 3;
30355                 this.y = 2;
30356                 break;
30357             case 'wide-thin' :
30358 //                this.intSize = 3;
30359                 this.x = 3;
30360                 this.y = 1;
30361                 break;
30362                         
30363             default :
30364                 break;
30365         }
30366         
30367         
30368         
30369         if(Roo.isTouch){
30370             this.el.on('touchstart', this.onTouchStart, this);
30371             this.el.on('touchmove', this.onTouchMove, this);
30372             this.el.on('touchend', this.onTouchEnd, this);
30373             this.el.on('contextmenu', this.onContextMenu, this);
30374         } else {
30375             this.el.on('mouseenter'  ,this.enter, this);
30376             this.el.on('mouseleave', this.leave, this);
30377         }
30378         
30379         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30380             this.parent().bricks.push(this);   
30381         }
30382         
30383     },
30384     
30385     onClick: function(e, el)
30386     {
30387        // alert('click');
30388         
30389         if(!Roo.isTouch){
30390             return;
30391         }
30392         
30393         var time = this.endTimer - this.startTimer;
30394         
30395         //alert(time);
30396         
30397         if(time < 1000){
30398             return;
30399         }
30400         
30401         e.preventDefault();
30402     },
30403     
30404     enter: function(e, el)
30405     {
30406         e.preventDefault();
30407         
30408         if(this.bgimage.length && this.html.length){
30409             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30410         }
30411     },
30412     
30413     leave: function(e, el)
30414     {
30415         e.preventDefault();
30416         
30417         if(this.bgimage.length && this.html.length){
30418             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30419         }
30420     },
30421     
30422     onTouchStart: function(e, el)
30423     {
30424 //        e.preventDefault();
30425         
30426         if(!this.bgimage.length || !this.html.length){
30427             return;
30428         }
30429         
30430         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30431         
30432         this.timer = new Date().getTime();
30433         
30434         this.touchmoved = false;
30435     },
30436     
30437     onTouchMove: function(e, el)
30438     {
30439         this.touchmoved = true;
30440     },
30441     onContextMenu : function(e,el)
30442     {
30443             e.preventDefault();
30444             e.stopPropagation();
30445             return false;
30446     },
30447     
30448     
30449     onTouchEnd: function(e, el)
30450     {
30451 //        e.preventDefault();
30452         
30453         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30454             
30455             this.leave(e,el);
30456             
30457             return;
30458         }
30459         
30460         if(!this.bgimage.length || !this.html.length){
30461             
30462             if(this.href.length){
30463                 window.location.href = this.href;
30464             }
30465             
30466             return;
30467         }
30468         
30469         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30470         
30471         window.location.href = this.href;
30472     }
30473     
30474 });
30475
30476  
30477
30478  /*
30479  * - LGPL
30480  *
30481  * element
30482  * 
30483  */
30484
30485 /**
30486  * @class Roo.bootstrap.Brick
30487  * @extends Roo.bootstrap.Component
30488  * Bootstrap Brick class
30489  * 
30490  * @constructor
30491  * Create a new Brick
30492  * @param {Object} config The config object
30493  */
30494
30495 Roo.bootstrap.Brick = function(config){
30496     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30497     
30498     this.addEvents({
30499         // raw events
30500         /**
30501          * @event click
30502          * When a Brick is click
30503          * @param {Roo.bootstrap.Brick} this
30504          * @param {Roo.EventObject} e
30505          */
30506         "click" : true
30507     });
30508 };
30509
30510 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30511     
30512     /**
30513      * @cfg {String} title
30514      */   
30515     title : '',
30516     /**
30517      * @cfg {String} html
30518      */   
30519     html : '',
30520     /**
30521      * @cfg {String} bgimage
30522      */   
30523     bgimage : '',
30524     /**
30525      * @cfg {String} cls
30526      */   
30527     cls : '',
30528     /**
30529      * @cfg {String} href
30530      */   
30531     href : '',
30532     /**
30533      * @cfg {String} video
30534      */   
30535     video : '',
30536     /**
30537      * @cfg {Boolean} square
30538      */   
30539     square : true,
30540     
30541     getAutoCreate : function()
30542     {
30543         var cls = 'roo-brick';
30544         
30545         if(this.href.length){
30546             cls += ' roo-brick-link';
30547         }
30548         
30549         if(this.bgimage.length){
30550             cls += ' roo-brick-image';
30551         }
30552         
30553         if(!this.html.length && !this.bgimage.length){
30554             cls += ' roo-brick-center-title';
30555         }
30556         
30557         if(!this.html.length && this.bgimage.length){
30558             cls += ' roo-brick-bottom-title';
30559         }
30560         
30561         if(this.cls){
30562             cls += ' ' + this.cls;
30563         }
30564         
30565         var cfg = {
30566             tag: (this.href.length) ? 'a' : 'div',
30567             cls: cls,
30568             cn: [
30569                 {
30570                     tag: 'div',
30571                     cls: 'roo-brick-paragraph',
30572                     cn: []
30573                 }
30574             ]
30575         };
30576         
30577         if(this.href.length){
30578             cfg.href = this.href;
30579         }
30580         
30581         var cn = cfg.cn[0].cn;
30582         
30583         if(this.title.length){
30584             cn.push({
30585                 tag: 'h4',
30586                 cls: 'roo-brick-title',
30587                 html: this.title
30588             });
30589         }
30590         
30591         if(this.html.length){
30592             cn.push({
30593                 tag: 'p',
30594                 cls: 'roo-brick-text',
30595                 html: this.html
30596             });
30597         } else {
30598             cn.cls += ' hide';
30599         }
30600         
30601         if(this.bgimage.length){
30602             cfg.cn.push({
30603                 tag: 'img',
30604                 cls: 'roo-brick-image-view',
30605                 src: this.bgimage
30606             });
30607         }
30608         
30609         return cfg;
30610     },
30611     
30612     initEvents: function() 
30613     {
30614         if(this.title.length || this.html.length){
30615             this.el.on('mouseenter'  ,this.enter, this);
30616             this.el.on('mouseleave', this.leave, this);
30617         }
30618         
30619         
30620         Roo.EventManager.onWindowResize(this.resize, this); 
30621         
30622         this.resize();
30623     },
30624     
30625     resize : function()
30626     {
30627         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30628         
30629         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30630 //        paragraph.setHeight(paragraph.getWidth());
30631         
30632         if(this.bgimage.length){
30633             var image = this.el.select('.roo-brick-image-view', true).first();
30634             image.setWidth(paragraph.getWidth());
30635             image.setHeight(paragraph.getWidth());
30636         }
30637         
30638     },
30639     
30640     enter: function(e, el)
30641     {
30642         e.preventDefault();
30643         
30644         if(this.bgimage.length){
30645             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30646             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30647         }
30648     },
30649     
30650     leave: function(e, el)
30651     {
30652         e.preventDefault();
30653         
30654         if(this.bgimage.length){
30655             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30656             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30657         }
30658     }
30659     
30660 });
30661
30662  
30663
30664  /*
30665  * Based on:
30666  * Ext JS Library 1.1.1
30667  * Copyright(c) 2006-2007, Ext JS, LLC.
30668  *
30669  * Originally Released Under LGPL - original licence link has changed is not relivant.
30670  *
30671  * Fork - LGPL
30672  * <script type="text/javascript">
30673  */
30674
30675
30676 /**
30677  * @class Roo.bootstrap.SplitBar
30678  * @extends Roo.util.Observable
30679  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30680  * <br><br>
30681  * Usage:
30682  * <pre><code>
30683 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30684                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30685 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30686 split.minSize = 100;
30687 split.maxSize = 600;
30688 split.animate = true;
30689 split.on('moved', splitterMoved);
30690 </code></pre>
30691  * @constructor
30692  * Create a new SplitBar
30693  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
30694  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
30695  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30696  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
30697                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30698                         position of the SplitBar).
30699  */
30700 Roo.bootstrap.SplitBar = function(cfg){
30701     
30702     /** @private */
30703     
30704     //{
30705     //  dragElement : elm
30706     //  resizingElement: el,
30707         // optional..
30708     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30709     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
30710         // existingProxy ???
30711     //}
30712     
30713     this.el = Roo.get(cfg.dragElement, true);
30714     this.el.dom.unselectable = "on";
30715     /** @private */
30716     this.resizingEl = Roo.get(cfg.resizingElement, true);
30717
30718     /**
30719      * @private
30720      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30721      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30722      * @type Number
30723      */
30724     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30725     
30726     /**
30727      * The minimum size of the resizing element. (Defaults to 0)
30728      * @type Number
30729      */
30730     this.minSize = 0;
30731     
30732     /**
30733      * The maximum size of the resizing element. (Defaults to 2000)
30734      * @type Number
30735      */
30736     this.maxSize = 2000;
30737     
30738     /**
30739      * Whether to animate the transition to the new size
30740      * @type Boolean
30741      */
30742     this.animate = false;
30743     
30744     /**
30745      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30746      * @type Boolean
30747      */
30748     this.useShim = false;
30749     
30750     /** @private */
30751     this.shim = null;
30752     
30753     if(!cfg.existingProxy){
30754         /** @private */
30755         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30756     }else{
30757         this.proxy = Roo.get(cfg.existingProxy).dom;
30758     }
30759     /** @private */
30760     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30761     
30762     /** @private */
30763     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30764     
30765     /** @private */
30766     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30767     
30768     /** @private */
30769     this.dragSpecs = {};
30770     
30771     /**
30772      * @private The adapter to use to positon and resize elements
30773      */
30774     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30775     this.adapter.init(this);
30776     
30777     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30778         /** @private */
30779         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30780         this.el.addClass("roo-splitbar-h");
30781     }else{
30782         /** @private */
30783         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30784         this.el.addClass("roo-splitbar-v");
30785     }
30786     
30787     this.addEvents({
30788         /**
30789          * @event resize
30790          * Fires when the splitter is moved (alias for {@link #event-moved})
30791          * @param {Roo.bootstrap.SplitBar} this
30792          * @param {Number} newSize the new width or height
30793          */
30794         "resize" : true,
30795         /**
30796          * @event moved
30797          * Fires when the splitter is moved
30798          * @param {Roo.bootstrap.SplitBar} this
30799          * @param {Number} newSize the new width or height
30800          */
30801         "moved" : true,
30802         /**
30803          * @event beforeresize
30804          * Fires before the splitter is dragged
30805          * @param {Roo.bootstrap.SplitBar} this
30806          */
30807         "beforeresize" : true,
30808
30809         "beforeapply" : true
30810     });
30811
30812     Roo.util.Observable.call(this);
30813 };
30814
30815 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30816     onStartProxyDrag : function(x, y){
30817         this.fireEvent("beforeresize", this);
30818         if(!this.overlay){
30819             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
30820             o.unselectable();
30821             o.enableDisplayMode("block");
30822             // all splitbars share the same overlay
30823             Roo.bootstrap.SplitBar.prototype.overlay = o;
30824         }
30825         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30826         this.overlay.show();
30827         Roo.get(this.proxy).setDisplayed("block");
30828         var size = this.adapter.getElementSize(this);
30829         this.activeMinSize = this.getMinimumSize();;
30830         this.activeMaxSize = this.getMaximumSize();;
30831         var c1 = size - this.activeMinSize;
30832         var c2 = Math.max(this.activeMaxSize - size, 0);
30833         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30834             this.dd.resetConstraints();
30835             this.dd.setXConstraint(
30836                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
30837                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30838             );
30839             this.dd.setYConstraint(0, 0);
30840         }else{
30841             this.dd.resetConstraints();
30842             this.dd.setXConstraint(0, 0);
30843             this.dd.setYConstraint(
30844                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
30845                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30846             );
30847          }
30848         this.dragSpecs.startSize = size;
30849         this.dragSpecs.startPoint = [x, y];
30850         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30851     },
30852     
30853     /** 
30854      * @private Called after the drag operation by the DDProxy
30855      */
30856     onEndProxyDrag : function(e){
30857         Roo.get(this.proxy).setDisplayed(false);
30858         var endPoint = Roo.lib.Event.getXY(e);
30859         if(this.overlay){
30860             this.overlay.hide();
30861         }
30862         var newSize;
30863         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30864             newSize = this.dragSpecs.startSize + 
30865                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30866                     endPoint[0] - this.dragSpecs.startPoint[0] :
30867                     this.dragSpecs.startPoint[0] - endPoint[0]
30868                 );
30869         }else{
30870             newSize = this.dragSpecs.startSize + 
30871                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30872                     endPoint[1] - this.dragSpecs.startPoint[1] :
30873                     this.dragSpecs.startPoint[1] - endPoint[1]
30874                 );
30875         }
30876         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30877         if(newSize != this.dragSpecs.startSize){
30878             if(this.fireEvent('beforeapply', this, newSize) !== false){
30879                 this.adapter.setElementSize(this, newSize);
30880                 this.fireEvent("moved", this, newSize);
30881                 this.fireEvent("resize", this, newSize);
30882             }
30883         }
30884     },
30885     
30886     /**
30887      * Get the adapter this SplitBar uses
30888      * @return The adapter object
30889      */
30890     getAdapter : function(){
30891         return this.adapter;
30892     },
30893     
30894     /**
30895      * Set the adapter this SplitBar uses
30896      * @param {Object} adapter A SplitBar adapter object
30897      */
30898     setAdapter : function(adapter){
30899         this.adapter = adapter;
30900         this.adapter.init(this);
30901     },
30902     
30903     /**
30904      * Gets the minimum size for the resizing element
30905      * @return {Number} The minimum size
30906      */
30907     getMinimumSize : function(){
30908         return this.minSize;
30909     },
30910     
30911     /**
30912      * Sets the minimum size for the resizing element
30913      * @param {Number} minSize The minimum size
30914      */
30915     setMinimumSize : function(minSize){
30916         this.minSize = minSize;
30917     },
30918     
30919     /**
30920      * Gets the maximum size for the resizing element
30921      * @return {Number} The maximum size
30922      */
30923     getMaximumSize : function(){
30924         return this.maxSize;
30925     },
30926     
30927     /**
30928      * Sets the maximum size for the resizing element
30929      * @param {Number} maxSize The maximum size
30930      */
30931     setMaximumSize : function(maxSize){
30932         this.maxSize = maxSize;
30933     },
30934     
30935     /**
30936      * Sets the initialize size for the resizing element
30937      * @param {Number} size The initial size
30938      */
30939     setCurrentSize : function(size){
30940         var oldAnimate = this.animate;
30941         this.animate = false;
30942         this.adapter.setElementSize(this, size);
30943         this.animate = oldAnimate;
30944     },
30945     
30946     /**
30947      * Destroy this splitbar. 
30948      * @param {Boolean} removeEl True to remove the element
30949      */
30950     destroy : function(removeEl){
30951         if(this.shim){
30952             this.shim.remove();
30953         }
30954         this.dd.unreg();
30955         this.proxy.parentNode.removeChild(this.proxy);
30956         if(removeEl){
30957             this.el.remove();
30958         }
30959     }
30960 });
30961
30962 /**
30963  * @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.
30964  */
30965 Roo.bootstrap.SplitBar.createProxy = function(dir){
30966     var proxy = new Roo.Element(document.createElement("div"));
30967     proxy.unselectable();
30968     var cls = 'roo-splitbar-proxy';
30969     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
30970     document.body.appendChild(proxy.dom);
30971     return proxy.dom;
30972 };
30973
30974 /** 
30975  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
30976  * Default Adapter. It assumes the splitter and resizing element are not positioned
30977  * elements and only gets/sets the width of the element. Generally used for table based layouts.
30978  */
30979 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
30980 };
30981
30982 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
30983     // do nothing for now
30984     init : function(s){
30985     
30986     },
30987     /**
30988      * Called before drag operations to get the current size of the resizing element. 
30989      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30990      */
30991      getElementSize : function(s){
30992         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30993             return s.resizingEl.getWidth();
30994         }else{
30995             return s.resizingEl.getHeight();
30996         }
30997     },
30998     
30999     /**
31000      * Called after drag operations to set the size of the resizing element.
31001      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31002      * @param {Number} newSize The new size to set
31003      * @param {Function} onComplete A function to be invoked when resizing is complete
31004      */
31005     setElementSize : function(s, newSize, onComplete){
31006         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31007             if(!s.animate){
31008                 s.resizingEl.setWidth(newSize);
31009                 if(onComplete){
31010                     onComplete(s, newSize);
31011                 }
31012             }else{
31013                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31014             }
31015         }else{
31016             
31017             if(!s.animate){
31018                 s.resizingEl.setHeight(newSize);
31019                 if(onComplete){
31020                     onComplete(s, newSize);
31021                 }
31022             }else{
31023                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31024             }
31025         }
31026     }
31027 };
31028
31029 /** 
31030  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31031  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31032  * Adapter that  moves the splitter element to align with the resized sizing element. 
31033  * Used with an absolute positioned SplitBar.
31034  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31035  * document.body, make sure you assign an id to the body element.
31036  */
31037 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31038     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31039     this.container = Roo.get(container);
31040 };
31041
31042 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31043     init : function(s){
31044         this.basic.init(s);
31045     },
31046     
31047     getElementSize : function(s){
31048         return this.basic.getElementSize(s);
31049     },
31050     
31051     setElementSize : function(s, newSize, onComplete){
31052         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31053     },
31054     
31055     moveSplitter : function(s){
31056         var yes = Roo.bootstrap.SplitBar;
31057         switch(s.placement){
31058             case yes.LEFT:
31059                 s.el.setX(s.resizingEl.getRight());
31060                 break;
31061             case yes.RIGHT:
31062                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31063                 break;
31064             case yes.TOP:
31065                 s.el.setY(s.resizingEl.getBottom());
31066                 break;
31067             case yes.BOTTOM:
31068                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31069                 break;
31070         }
31071     }
31072 };
31073
31074 /**
31075  * Orientation constant - Create a vertical SplitBar
31076  * @static
31077  * @type Number
31078  */
31079 Roo.bootstrap.SplitBar.VERTICAL = 1;
31080
31081 /**
31082  * Orientation constant - Create a horizontal SplitBar
31083  * @static
31084  * @type Number
31085  */
31086 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31087
31088 /**
31089  * Placement constant - The resizing element is to the left of the splitter element
31090  * @static
31091  * @type Number
31092  */
31093 Roo.bootstrap.SplitBar.LEFT = 1;
31094
31095 /**
31096  * Placement constant - The resizing element is to the right of the splitter element
31097  * @static
31098  * @type Number
31099  */
31100 Roo.bootstrap.SplitBar.RIGHT = 2;
31101
31102 /**
31103  * Placement constant - The resizing element is positioned above the splitter element
31104  * @static
31105  * @type Number
31106  */
31107 Roo.bootstrap.SplitBar.TOP = 3;
31108
31109 /**
31110  * Placement constant - The resizing element is positioned under splitter element
31111  * @static
31112  * @type Number
31113  */
31114 Roo.bootstrap.SplitBar.BOTTOM = 4;
31115 Roo.namespace("Roo.bootstrap.layout");/*
31116  * Based on:
31117  * Ext JS Library 1.1.1
31118  * Copyright(c) 2006-2007, Ext JS, LLC.
31119  *
31120  * Originally Released Under LGPL - original licence link has changed is not relivant.
31121  *
31122  * Fork - LGPL
31123  * <script type="text/javascript">
31124  */
31125  
31126 /**
31127  * @class Roo.bootstrap.layout.Manager
31128  * @extends Roo.bootstrap.Component
31129  * Base class for layout managers.
31130  */
31131 Roo.bootstrap.layout.Manager = function(config)
31132 {
31133     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31134     
31135     
31136      
31137     
31138     
31139     /** false to disable window resize monitoring @type Boolean */
31140     this.monitorWindowResize = true;
31141     this.regions = {};
31142     this.addEvents({
31143         /**
31144          * @event layout
31145          * Fires when a layout is performed. 
31146          * @param {Roo.LayoutManager} this
31147          */
31148         "layout" : true,
31149         /**
31150          * @event regionresized
31151          * Fires when the user resizes a region. 
31152          * @param {Roo.LayoutRegion} region The resized region
31153          * @param {Number} newSize The new size (width for east/west, height for north/south)
31154          */
31155         "regionresized" : true,
31156         /**
31157          * @event regioncollapsed
31158          * Fires when a region is collapsed. 
31159          * @param {Roo.LayoutRegion} region The collapsed region
31160          */
31161         "regioncollapsed" : true,
31162         /**
31163          * @event regionexpanded
31164          * Fires when a region is expanded.  
31165          * @param {Roo.LayoutRegion} region The expanded region
31166          */
31167         "regionexpanded" : true
31168     });
31169     this.updating = false;
31170     
31171     if (config.el) {
31172         this.el = Roo.get(config.el);
31173         this.initEvents();
31174     }
31175     
31176 };
31177
31178 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31179     
31180     
31181     regions : null,
31182     
31183     monitorWindowResize : true,
31184     
31185     
31186     updating : false,
31187     
31188     
31189     onRender : function(ct, position)
31190     {
31191         if(!this.el){
31192             this.el = Roo.get(ct);
31193             this.initEvents();
31194         }
31195     },
31196     
31197     
31198     initEvents: function()
31199     {
31200         
31201         
31202         // ie scrollbar fix
31203         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31204             document.body.scroll = "no";
31205         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31206             this.el.position('relative');
31207         }
31208         this.id = this.el.id;
31209         this.el.addClass("roo-layout-container");
31210         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31211         if(this.el.dom != document.body ) {
31212             this.el.on('resize', this.layout,this);
31213             this.el.on('show', this.layout,this);
31214         }
31215
31216     },
31217     
31218     /**
31219      * Returns true if this layout is currently being updated
31220      * @return {Boolean}
31221      */
31222     isUpdating : function(){
31223         return this.updating; 
31224     },
31225     
31226     /**
31227      * Suspend the LayoutManager from doing auto-layouts while
31228      * making multiple add or remove calls
31229      */
31230     beginUpdate : function(){
31231         this.updating = true;    
31232     },
31233     
31234     /**
31235      * Restore auto-layouts and optionally disable the manager from performing a layout
31236      * @param {Boolean} noLayout true to disable a layout update 
31237      */
31238     endUpdate : function(noLayout){
31239         this.updating = false;
31240         if(!noLayout){
31241             this.layout();
31242         }    
31243     },
31244     
31245     layout: function(){
31246         // abstract...
31247     },
31248     
31249     onRegionResized : function(region, newSize){
31250         this.fireEvent("regionresized", region, newSize);
31251         this.layout();
31252     },
31253     
31254     onRegionCollapsed : function(region){
31255         this.fireEvent("regioncollapsed", region);
31256     },
31257     
31258     onRegionExpanded : function(region){
31259         this.fireEvent("regionexpanded", region);
31260     },
31261         
31262     /**
31263      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31264      * performs box-model adjustments.
31265      * @return {Object} The size as an object {width: (the width), height: (the height)}
31266      */
31267     getViewSize : function()
31268     {
31269         var size;
31270         if(this.el.dom != document.body){
31271             size = this.el.getSize();
31272         }else{
31273             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31274         }
31275         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31276         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31277         return size;
31278     },
31279     
31280     /**
31281      * Returns the Element this layout is bound to.
31282      * @return {Roo.Element}
31283      */
31284     getEl : function(){
31285         return this.el;
31286     },
31287     
31288     /**
31289      * Returns the specified region.
31290      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31291      * @return {Roo.LayoutRegion}
31292      */
31293     getRegion : function(target){
31294         return this.regions[target.toLowerCase()];
31295     },
31296     
31297     onWindowResize : function(){
31298         if(this.monitorWindowResize){
31299             this.layout();
31300         }
31301     }
31302 });/*
31303  * Based on:
31304  * Ext JS Library 1.1.1
31305  * Copyright(c) 2006-2007, Ext JS, LLC.
31306  *
31307  * Originally Released Under LGPL - original licence link has changed is not relivant.
31308  *
31309  * Fork - LGPL
31310  * <script type="text/javascript">
31311  */
31312 /**
31313  * @class Roo.bootstrap.layout.Border
31314  * @extends Roo.bootstrap.layout.Manager
31315  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31316  * please see: examples/bootstrap/nested.html<br><br>
31317  
31318 <b>The container the layout is rendered into can be either the body element or any other element.
31319 If it is not the body element, the container needs to either be an absolute positioned element,
31320 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31321 the container size if it is not the body element.</b>
31322
31323 * @constructor
31324 * Create a new Border
31325 * @param {Object} config Configuration options
31326  */
31327 Roo.bootstrap.layout.Border = function(config){
31328     config = config || {};
31329     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31330     
31331     
31332     
31333     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31334         if(config[region]){
31335             config[region].region = region;
31336             this.addRegion(config[region]);
31337         }
31338     },this);
31339     
31340 };
31341
31342 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31343
31344 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31345     /**
31346      * Creates and adds a new region if it doesn't already exist.
31347      * @param {String} target The target region key (north, south, east, west or center).
31348      * @param {Object} config The regions config object
31349      * @return {BorderLayoutRegion} The new region
31350      */
31351     addRegion : function(config)
31352     {
31353         if(!this.regions[config.region]){
31354             var r = this.factory(config);
31355             this.bindRegion(r);
31356         }
31357         return this.regions[config.region];
31358     },
31359
31360     // private (kinda)
31361     bindRegion : function(r){
31362         this.regions[r.config.region] = r;
31363         
31364         r.on("visibilitychange",    this.layout, this);
31365         r.on("paneladded",          this.layout, this);
31366         r.on("panelremoved",        this.layout, this);
31367         r.on("invalidated",         this.layout, this);
31368         r.on("resized",             this.onRegionResized, this);
31369         r.on("collapsed",           this.onRegionCollapsed, this);
31370         r.on("expanded",            this.onRegionExpanded, this);
31371     },
31372
31373     /**
31374      * Performs a layout update.
31375      */
31376     layout : function()
31377     {
31378         if(this.updating) {
31379             return;
31380         }
31381         var size = this.getViewSize();
31382         var w = size.width;
31383         var h = size.height;
31384         var centerW = w;
31385         var centerH = h;
31386         var centerY = 0;
31387         var centerX = 0;
31388         //var x = 0, y = 0;
31389
31390         var rs = this.regions;
31391         var north = rs["north"];
31392         var south = rs["south"]; 
31393         var west = rs["west"];
31394         var east = rs["east"];
31395         var center = rs["center"];
31396         //if(this.hideOnLayout){ // not supported anymore
31397             //c.el.setStyle("display", "none");
31398         //}
31399         if(north && north.isVisible()){
31400             var b = north.getBox();
31401             var m = north.getMargins();
31402             b.width = w - (m.left+m.right);
31403             b.x = m.left;
31404             b.y = m.top;
31405             centerY = b.height + b.y + m.bottom;
31406             centerH -= centerY;
31407             north.updateBox(this.safeBox(b));
31408         }
31409         if(south && south.isVisible()){
31410             var b = south.getBox();
31411             var m = south.getMargins();
31412             b.width = w - (m.left+m.right);
31413             b.x = m.left;
31414             var totalHeight = (b.height + m.top + m.bottom);
31415             b.y = h - totalHeight + m.top;
31416             centerH -= totalHeight;
31417             south.updateBox(this.safeBox(b));
31418         }
31419         if(west && west.isVisible()){
31420             var b = west.getBox();
31421             var m = west.getMargins();
31422             b.height = centerH - (m.top+m.bottom);
31423             b.x = m.left;
31424             b.y = centerY + m.top;
31425             var totalWidth = (b.width + m.left + m.right);
31426             centerX += totalWidth;
31427             centerW -= totalWidth;
31428             west.updateBox(this.safeBox(b));
31429         }
31430         if(east && east.isVisible()){
31431             var b = east.getBox();
31432             var m = east.getMargins();
31433             b.height = centerH - (m.top+m.bottom);
31434             var totalWidth = (b.width + m.left + m.right);
31435             b.x = w - totalWidth + m.left;
31436             b.y = centerY + m.top;
31437             centerW -= totalWidth;
31438             east.updateBox(this.safeBox(b));
31439         }
31440         if(center){
31441             var m = center.getMargins();
31442             var centerBox = {
31443                 x: centerX + m.left,
31444                 y: centerY + m.top,
31445                 width: centerW - (m.left+m.right),
31446                 height: centerH - (m.top+m.bottom)
31447             };
31448             //if(this.hideOnLayout){
31449                 //center.el.setStyle("display", "block");
31450             //}
31451             center.updateBox(this.safeBox(centerBox));
31452         }
31453         this.el.repaint();
31454         this.fireEvent("layout", this);
31455     },
31456
31457     // private
31458     safeBox : function(box){
31459         box.width = Math.max(0, box.width);
31460         box.height = Math.max(0, box.height);
31461         return box;
31462     },
31463
31464     /**
31465      * Adds a ContentPanel (or subclass) to this layout.
31466      * @param {String} target The target region key (north, south, east, west or center).
31467      * @param {Roo.ContentPanel} panel The panel to add
31468      * @return {Roo.ContentPanel} The added panel
31469      */
31470     add : function(target, panel){
31471          
31472         target = target.toLowerCase();
31473         return this.regions[target].add(panel);
31474     },
31475
31476     /**
31477      * Remove a ContentPanel (or subclass) to this layout.
31478      * @param {String} target The target region key (north, south, east, west or center).
31479      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31480      * @return {Roo.ContentPanel} The removed panel
31481      */
31482     remove : function(target, panel){
31483         target = target.toLowerCase();
31484         return this.regions[target].remove(panel);
31485     },
31486
31487     /**
31488      * Searches all regions for a panel with the specified id
31489      * @param {String} panelId
31490      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31491      */
31492     findPanel : function(panelId){
31493         var rs = this.regions;
31494         for(var target in rs){
31495             if(typeof rs[target] != "function"){
31496                 var p = rs[target].getPanel(panelId);
31497                 if(p){
31498                     return p;
31499                 }
31500             }
31501         }
31502         return null;
31503     },
31504
31505     /**
31506      * Searches all regions for a panel with the specified id and activates (shows) it.
31507      * @param {String/ContentPanel} panelId The panels id or the panel itself
31508      * @return {Roo.ContentPanel} The shown panel or null
31509      */
31510     showPanel : function(panelId) {
31511       var rs = this.regions;
31512       for(var target in rs){
31513          var r = rs[target];
31514          if(typeof r != "function"){
31515             if(r.hasPanel(panelId)){
31516                return r.showPanel(panelId);
31517             }
31518          }
31519       }
31520       return null;
31521    },
31522
31523    /**
31524      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31525      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31526      */
31527    /*
31528     restoreState : function(provider){
31529         if(!provider){
31530             provider = Roo.state.Manager;
31531         }
31532         var sm = new Roo.LayoutStateManager();
31533         sm.init(this, provider);
31534     },
31535 */
31536  
31537  
31538     /**
31539      * Adds a xtype elements to the layout.
31540      * <pre><code>
31541
31542 layout.addxtype({
31543        xtype : 'ContentPanel',
31544        region: 'west',
31545        items: [ .... ]
31546    }
31547 );
31548
31549 layout.addxtype({
31550         xtype : 'NestedLayoutPanel',
31551         region: 'west',
31552         layout: {
31553            center: { },
31554            west: { }   
31555         },
31556         items : [ ... list of content panels or nested layout panels.. ]
31557    }
31558 );
31559 </code></pre>
31560      * @param {Object} cfg Xtype definition of item to add.
31561      */
31562     addxtype : function(cfg)
31563     {
31564         // basically accepts a pannel...
31565         // can accept a layout region..!?!?
31566         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31567         
31568         
31569         // theory?  children can only be panels??
31570         
31571         //if (!cfg.xtype.match(/Panel$/)) {
31572         //    return false;
31573         //}
31574         var ret = false;
31575         
31576         if (typeof(cfg.region) == 'undefined') {
31577             Roo.log("Failed to add Panel, region was not set");
31578             Roo.log(cfg);
31579             return false;
31580         }
31581         var region = cfg.region;
31582         delete cfg.region;
31583         
31584           
31585         var xitems = [];
31586         if (cfg.items) {
31587             xitems = cfg.items;
31588             delete cfg.items;
31589         }
31590         var nb = false;
31591         
31592         switch(cfg.xtype) 
31593         {
31594             case 'Content':  // ContentPanel (el, cfg)
31595             case 'Scroll':  // ContentPanel (el, cfg)
31596             case 'View': 
31597                 cfg.autoCreate = true;
31598                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31599                 //} else {
31600                 //    var el = this.el.createChild();
31601                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31602                 //}
31603                 
31604                 this.add(region, ret);
31605                 break;
31606             
31607             /*
31608             case 'TreePanel': // our new panel!
31609                 cfg.el = this.el.createChild();
31610                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31611                 this.add(region, ret);
31612                 break;
31613             */
31614             
31615             case 'Nest': 
31616                 // create a new Layout (which is  a Border Layout...
31617                 
31618                 var clayout = cfg.layout;
31619                 clayout.el  = this.el.createChild();
31620                 clayout.items   = clayout.items  || [];
31621                 
31622                 delete cfg.layout;
31623                 
31624                 // replace this exitems with the clayout ones..
31625                 xitems = clayout.items;
31626                  
31627                 // force background off if it's in center...
31628                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31629                     cfg.background = false;
31630                 }
31631                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31632                 
31633                 
31634                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31635                 //console.log('adding nested layout panel '  + cfg.toSource());
31636                 this.add(region, ret);
31637                 nb = {}; /// find first...
31638                 break;
31639             
31640             case 'Grid':
31641                 
31642                 // needs grid and region
31643                 
31644                 //var el = this.getRegion(region).el.createChild();
31645                 /*
31646                  *var el = this.el.createChild();
31647                 // create the grid first...
31648                 cfg.grid.container = el;
31649                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31650                 */
31651                 
31652                 if (region == 'center' && this.active ) {
31653                     cfg.background = false;
31654                 }
31655                 
31656                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31657                 
31658                 this.add(region, ret);
31659                 /*
31660                 if (cfg.background) {
31661                     // render grid on panel activation (if panel background)
31662                     ret.on('activate', function(gp) {
31663                         if (!gp.grid.rendered) {
31664                     //        gp.grid.render(el);
31665                         }
31666                     });
31667                 } else {
31668                   //  cfg.grid.render(el);
31669                 }
31670                 */
31671                 break;
31672            
31673            
31674             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31675                 // it was the old xcomponent building that caused this before.
31676                 // espeically if border is the top element in the tree.
31677                 ret = this;
31678                 break; 
31679                 
31680                     
31681                 
31682                 
31683                 
31684             default:
31685                 /*
31686                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31687                     
31688                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31689                     this.add(region, ret);
31690                 } else {
31691                 */
31692                     Roo.log(cfg);
31693                     throw "Can not add '" + cfg.xtype + "' to Border";
31694                     return null;
31695              
31696                                 
31697              
31698         }
31699         this.beginUpdate();
31700         // add children..
31701         var region = '';
31702         var abn = {};
31703         Roo.each(xitems, function(i)  {
31704             region = nb && i.region ? i.region : false;
31705             
31706             var add = ret.addxtype(i);
31707            
31708             if (region) {
31709                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31710                 if (!i.background) {
31711                     abn[region] = nb[region] ;
31712                 }
31713             }
31714             
31715         });
31716         this.endUpdate();
31717
31718         // make the last non-background panel active..
31719         //if (nb) { Roo.log(abn); }
31720         if (nb) {
31721             
31722             for(var r in abn) {
31723                 region = this.getRegion(r);
31724                 if (region) {
31725                     // tried using nb[r], but it does not work..
31726                      
31727                     region.showPanel(abn[r]);
31728                    
31729                 }
31730             }
31731         }
31732         return ret;
31733         
31734     },
31735     
31736     
31737 // private
31738     factory : function(cfg)
31739     {
31740         
31741         var validRegions = Roo.bootstrap.layout.Border.regions;
31742
31743         var target = cfg.region;
31744         cfg.mgr = this;
31745         
31746         var r = Roo.bootstrap.layout;
31747         Roo.log(target);
31748         switch(target){
31749             case "north":
31750                 return new r.North(cfg);
31751             case "south":
31752                 return new r.South(cfg);
31753             case "east":
31754                 return new r.East(cfg);
31755             case "west":
31756                 return new r.West(cfg);
31757             case "center":
31758                 return new r.Center(cfg);
31759         }
31760         throw 'Layout region "'+target+'" not supported.';
31761     }
31762     
31763     
31764 });
31765  /*
31766  * Based on:
31767  * Ext JS Library 1.1.1
31768  * Copyright(c) 2006-2007, Ext JS, LLC.
31769  *
31770  * Originally Released Under LGPL - original licence link has changed is not relivant.
31771  *
31772  * Fork - LGPL
31773  * <script type="text/javascript">
31774  */
31775  
31776 /**
31777  * @class Roo.bootstrap.layout.Basic
31778  * @extends Roo.util.Observable
31779  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31780  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31781  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31782  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31783  * @cfg {string}   region  the region that it inhabits..
31784  * @cfg {bool}   skipConfig skip config?
31785  * 
31786
31787  */
31788 Roo.bootstrap.layout.Basic = function(config){
31789     
31790     this.mgr = config.mgr;
31791     
31792     this.position = config.region;
31793     
31794     var skipConfig = config.skipConfig;
31795     
31796     this.events = {
31797         /**
31798          * @scope Roo.BasicLayoutRegion
31799          */
31800         
31801         /**
31802          * @event beforeremove
31803          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31804          * @param {Roo.LayoutRegion} this
31805          * @param {Roo.ContentPanel} panel The panel
31806          * @param {Object} e The cancel event object
31807          */
31808         "beforeremove" : true,
31809         /**
31810          * @event invalidated
31811          * Fires when the layout for this region is changed.
31812          * @param {Roo.LayoutRegion} this
31813          */
31814         "invalidated" : true,
31815         /**
31816          * @event visibilitychange
31817          * Fires when this region is shown or hidden 
31818          * @param {Roo.LayoutRegion} this
31819          * @param {Boolean} visibility true or false
31820          */
31821         "visibilitychange" : true,
31822         /**
31823          * @event paneladded
31824          * Fires when a panel is added. 
31825          * @param {Roo.LayoutRegion} this
31826          * @param {Roo.ContentPanel} panel The panel
31827          */
31828         "paneladded" : true,
31829         /**
31830          * @event panelremoved
31831          * Fires when a panel is removed. 
31832          * @param {Roo.LayoutRegion} this
31833          * @param {Roo.ContentPanel} panel The panel
31834          */
31835         "panelremoved" : true,
31836         /**
31837          * @event beforecollapse
31838          * Fires when this region before collapse.
31839          * @param {Roo.LayoutRegion} this
31840          */
31841         "beforecollapse" : true,
31842         /**
31843          * @event collapsed
31844          * Fires when this region is collapsed.
31845          * @param {Roo.LayoutRegion} this
31846          */
31847         "collapsed" : true,
31848         /**
31849          * @event expanded
31850          * Fires when this region is expanded.
31851          * @param {Roo.LayoutRegion} this
31852          */
31853         "expanded" : true,
31854         /**
31855          * @event slideshow
31856          * Fires when this region is slid into view.
31857          * @param {Roo.LayoutRegion} this
31858          */
31859         "slideshow" : true,
31860         /**
31861          * @event slidehide
31862          * Fires when this region slides out of view. 
31863          * @param {Roo.LayoutRegion} this
31864          */
31865         "slidehide" : true,
31866         /**
31867          * @event panelactivated
31868          * Fires when a panel is activated. 
31869          * @param {Roo.LayoutRegion} this
31870          * @param {Roo.ContentPanel} panel The activated panel
31871          */
31872         "panelactivated" : true,
31873         /**
31874          * @event resized
31875          * Fires when the user resizes this region. 
31876          * @param {Roo.LayoutRegion} this
31877          * @param {Number} newSize The new size (width for east/west, height for north/south)
31878          */
31879         "resized" : true
31880     };
31881     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31882     this.panels = new Roo.util.MixedCollection();
31883     this.panels.getKey = this.getPanelId.createDelegate(this);
31884     this.box = null;
31885     this.activePanel = null;
31886     // ensure listeners are added...
31887     
31888     if (config.listeners || config.events) {
31889         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31890             listeners : config.listeners || {},
31891             events : config.events || {}
31892         });
31893     }
31894     
31895     if(skipConfig !== true){
31896         this.applyConfig(config);
31897     }
31898 };
31899
31900 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31901 {
31902     getPanelId : function(p){
31903         return p.getId();
31904     },
31905     
31906     applyConfig : function(config){
31907         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31908         this.config = config;
31909         
31910     },
31911     
31912     /**
31913      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31914      * the width, for horizontal (north, south) the height.
31915      * @param {Number} newSize The new width or height
31916      */
31917     resizeTo : function(newSize){
31918         var el = this.el ? this.el :
31919                  (this.activePanel ? this.activePanel.getEl() : null);
31920         if(el){
31921             switch(this.position){
31922                 case "east":
31923                 case "west":
31924                     el.setWidth(newSize);
31925                     this.fireEvent("resized", this, newSize);
31926                 break;
31927                 case "north":
31928                 case "south":
31929                     el.setHeight(newSize);
31930                     this.fireEvent("resized", this, newSize);
31931                 break;                
31932             }
31933         }
31934     },
31935     
31936     getBox : function(){
31937         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31938     },
31939     
31940     getMargins : function(){
31941         return this.margins;
31942     },
31943     
31944     updateBox : function(box){
31945         this.box = box;
31946         var el = this.activePanel.getEl();
31947         el.dom.style.left = box.x + "px";
31948         el.dom.style.top = box.y + "px";
31949         this.activePanel.setSize(box.width, box.height);
31950     },
31951     
31952     /**
31953      * Returns the container element for this region.
31954      * @return {Roo.Element}
31955      */
31956     getEl : function(){
31957         return this.activePanel;
31958     },
31959     
31960     /**
31961      * Returns true if this region is currently visible.
31962      * @return {Boolean}
31963      */
31964     isVisible : function(){
31965         return this.activePanel ? true : false;
31966     },
31967     
31968     setActivePanel : function(panel){
31969         panel = this.getPanel(panel);
31970         if(this.activePanel && this.activePanel != panel){
31971             this.activePanel.setActiveState(false);
31972             this.activePanel.getEl().setLeftTop(-10000,-10000);
31973         }
31974         this.activePanel = panel;
31975         panel.setActiveState(true);
31976         if(this.box){
31977             panel.setSize(this.box.width, this.box.height);
31978         }
31979         this.fireEvent("panelactivated", this, panel);
31980         this.fireEvent("invalidated");
31981     },
31982     
31983     /**
31984      * Show the specified panel.
31985      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31986      * @return {Roo.ContentPanel} The shown panel or null
31987      */
31988     showPanel : function(panel){
31989         panel = this.getPanel(panel);
31990         if(panel){
31991             this.setActivePanel(panel);
31992         }
31993         return panel;
31994     },
31995     
31996     /**
31997      * Get the active panel for this region.
31998      * @return {Roo.ContentPanel} The active panel or null
31999      */
32000     getActivePanel : function(){
32001         return this.activePanel;
32002     },
32003     
32004     /**
32005      * Add the passed ContentPanel(s)
32006      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32007      * @return {Roo.ContentPanel} The panel added (if only one was added)
32008      */
32009     add : function(panel){
32010         if(arguments.length > 1){
32011             for(var i = 0, len = arguments.length; i < len; i++) {
32012                 this.add(arguments[i]);
32013             }
32014             return null;
32015         }
32016         if(this.hasPanel(panel)){
32017             this.showPanel(panel);
32018             return panel;
32019         }
32020         var el = panel.getEl();
32021         if(el.dom.parentNode != this.mgr.el.dom){
32022             this.mgr.el.dom.appendChild(el.dom);
32023         }
32024         if(panel.setRegion){
32025             panel.setRegion(this);
32026         }
32027         this.panels.add(panel);
32028         el.setStyle("position", "absolute");
32029         if(!panel.background){
32030             this.setActivePanel(panel);
32031             if(this.config.initialSize && this.panels.getCount()==1){
32032                 this.resizeTo(this.config.initialSize);
32033             }
32034         }
32035         this.fireEvent("paneladded", this, panel);
32036         return panel;
32037     },
32038     
32039     /**
32040      * Returns true if the panel is in this region.
32041      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32042      * @return {Boolean}
32043      */
32044     hasPanel : function(panel){
32045         if(typeof panel == "object"){ // must be panel obj
32046             panel = panel.getId();
32047         }
32048         return this.getPanel(panel) ? true : false;
32049     },
32050     
32051     /**
32052      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32053      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32054      * @param {Boolean} preservePanel Overrides the config preservePanel option
32055      * @return {Roo.ContentPanel} The panel that was removed
32056      */
32057     remove : function(panel, preservePanel){
32058         panel = this.getPanel(panel);
32059         if(!panel){
32060             return null;
32061         }
32062         var e = {};
32063         this.fireEvent("beforeremove", this, panel, e);
32064         if(e.cancel === true){
32065             return null;
32066         }
32067         var panelId = panel.getId();
32068         this.panels.removeKey(panelId);
32069         return panel;
32070     },
32071     
32072     /**
32073      * Returns the panel specified or null if it's not in this region.
32074      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32075      * @return {Roo.ContentPanel}
32076      */
32077     getPanel : function(id){
32078         if(typeof id == "object"){ // must be panel obj
32079             return id;
32080         }
32081         return this.panels.get(id);
32082     },
32083     
32084     /**
32085      * Returns this regions position (north/south/east/west/center).
32086      * @return {String} 
32087      */
32088     getPosition: function(){
32089         return this.position;    
32090     }
32091 });/*
32092  * Based on:
32093  * Ext JS Library 1.1.1
32094  * Copyright(c) 2006-2007, Ext JS, LLC.
32095  *
32096  * Originally Released Under LGPL - original licence link has changed is not relivant.
32097  *
32098  * Fork - LGPL
32099  * <script type="text/javascript">
32100  */
32101  
32102 /**
32103  * @class Roo.bootstrap.layout.Region
32104  * @extends Roo.bootstrap.layout.Basic
32105  * This class represents a region in a layout manager.
32106  
32107  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32108  * @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})
32109  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32110  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32111  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32112  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32113  * @cfg {String}    title           The title for the region (overrides panel titles)
32114  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32115  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32116  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32117  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32118  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32119  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32120  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32121  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32122  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32123  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32124
32125  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32126  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32127  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32128  * @cfg {Number}    width           For East/West panels
32129  * @cfg {Number}    height          For North/South panels
32130  * @cfg {Boolean}   split           To show the splitter
32131  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32132  * 
32133  * @cfg {string}   cls             Extra CSS classes to add to region
32134  * 
32135  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32136  * @cfg {string}   region  the region that it inhabits..
32137  *
32138
32139  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32140  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32141
32142  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32143  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32144  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32145  */
32146 Roo.bootstrap.layout.Region = function(config)
32147 {
32148     this.applyConfig(config);
32149
32150     var mgr = config.mgr;
32151     var pos = config.region;
32152     config.skipConfig = true;
32153     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32154     
32155     if (mgr.el) {
32156         this.onRender(mgr.el);   
32157     }
32158      
32159     this.visible = true;
32160     this.collapsed = false;
32161 };
32162
32163 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32164
32165     position: '', // set by wrapper (eg. north/south etc..)
32166
32167     createBody : function(){
32168         /** This region's body element 
32169         * @type Roo.Element */
32170         this.bodyEl = this.el.createChild({
32171                 tag: "div",
32172                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32173         });
32174     },
32175
32176     onRender: function(ctr, pos)
32177     {
32178         var dh = Roo.DomHelper;
32179         /** This region's container element 
32180         * @type Roo.Element */
32181         this.el = dh.append(ctr.dom, {
32182                 tag: "div",
32183                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32184             }, true);
32185         /** This region's title element 
32186         * @type Roo.Element */
32187     
32188         this.titleEl = dh.append(this.el.dom,
32189             {
32190                     tag: "div",
32191                     unselectable: "on",
32192                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32193                     children:[
32194                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32195                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32196                     ]}, true);
32197         
32198         this.titleEl.enableDisplayMode();
32199         /** This region's title text element 
32200         * @type HTMLElement */
32201         this.titleTextEl = this.titleEl.dom.firstChild;
32202         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32203         /*
32204         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32205         this.closeBtn.enableDisplayMode();
32206         this.closeBtn.on("click", this.closeClicked, this);
32207         this.closeBtn.hide();
32208     */
32209         this.createBody(this.config);
32210         if(this.config.hideWhenEmpty){
32211             this.hide();
32212             this.on("paneladded", this.validateVisibility, this);
32213             this.on("panelremoved", this.validateVisibility, this);
32214         }
32215         if(this.autoScroll){
32216             this.bodyEl.setStyle("overflow", "auto");
32217         }else{
32218             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32219         }
32220         //if(c.titlebar !== false){
32221             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32222                 this.titleEl.hide();
32223             }else{
32224                 this.titleEl.show();
32225                 if(this.config.title){
32226                     this.titleTextEl.innerHTML = this.config.title;
32227                 }
32228             }
32229         //}
32230         if(this.config.collapsed){
32231             this.collapse(true);
32232         }
32233         if(this.config.hidden){
32234             this.hide();
32235         }
32236     },
32237     
32238     applyConfig : function(c)
32239     {
32240         /*
32241          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32242             var dh = Roo.DomHelper;
32243             if(c.titlebar !== false){
32244                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32245                 this.collapseBtn.on("click", this.collapse, this);
32246                 this.collapseBtn.enableDisplayMode();
32247                 /*
32248                 if(c.showPin === true || this.showPin){
32249                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32250                     this.stickBtn.enableDisplayMode();
32251                     this.stickBtn.on("click", this.expand, this);
32252                     this.stickBtn.hide();
32253                 }
32254                 
32255             }
32256             */
32257             /** This region's collapsed element
32258             * @type Roo.Element */
32259             /*
32260              *
32261             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32262                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32263             ]}, true);
32264             
32265             if(c.floatable !== false){
32266                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32267                this.collapsedEl.on("click", this.collapseClick, this);
32268             }
32269
32270             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32271                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32272                    id: "message", unselectable: "on", style:{"float":"left"}});
32273                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32274              }
32275             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32276             this.expandBtn.on("click", this.expand, this);
32277             
32278         }
32279         
32280         if(this.collapseBtn){
32281             this.collapseBtn.setVisible(c.collapsible == true);
32282         }
32283         
32284         this.cmargins = c.cmargins || this.cmargins ||
32285                          (this.position == "west" || this.position == "east" ?
32286                              {top: 0, left: 2, right:2, bottom: 0} :
32287                              {top: 2, left: 0, right:0, bottom: 2});
32288         */
32289         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32290         
32291         
32292         this.bottomTabs = c.tabPosition != "top";
32293         
32294         this.autoScroll = c.autoScroll || false;
32295         
32296         
32297        
32298         
32299         this.duration = c.duration || .30;
32300         this.slideDuration = c.slideDuration || .45;
32301         this.config = c;
32302        
32303     },
32304     /**
32305      * Returns true if this region is currently visible.
32306      * @return {Boolean}
32307      */
32308     isVisible : function(){
32309         return this.visible;
32310     },
32311
32312     /**
32313      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32314      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32315      */
32316     //setCollapsedTitle : function(title){
32317     //    title = title || "&#160;";
32318      //   if(this.collapsedTitleTextEl){
32319       //      this.collapsedTitleTextEl.innerHTML = title;
32320        // }
32321     //},
32322
32323     getBox : function(){
32324         var b;
32325       //  if(!this.collapsed){
32326             b = this.el.getBox(false, true);
32327        // }else{
32328           //  b = this.collapsedEl.getBox(false, true);
32329         //}
32330         return b;
32331     },
32332
32333     getMargins : function(){
32334         return this.margins;
32335         //return this.collapsed ? this.cmargins : this.margins;
32336     },
32337 /*
32338     highlight : function(){
32339         this.el.addClass("x-layout-panel-dragover");
32340     },
32341
32342     unhighlight : function(){
32343         this.el.removeClass("x-layout-panel-dragover");
32344     },
32345 */
32346     updateBox : function(box)
32347     {
32348         this.box = box;
32349         if(!this.collapsed){
32350             this.el.dom.style.left = box.x + "px";
32351             this.el.dom.style.top = box.y + "px";
32352             this.updateBody(box.width, box.height);
32353         }else{
32354             this.collapsedEl.dom.style.left = box.x + "px";
32355             this.collapsedEl.dom.style.top = box.y + "px";
32356             this.collapsedEl.setSize(box.width, box.height);
32357         }
32358         if(this.tabs){
32359             this.tabs.autoSizeTabs();
32360         }
32361     },
32362
32363     updateBody : function(w, h)
32364     {
32365         if(w !== null){
32366             this.el.setWidth(w);
32367             w -= this.el.getBorderWidth("rl");
32368             if(this.config.adjustments){
32369                 w += this.config.adjustments[0];
32370             }
32371         }
32372         if(h !== null){
32373             this.el.setHeight(h);
32374             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32375             h -= this.el.getBorderWidth("tb");
32376             if(this.config.adjustments){
32377                 h += this.config.adjustments[1];
32378             }
32379             this.bodyEl.setHeight(h);
32380             if(this.tabs){
32381                 h = this.tabs.syncHeight(h);
32382             }
32383         }
32384         if(this.panelSize){
32385             w = w !== null ? w : this.panelSize.width;
32386             h = h !== null ? h : this.panelSize.height;
32387         }
32388         if(this.activePanel){
32389             var el = this.activePanel.getEl();
32390             w = w !== null ? w : el.getWidth();
32391             h = h !== null ? h : el.getHeight();
32392             this.panelSize = {width: w, height: h};
32393             this.activePanel.setSize(w, h);
32394         }
32395         if(Roo.isIE && this.tabs){
32396             this.tabs.el.repaint();
32397         }
32398     },
32399
32400     /**
32401      * Returns the container element for this region.
32402      * @return {Roo.Element}
32403      */
32404     getEl : function(){
32405         return this.el;
32406     },
32407
32408     /**
32409      * Hides this region.
32410      */
32411     hide : function(){
32412         //if(!this.collapsed){
32413             this.el.dom.style.left = "-2000px";
32414             this.el.hide();
32415         //}else{
32416          //   this.collapsedEl.dom.style.left = "-2000px";
32417          //   this.collapsedEl.hide();
32418        // }
32419         this.visible = false;
32420         this.fireEvent("visibilitychange", this, false);
32421     },
32422
32423     /**
32424      * Shows this region if it was previously hidden.
32425      */
32426     show : function(){
32427         //if(!this.collapsed){
32428             this.el.show();
32429         //}else{
32430         //    this.collapsedEl.show();
32431        // }
32432         this.visible = true;
32433         this.fireEvent("visibilitychange", this, true);
32434     },
32435 /*
32436     closeClicked : function(){
32437         if(this.activePanel){
32438             this.remove(this.activePanel);
32439         }
32440     },
32441
32442     collapseClick : function(e){
32443         if(this.isSlid){
32444            e.stopPropagation();
32445            this.slideIn();
32446         }else{
32447            e.stopPropagation();
32448            this.slideOut();
32449         }
32450     },
32451 */
32452     /**
32453      * Collapses this region.
32454      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32455      */
32456     /*
32457     collapse : function(skipAnim, skipCheck = false){
32458         if(this.collapsed) {
32459             return;
32460         }
32461         
32462         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32463             
32464             this.collapsed = true;
32465             if(this.split){
32466                 this.split.el.hide();
32467             }
32468             if(this.config.animate && skipAnim !== true){
32469                 this.fireEvent("invalidated", this);
32470                 this.animateCollapse();
32471             }else{
32472                 this.el.setLocation(-20000,-20000);
32473                 this.el.hide();
32474                 this.collapsedEl.show();
32475                 this.fireEvent("collapsed", this);
32476                 this.fireEvent("invalidated", this);
32477             }
32478         }
32479         
32480     },
32481 */
32482     animateCollapse : function(){
32483         // overridden
32484     },
32485
32486     /**
32487      * Expands this region if it was previously collapsed.
32488      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32489      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32490      */
32491     /*
32492     expand : function(e, skipAnim){
32493         if(e) {
32494             e.stopPropagation();
32495         }
32496         if(!this.collapsed || this.el.hasActiveFx()) {
32497             return;
32498         }
32499         if(this.isSlid){
32500             this.afterSlideIn();
32501             skipAnim = true;
32502         }
32503         this.collapsed = false;
32504         if(this.config.animate && skipAnim !== true){
32505             this.animateExpand();
32506         }else{
32507             this.el.show();
32508             if(this.split){
32509                 this.split.el.show();
32510             }
32511             this.collapsedEl.setLocation(-2000,-2000);
32512             this.collapsedEl.hide();
32513             this.fireEvent("invalidated", this);
32514             this.fireEvent("expanded", this);
32515         }
32516     },
32517 */
32518     animateExpand : function(){
32519         // overridden
32520     },
32521
32522     initTabs : function()
32523     {
32524         this.bodyEl.setStyle("overflow", "hidden");
32525         var ts = new Roo.bootstrap.panel.Tabs({
32526                 el: this.bodyEl.dom,
32527                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32528                 disableTooltips: this.config.disableTabTips,
32529                 toolbar : this.config.toolbar
32530             });
32531         
32532         if(this.config.hideTabs){
32533             ts.stripWrap.setDisplayed(false);
32534         }
32535         this.tabs = ts;
32536         ts.resizeTabs = this.config.resizeTabs === true;
32537         ts.minTabWidth = this.config.minTabWidth || 40;
32538         ts.maxTabWidth = this.config.maxTabWidth || 250;
32539         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32540         ts.monitorResize = false;
32541         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32542         ts.bodyEl.addClass('roo-layout-tabs-body');
32543         this.panels.each(this.initPanelAsTab, this);
32544     },
32545
32546     initPanelAsTab : function(panel){
32547         var ti = this.tabs.addTab(
32548                     panel.getEl().id,
32549                     panel.getTitle(), null,
32550                     this.config.closeOnTab && panel.isClosable()
32551             );
32552         if(panel.tabTip !== undefined){
32553             ti.setTooltip(panel.tabTip);
32554         }
32555         ti.on("activate", function(){
32556               this.setActivePanel(panel);
32557         }, this);
32558         
32559         if(this.config.closeOnTab){
32560             ti.on("beforeclose", function(t, e){
32561                 e.cancel = true;
32562                 this.remove(panel);
32563             }, this);
32564         }
32565         return ti;
32566     },
32567
32568     updatePanelTitle : function(panel, title)
32569     {
32570         if(this.activePanel == panel){
32571             this.updateTitle(title);
32572         }
32573         if(this.tabs){
32574             var ti = this.tabs.getTab(panel.getEl().id);
32575             ti.setText(title);
32576             if(panel.tabTip !== undefined){
32577                 ti.setTooltip(panel.tabTip);
32578             }
32579         }
32580     },
32581
32582     updateTitle : function(title){
32583         if(this.titleTextEl && !this.config.title){
32584             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32585         }
32586     },
32587
32588     setActivePanel : function(panel)
32589     {
32590         panel = this.getPanel(panel);
32591         if(this.activePanel && this.activePanel != panel){
32592             this.activePanel.setActiveState(false);
32593         }
32594         this.activePanel = panel;
32595         panel.setActiveState(true);
32596         if(this.panelSize){
32597             panel.setSize(this.panelSize.width, this.panelSize.height);
32598         }
32599         if(this.closeBtn){
32600             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32601         }
32602         this.updateTitle(panel.getTitle());
32603         if(this.tabs){
32604             this.fireEvent("invalidated", this);
32605         }
32606         this.fireEvent("panelactivated", this, panel);
32607     },
32608
32609     /**
32610      * Shows the specified panel.
32611      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32612      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32613      */
32614     showPanel : function(panel)
32615     {
32616         panel = this.getPanel(panel);
32617         if(panel){
32618             if(this.tabs){
32619                 var tab = this.tabs.getTab(panel.getEl().id);
32620                 if(tab.isHidden()){
32621                     this.tabs.unhideTab(tab.id);
32622                 }
32623                 tab.activate();
32624             }else{
32625                 this.setActivePanel(panel);
32626             }
32627         }
32628         return panel;
32629     },
32630
32631     /**
32632      * Get the active panel for this region.
32633      * @return {Roo.ContentPanel} The active panel or null
32634      */
32635     getActivePanel : function(){
32636         return this.activePanel;
32637     },
32638
32639     validateVisibility : function(){
32640         if(this.panels.getCount() < 1){
32641             this.updateTitle("&#160;");
32642             this.closeBtn.hide();
32643             this.hide();
32644         }else{
32645             if(!this.isVisible()){
32646                 this.show();
32647             }
32648         }
32649     },
32650
32651     /**
32652      * Adds the passed ContentPanel(s) to this region.
32653      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32654      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32655      */
32656     add : function(panel){
32657         if(arguments.length > 1){
32658             for(var i = 0, len = arguments.length; i < len; i++) {
32659                 this.add(arguments[i]);
32660             }
32661             return null;
32662         }
32663         if(this.hasPanel(panel)){
32664             this.showPanel(panel);
32665             return panel;
32666         }
32667         panel.setRegion(this);
32668         this.panels.add(panel);
32669         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32670             this.bodyEl.dom.appendChild(panel.getEl().dom);
32671             if(panel.background !== true){
32672                 this.setActivePanel(panel);
32673             }
32674             this.fireEvent("paneladded", this, panel);
32675             return panel;
32676         }
32677         if(!this.tabs){
32678             this.initTabs();
32679         }else{
32680             this.initPanelAsTab(panel);
32681         }
32682         
32683         
32684         if(panel.background !== true){
32685             this.tabs.activate(panel.getEl().id);
32686         }
32687         this.fireEvent("paneladded", this, panel);
32688         return panel;
32689     },
32690
32691     /**
32692      * Hides the tab for the specified panel.
32693      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32694      */
32695     hidePanel : function(panel){
32696         if(this.tabs && (panel = this.getPanel(panel))){
32697             this.tabs.hideTab(panel.getEl().id);
32698         }
32699     },
32700
32701     /**
32702      * Unhides the tab for a previously hidden panel.
32703      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32704      */
32705     unhidePanel : function(panel){
32706         if(this.tabs && (panel = this.getPanel(panel))){
32707             this.tabs.unhideTab(panel.getEl().id);
32708         }
32709     },
32710
32711     clearPanels : function(){
32712         while(this.panels.getCount() > 0){
32713              this.remove(this.panels.first());
32714         }
32715     },
32716
32717     /**
32718      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32719      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32720      * @param {Boolean} preservePanel Overrides the config preservePanel option
32721      * @return {Roo.ContentPanel} The panel that was removed
32722      */
32723     remove : function(panel, preservePanel)
32724     {
32725         panel = this.getPanel(panel);
32726         if(!panel){
32727             return null;
32728         }
32729         var e = {};
32730         this.fireEvent("beforeremove", this, panel, e);
32731         if(e.cancel === true){
32732             return null;
32733         }
32734         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32735         var panelId = panel.getId();
32736         this.panels.removeKey(panelId);
32737         if(preservePanel){
32738             document.body.appendChild(panel.getEl().dom);
32739         }
32740         if(this.tabs){
32741             this.tabs.removeTab(panel.getEl().id);
32742         }else if (!preservePanel){
32743             this.bodyEl.dom.removeChild(panel.getEl().dom);
32744         }
32745         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32746             var p = this.panels.first();
32747             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32748             tempEl.appendChild(p.getEl().dom);
32749             this.bodyEl.update("");
32750             this.bodyEl.dom.appendChild(p.getEl().dom);
32751             tempEl = null;
32752             this.updateTitle(p.getTitle());
32753             this.tabs = null;
32754             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32755             this.setActivePanel(p);
32756         }
32757         panel.setRegion(null);
32758         if(this.activePanel == panel){
32759             this.activePanel = null;
32760         }
32761         if(this.config.autoDestroy !== false && preservePanel !== true){
32762             try{panel.destroy();}catch(e){}
32763         }
32764         this.fireEvent("panelremoved", this, panel);
32765         return panel;
32766     },
32767
32768     /**
32769      * Returns the TabPanel component used by this region
32770      * @return {Roo.TabPanel}
32771      */
32772     getTabs : function(){
32773         return this.tabs;
32774     },
32775
32776     createTool : function(parentEl, className){
32777         var btn = Roo.DomHelper.append(parentEl, {
32778             tag: "div",
32779             cls: "x-layout-tools-button",
32780             children: [ {
32781                 tag: "div",
32782                 cls: "roo-layout-tools-button-inner " + className,
32783                 html: "&#160;"
32784             }]
32785         }, true);
32786         btn.addClassOnOver("roo-layout-tools-button-over");
32787         return btn;
32788     }
32789 });/*
32790  * Based on:
32791  * Ext JS Library 1.1.1
32792  * Copyright(c) 2006-2007, Ext JS, LLC.
32793  *
32794  * Originally Released Under LGPL - original licence link has changed is not relivant.
32795  *
32796  * Fork - LGPL
32797  * <script type="text/javascript">
32798  */
32799  
32800
32801
32802 /**
32803  * @class Roo.SplitLayoutRegion
32804  * @extends Roo.LayoutRegion
32805  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32806  */
32807 Roo.bootstrap.layout.Split = function(config){
32808     this.cursor = config.cursor;
32809     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32810 };
32811
32812 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32813 {
32814     splitTip : "Drag to resize.",
32815     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32816     useSplitTips : false,
32817
32818     applyConfig : function(config){
32819         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32820     },
32821     
32822     onRender : function(ctr,pos) {
32823         
32824         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
32825         if(!this.config.split){
32826             return;
32827         }
32828         if(!this.split){
32829             
32830             var splitEl = Roo.DomHelper.append(ctr.dom,  {
32831                             tag: "div",
32832                             id: this.el.id + "-split",
32833                             cls: "roo-layout-split roo-layout-split-"+this.position,
32834                             html: "&#160;"
32835             });
32836             /** The SplitBar for this region 
32837             * @type Roo.SplitBar */
32838             // does not exist yet...
32839             Roo.log([this.position, this.orientation]);
32840             
32841             this.split = new Roo.bootstrap.SplitBar({
32842                 dragElement : splitEl,
32843                 resizingElement: this.el,
32844                 orientation : this.orientation
32845             });
32846             
32847             this.split.on("moved", this.onSplitMove, this);
32848             this.split.useShim = this.config.useShim === true;
32849             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32850             if(this.useSplitTips){
32851                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32852             }
32853             //if(config.collapsible){
32854             //    this.split.el.on("dblclick", this.collapse,  this);
32855             //}
32856         }
32857         if(typeof this.config.minSize != "undefined"){
32858             this.split.minSize = this.config.minSize;
32859         }
32860         if(typeof this.config.maxSize != "undefined"){
32861             this.split.maxSize = this.config.maxSize;
32862         }
32863         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
32864             this.hideSplitter();
32865         }
32866         
32867     },
32868
32869     getHMaxSize : function(){
32870          var cmax = this.config.maxSize || 10000;
32871          var center = this.mgr.getRegion("center");
32872          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32873     },
32874
32875     getVMaxSize : function(){
32876          var cmax = this.config.maxSize || 10000;
32877          var center = this.mgr.getRegion("center");
32878          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32879     },
32880
32881     onSplitMove : function(split, newSize){
32882         this.fireEvent("resized", this, newSize);
32883     },
32884     
32885     /** 
32886      * Returns the {@link Roo.SplitBar} for this region.
32887      * @return {Roo.SplitBar}
32888      */
32889     getSplitBar : function(){
32890         return this.split;
32891     },
32892     
32893     hide : function(){
32894         this.hideSplitter();
32895         Roo.bootstrap.layout.Split.superclass.hide.call(this);
32896     },
32897
32898     hideSplitter : function(){
32899         if(this.split){
32900             this.split.el.setLocation(-2000,-2000);
32901             this.split.el.hide();
32902         }
32903     },
32904
32905     show : function(){
32906         if(this.split){
32907             this.split.el.show();
32908         }
32909         Roo.bootstrap.layout.Split.superclass.show.call(this);
32910     },
32911     
32912     beforeSlide: function(){
32913         if(Roo.isGecko){// firefox overflow auto bug workaround
32914             this.bodyEl.clip();
32915             if(this.tabs) {
32916                 this.tabs.bodyEl.clip();
32917             }
32918             if(this.activePanel){
32919                 this.activePanel.getEl().clip();
32920                 
32921                 if(this.activePanel.beforeSlide){
32922                     this.activePanel.beforeSlide();
32923                 }
32924             }
32925         }
32926     },
32927     
32928     afterSlide : function(){
32929         if(Roo.isGecko){// firefox overflow auto bug workaround
32930             this.bodyEl.unclip();
32931             if(this.tabs) {
32932                 this.tabs.bodyEl.unclip();
32933             }
32934             if(this.activePanel){
32935                 this.activePanel.getEl().unclip();
32936                 if(this.activePanel.afterSlide){
32937                     this.activePanel.afterSlide();
32938                 }
32939             }
32940         }
32941     },
32942
32943     initAutoHide : function(){
32944         if(this.autoHide !== false){
32945             if(!this.autoHideHd){
32946                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32947                 this.autoHideHd = {
32948                     "mouseout": function(e){
32949                         if(!e.within(this.el, true)){
32950                             st.delay(500);
32951                         }
32952                     },
32953                     "mouseover" : function(e){
32954                         st.cancel();
32955                     },
32956                     scope : this
32957                 };
32958             }
32959             this.el.on(this.autoHideHd);
32960         }
32961     },
32962
32963     clearAutoHide : function(){
32964         if(this.autoHide !== false){
32965             this.el.un("mouseout", this.autoHideHd.mouseout);
32966             this.el.un("mouseover", this.autoHideHd.mouseover);
32967         }
32968     },
32969
32970     clearMonitor : function(){
32971         Roo.get(document).un("click", this.slideInIf, this);
32972     },
32973
32974     // these names are backwards but not changed for compat
32975     slideOut : function(){
32976         if(this.isSlid || this.el.hasActiveFx()){
32977             return;
32978         }
32979         this.isSlid = true;
32980         if(this.collapseBtn){
32981             this.collapseBtn.hide();
32982         }
32983         this.closeBtnState = this.closeBtn.getStyle('display');
32984         this.closeBtn.hide();
32985         if(this.stickBtn){
32986             this.stickBtn.show();
32987         }
32988         this.el.show();
32989         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32990         this.beforeSlide();
32991         this.el.setStyle("z-index", 10001);
32992         this.el.slideIn(this.getSlideAnchor(), {
32993             callback: function(){
32994                 this.afterSlide();
32995                 this.initAutoHide();
32996                 Roo.get(document).on("click", this.slideInIf, this);
32997                 this.fireEvent("slideshow", this);
32998             },
32999             scope: this,
33000             block: true
33001         });
33002     },
33003
33004     afterSlideIn : function(){
33005         this.clearAutoHide();
33006         this.isSlid = false;
33007         this.clearMonitor();
33008         this.el.setStyle("z-index", "");
33009         if(this.collapseBtn){
33010             this.collapseBtn.show();
33011         }
33012         this.closeBtn.setStyle('display', this.closeBtnState);
33013         if(this.stickBtn){
33014             this.stickBtn.hide();
33015         }
33016         this.fireEvent("slidehide", this);
33017     },
33018
33019     slideIn : function(cb){
33020         if(!this.isSlid || this.el.hasActiveFx()){
33021             Roo.callback(cb);
33022             return;
33023         }
33024         this.isSlid = false;
33025         this.beforeSlide();
33026         this.el.slideOut(this.getSlideAnchor(), {
33027             callback: function(){
33028                 this.el.setLeftTop(-10000, -10000);
33029                 this.afterSlide();
33030                 this.afterSlideIn();
33031                 Roo.callback(cb);
33032             },
33033             scope: this,
33034             block: true
33035         });
33036     },
33037     
33038     slideInIf : function(e){
33039         if(!e.within(this.el)){
33040             this.slideIn();
33041         }
33042     },
33043
33044     animateCollapse : function(){
33045         this.beforeSlide();
33046         this.el.setStyle("z-index", 20000);
33047         var anchor = this.getSlideAnchor();
33048         this.el.slideOut(anchor, {
33049             callback : function(){
33050                 this.el.setStyle("z-index", "");
33051                 this.collapsedEl.slideIn(anchor, {duration:.3});
33052                 this.afterSlide();
33053                 this.el.setLocation(-10000,-10000);
33054                 this.el.hide();
33055                 this.fireEvent("collapsed", this);
33056             },
33057             scope: this,
33058             block: true
33059         });
33060     },
33061
33062     animateExpand : function(){
33063         this.beforeSlide();
33064         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33065         this.el.setStyle("z-index", 20000);
33066         this.collapsedEl.hide({
33067             duration:.1
33068         });
33069         this.el.slideIn(this.getSlideAnchor(), {
33070             callback : function(){
33071                 this.el.setStyle("z-index", "");
33072                 this.afterSlide();
33073                 if(this.split){
33074                     this.split.el.show();
33075                 }
33076                 this.fireEvent("invalidated", this);
33077                 this.fireEvent("expanded", this);
33078             },
33079             scope: this,
33080             block: true
33081         });
33082     },
33083
33084     anchors : {
33085         "west" : "left",
33086         "east" : "right",
33087         "north" : "top",
33088         "south" : "bottom"
33089     },
33090
33091     sanchors : {
33092         "west" : "l",
33093         "east" : "r",
33094         "north" : "t",
33095         "south" : "b"
33096     },
33097
33098     canchors : {
33099         "west" : "tl-tr",
33100         "east" : "tr-tl",
33101         "north" : "tl-bl",
33102         "south" : "bl-tl"
33103     },
33104
33105     getAnchor : function(){
33106         return this.anchors[this.position];
33107     },
33108
33109     getCollapseAnchor : function(){
33110         return this.canchors[this.position];
33111     },
33112
33113     getSlideAnchor : function(){
33114         return this.sanchors[this.position];
33115     },
33116
33117     getAlignAdj : function(){
33118         var cm = this.cmargins;
33119         switch(this.position){
33120             case "west":
33121                 return [0, 0];
33122             break;
33123             case "east":
33124                 return [0, 0];
33125             break;
33126             case "north":
33127                 return [0, 0];
33128             break;
33129             case "south":
33130                 return [0, 0];
33131             break;
33132         }
33133     },
33134
33135     getExpandAdj : function(){
33136         var c = this.collapsedEl, cm = this.cmargins;
33137         switch(this.position){
33138             case "west":
33139                 return [-(cm.right+c.getWidth()+cm.left), 0];
33140             break;
33141             case "east":
33142                 return [cm.right+c.getWidth()+cm.left, 0];
33143             break;
33144             case "north":
33145                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33146             break;
33147             case "south":
33148                 return [0, cm.top+cm.bottom+c.getHeight()];
33149             break;
33150         }
33151     }
33152 });/*
33153  * Based on:
33154  * Ext JS Library 1.1.1
33155  * Copyright(c) 2006-2007, Ext JS, LLC.
33156  *
33157  * Originally Released Under LGPL - original licence link has changed is not relivant.
33158  *
33159  * Fork - LGPL
33160  * <script type="text/javascript">
33161  */
33162 /*
33163  * These classes are private internal classes
33164  */
33165 Roo.bootstrap.layout.Center = function(config){
33166     config.region = "center";
33167     Roo.bootstrap.layout.Region.call(this, config);
33168     this.visible = true;
33169     this.minWidth = config.minWidth || 20;
33170     this.minHeight = config.minHeight || 20;
33171 };
33172
33173 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33174     hide : function(){
33175         // center panel can't be hidden
33176     },
33177     
33178     show : function(){
33179         // center panel can't be hidden
33180     },
33181     
33182     getMinWidth: function(){
33183         return this.minWidth;
33184     },
33185     
33186     getMinHeight: function(){
33187         return this.minHeight;
33188     }
33189 });
33190
33191
33192
33193
33194  
33195
33196
33197
33198
33199
33200 Roo.bootstrap.layout.North = function(config)
33201 {
33202     config.region = 'north';
33203     config.cursor = 'n-resize';
33204     
33205     Roo.bootstrap.layout.Split.call(this, config);
33206     
33207     
33208     if(this.split){
33209         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33210         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33211         this.split.el.addClass("roo-layout-split-v");
33212     }
33213     var size = config.initialSize || config.height;
33214     if(typeof size != "undefined"){
33215         this.el.setHeight(size);
33216     }
33217 };
33218 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33219 {
33220     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33221     
33222     
33223     
33224     getBox : function(){
33225         if(this.collapsed){
33226             return this.collapsedEl.getBox();
33227         }
33228         var box = this.el.getBox();
33229         if(this.split){
33230             box.height += this.split.el.getHeight();
33231         }
33232         return box;
33233     },
33234     
33235     updateBox : function(box){
33236         if(this.split && !this.collapsed){
33237             box.height -= this.split.el.getHeight();
33238             this.split.el.setLeft(box.x);
33239             this.split.el.setTop(box.y+box.height);
33240             this.split.el.setWidth(box.width);
33241         }
33242         if(this.collapsed){
33243             this.updateBody(box.width, null);
33244         }
33245         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33246     }
33247 });
33248
33249
33250
33251
33252
33253 Roo.bootstrap.layout.South = function(config){
33254     config.region = 'south';
33255     config.cursor = 's-resize';
33256     Roo.bootstrap.layout.Split.call(this, config);
33257     if(this.split){
33258         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33259         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33260         this.split.el.addClass("roo-layout-split-v");
33261     }
33262     var size = config.initialSize || config.height;
33263     if(typeof size != "undefined"){
33264         this.el.setHeight(size);
33265     }
33266 };
33267
33268 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33269     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33270     getBox : function(){
33271         if(this.collapsed){
33272             return this.collapsedEl.getBox();
33273         }
33274         var box = this.el.getBox();
33275         if(this.split){
33276             var sh = this.split.el.getHeight();
33277             box.height += sh;
33278             box.y -= sh;
33279         }
33280         return box;
33281     },
33282     
33283     updateBox : function(box){
33284         if(this.split && !this.collapsed){
33285             var sh = this.split.el.getHeight();
33286             box.height -= sh;
33287             box.y += sh;
33288             this.split.el.setLeft(box.x);
33289             this.split.el.setTop(box.y-sh);
33290             this.split.el.setWidth(box.width);
33291         }
33292         if(this.collapsed){
33293             this.updateBody(box.width, null);
33294         }
33295         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33296     }
33297 });
33298
33299 Roo.bootstrap.layout.East = function(config){
33300     config.region = "east";
33301     config.cursor = "e-resize";
33302     Roo.bootstrap.layout.Split.call(this, config);
33303     if(this.split){
33304         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33305         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33306         this.split.el.addClass("roo-layout-split-h");
33307     }
33308     var size = config.initialSize || config.width;
33309     if(typeof size != "undefined"){
33310         this.el.setWidth(size);
33311     }
33312 };
33313 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33314     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33315     getBox : function(){
33316         if(this.collapsed){
33317             return this.collapsedEl.getBox();
33318         }
33319         var box = this.el.getBox();
33320         if(this.split){
33321             var sw = this.split.el.getWidth();
33322             box.width += sw;
33323             box.x -= sw;
33324         }
33325         return box;
33326     },
33327
33328     updateBox : function(box){
33329         if(this.split && !this.collapsed){
33330             var sw = this.split.el.getWidth();
33331             box.width -= sw;
33332             this.split.el.setLeft(box.x);
33333             this.split.el.setTop(box.y);
33334             this.split.el.setHeight(box.height);
33335             box.x += sw;
33336         }
33337         if(this.collapsed){
33338             this.updateBody(null, box.height);
33339         }
33340         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33341     }
33342 });
33343
33344 Roo.bootstrap.layout.West = function(config){
33345     config.region = "west";
33346     config.cursor = "w-resize";
33347     
33348     Roo.bootstrap.layout.Split.call(this, config);
33349     if(this.split){
33350         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33351         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33352         this.split.el.addClass("roo-layout-split-h");
33353     }
33354     
33355 };
33356 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33357     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33358     
33359     onRender: function(ctr, pos)
33360     {
33361         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33362         var size = this.config.initialSize || this.config.width;
33363         if(typeof size != "undefined"){
33364             this.el.setWidth(size);
33365         }
33366     },
33367     
33368     getBox : function(){
33369         if(this.collapsed){
33370             return this.collapsedEl.getBox();
33371         }
33372         var box = this.el.getBox();
33373         if(this.split){
33374             box.width += this.split.el.getWidth();
33375         }
33376         return box;
33377     },
33378     
33379     updateBox : function(box){
33380         if(this.split && !this.collapsed){
33381             var sw = this.split.el.getWidth();
33382             box.width -= sw;
33383             this.split.el.setLeft(box.x+box.width);
33384             this.split.el.setTop(box.y);
33385             this.split.el.setHeight(box.height);
33386         }
33387         if(this.collapsed){
33388             this.updateBody(null, box.height);
33389         }
33390         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33391     }
33392 });
33393 Roo.namespace("Roo.bootstrap.panel");/*
33394  * Based on:
33395  * Ext JS Library 1.1.1
33396  * Copyright(c) 2006-2007, Ext JS, LLC.
33397  *
33398  * Originally Released Under LGPL - original licence link has changed is not relivant.
33399  *
33400  * Fork - LGPL
33401  * <script type="text/javascript">
33402  */
33403 /**
33404  * @class Roo.ContentPanel
33405  * @extends Roo.util.Observable
33406  * A basic ContentPanel element.
33407  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33408  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33409  * @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
33410  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33411  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33412  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33413  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33414  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33415  * @cfg {String} title          The title for this panel
33416  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33417  * @cfg {String} url            Calls {@link #setUrl} with this value
33418  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33419  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33420  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33421  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33422
33423  * @constructor
33424  * Create a new ContentPanel.
33425  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33426  * @param {String/Object} config A string to set only the title or a config object
33427  * @param {String} content (optional) Set the HTML content for this panel
33428  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33429  */
33430 Roo.bootstrap.panel.Content = function( config){
33431     
33432     var el = config.el;
33433     var content = config.content;
33434
33435     if(config.autoCreate){ // xtype is available if this is called from factory
33436         el = Roo.id();
33437     }
33438     this.el = Roo.get(el);
33439     if(!this.el && config && config.autoCreate){
33440         if(typeof config.autoCreate == "object"){
33441             if(!config.autoCreate.id){
33442                 config.autoCreate.id = config.id||el;
33443             }
33444             this.el = Roo.DomHelper.append(document.body,
33445                         config.autoCreate, true);
33446         }else{
33447             var elcfg =  {   tag: "div",
33448                             cls: "roo-layout-inactive-content",
33449                             id: config.id||el
33450                             };
33451             if (config.html) {
33452                 elcfg.html = config.html;
33453                 
33454             }
33455                         
33456             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33457         }
33458     } 
33459     this.closable = false;
33460     this.loaded = false;
33461     this.active = false;
33462    
33463       
33464     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33465         
33466         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33467         
33468         this.wrapEl = this.el.wrap();
33469         var ti = [];
33470         if (config.toolbar.items) {
33471             ti = config.toolbar.items ;
33472             delete config.toolbar.items ;
33473         }
33474         
33475         var nitems = [];
33476         this.toolbar.render(this.wrapEl, 'before');
33477         for(var i =0;i < ti.length;i++) {
33478           //  Roo.log(['add child', items[i]]);
33479             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33480         }
33481         this.toolbar.items = nitems;
33482         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33483         delete config.toolbar;
33484         
33485     }
33486     /*
33487     // xtype created footer. - not sure if will work as we normally have to render first..
33488     if (this.footer && !this.footer.el && this.footer.xtype) {
33489         if (!this.wrapEl) {
33490             this.wrapEl = this.el.wrap();
33491         }
33492     
33493         this.footer.container = this.wrapEl.createChild();
33494          
33495         this.footer = Roo.factory(this.footer, Roo);
33496         
33497     }
33498     */
33499     
33500      if(typeof config == "string"){
33501         this.title = config;
33502     }else{
33503         Roo.apply(this, config);
33504     }
33505     
33506     if(this.resizeEl){
33507         this.resizeEl = Roo.get(this.resizeEl, true);
33508     }else{
33509         this.resizeEl = this.el;
33510     }
33511     // handle view.xtype
33512     
33513  
33514     
33515     
33516     this.addEvents({
33517         /**
33518          * @event activate
33519          * Fires when this panel is activated. 
33520          * @param {Roo.ContentPanel} this
33521          */
33522         "activate" : true,
33523         /**
33524          * @event deactivate
33525          * Fires when this panel is activated. 
33526          * @param {Roo.ContentPanel} this
33527          */
33528         "deactivate" : true,
33529
33530         /**
33531          * @event resize
33532          * Fires when this panel is resized if fitToFrame is true.
33533          * @param {Roo.ContentPanel} this
33534          * @param {Number} width The width after any component adjustments
33535          * @param {Number} height The height after any component adjustments
33536          */
33537         "resize" : true,
33538         
33539          /**
33540          * @event render
33541          * Fires when this tab is created
33542          * @param {Roo.ContentPanel} this
33543          */
33544         "render" : true
33545         
33546         
33547         
33548     });
33549     
33550
33551     
33552     
33553     if(this.autoScroll){
33554         this.resizeEl.setStyle("overflow", "auto");
33555     } else {
33556         // fix randome scrolling
33557         //this.el.on('scroll', function() {
33558         //    Roo.log('fix random scolling');
33559         //    this.scrollTo('top',0); 
33560         //});
33561     }
33562     content = content || this.content;
33563     if(content){
33564         this.setContent(content);
33565     }
33566     if(config && config.url){
33567         this.setUrl(this.url, this.params, this.loadOnce);
33568     }
33569     
33570     
33571     
33572     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33573     
33574     if (this.view && typeof(this.view.xtype) != 'undefined') {
33575         this.view.el = this.el.appendChild(document.createElement("div"));
33576         this.view = Roo.factory(this.view); 
33577         this.view.render  &&  this.view.render(false, '');  
33578     }
33579     
33580     
33581     this.fireEvent('render', this);
33582 };
33583
33584 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33585     tabTip:'',
33586     setRegion : function(region){
33587         this.region = region;
33588         if(region){
33589            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33590         }else{
33591            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33592         } 
33593     },
33594     
33595     /**
33596      * Returns the toolbar for this Panel if one was configured. 
33597      * @return {Roo.Toolbar} 
33598      */
33599     getToolbar : function(){
33600         return this.toolbar;
33601     },
33602     
33603     setActiveState : function(active){
33604         this.active = active;
33605         if(!active){
33606             this.fireEvent("deactivate", this);
33607         }else{
33608             this.fireEvent("activate", this);
33609         }
33610     },
33611     /**
33612      * Updates this panel's element
33613      * @param {String} content The new content
33614      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33615     */
33616     setContent : function(content, loadScripts){
33617         this.el.update(content, loadScripts);
33618     },
33619
33620     ignoreResize : function(w, h){
33621         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33622             return true;
33623         }else{
33624             this.lastSize = {width: w, height: h};
33625             return false;
33626         }
33627     },
33628     /**
33629      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33630      * @return {Roo.UpdateManager} The UpdateManager
33631      */
33632     getUpdateManager : function(){
33633         return this.el.getUpdateManager();
33634     },
33635      /**
33636      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33637      * @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:
33638 <pre><code>
33639 panel.load({
33640     url: "your-url.php",
33641     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33642     callback: yourFunction,
33643     scope: yourObject, //(optional scope)
33644     discardUrl: false,
33645     nocache: false,
33646     text: "Loading...",
33647     timeout: 30,
33648     scripts: false
33649 });
33650 </code></pre>
33651      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33652      * 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.
33653      * @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}
33654      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33655      * @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.
33656      * @return {Roo.ContentPanel} this
33657      */
33658     load : function(){
33659         var um = this.el.getUpdateManager();
33660         um.update.apply(um, arguments);
33661         return this;
33662     },
33663
33664
33665     /**
33666      * 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.
33667      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33668      * @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)
33669      * @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)
33670      * @return {Roo.UpdateManager} The UpdateManager
33671      */
33672     setUrl : function(url, params, loadOnce){
33673         if(this.refreshDelegate){
33674             this.removeListener("activate", this.refreshDelegate);
33675         }
33676         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33677         this.on("activate", this.refreshDelegate);
33678         return this.el.getUpdateManager();
33679     },
33680     
33681     _handleRefresh : function(url, params, loadOnce){
33682         if(!loadOnce || !this.loaded){
33683             var updater = this.el.getUpdateManager();
33684             updater.update(url, params, this._setLoaded.createDelegate(this));
33685         }
33686     },
33687     
33688     _setLoaded : function(){
33689         this.loaded = true;
33690     }, 
33691     
33692     /**
33693      * Returns this panel's id
33694      * @return {String} 
33695      */
33696     getId : function(){
33697         return this.el.id;
33698     },
33699     
33700     /** 
33701      * Returns this panel's element - used by regiosn to add.
33702      * @return {Roo.Element} 
33703      */
33704     getEl : function(){
33705         return this.wrapEl || this.el;
33706     },
33707     
33708    
33709     
33710     adjustForComponents : function(width, height)
33711     {
33712         //Roo.log('adjustForComponents ');
33713         if(this.resizeEl != this.el){
33714             width -= this.el.getFrameWidth('lr');
33715             height -= this.el.getFrameWidth('tb');
33716         }
33717         if(this.toolbar){
33718             var te = this.toolbar.getEl();
33719             height -= te.getHeight();
33720             te.setWidth(width);
33721         }
33722         if(this.footer){
33723             var te = this.footer.getEl();
33724             Roo.log("footer:" + te.getHeight());
33725             
33726             height -= te.getHeight();
33727             te.setWidth(width);
33728         }
33729         
33730         
33731         if(this.adjustments){
33732             width += this.adjustments[0];
33733             height += this.adjustments[1];
33734         }
33735         return {"width": width, "height": height};
33736     },
33737     
33738     setSize : function(width, height){
33739         if(this.fitToFrame && !this.ignoreResize(width, height)){
33740             if(this.fitContainer && this.resizeEl != this.el){
33741                 this.el.setSize(width, height);
33742             }
33743             var size = this.adjustForComponents(width, height);
33744             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33745             this.fireEvent('resize', this, size.width, size.height);
33746         }
33747     },
33748     
33749     /**
33750      * Returns this panel's title
33751      * @return {String} 
33752      */
33753     getTitle : function(){
33754         return this.title;
33755     },
33756     
33757     /**
33758      * Set this panel's title
33759      * @param {String} title
33760      */
33761     setTitle : function(title){
33762         this.title = title;
33763         if(this.region){
33764             this.region.updatePanelTitle(this, title);
33765         }
33766     },
33767     
33768     /**
33769      * Returns true is this panel was configured to be closable
33770      * @return {Boolean} 
33771      */
33772     isClosable : function(){
33773         return this.closable;
33774     },
33775     
33776     beforeSlide : function(){
33777         this.el.clip();
33778         this.resizeEl.clip();
33779     },
33780     
33781     afterSlide : function(){
33782         this.el.unclip();
33783         this.resizeEl.unclip();
33784     },
33785     
33786     /**
33787      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33788      *   Will fail silently if the {@link #setUrl} method has not been called.
33789      *   This does not activate the panel, just updates its content.
33790      */
33791     refresh : function(){
33792         if(this.refreshDelegate){
33793            this.loaded = false;
33794            this.refreshDelegate();
33795         }
33796     },
33797     
33798     /**
33799      * Destroys this panel
33800      */
33801     destroy : function(){
33802         this.el.removeAllListeners();
33803         var tempEl = document.createElement("span");
33804         tempEl.appendChild(this.el.dom);
33805         tempEl.innerHTML = "";
33806         this.el.remove();
33807         this.el = null;
33808     },
33809     
33810     /**
33811      * form - if the content panel contains a form - this is a reference to it.
33812      * @type {Roo.form.Form}
33813      */
33814     form : false,
33815     /**
33816      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33817      *    This contains a reference to it.
33818      * @type {Roo.View}
33819      */
33820     view : false,
33821     
33822       /**
33823      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33824      * <pre><code>
33825
33826 layout.addxtype({
33827        xtype : 'Form',
33828        items: [ .... ]
33829    }
33830 );
33831
33832 </code></pre>
33833      * @param {Object} cfg Xtype definition of item to add.
33834      */
33835     
33836     
33837     getChildContainer: function () {
33838         return this.getEl();
33839     }
33840     
33841     
33842     /*
33843         var  ret = new Roo.factory(cfg);
33844         return ret;
33845         
33846         
33847         // add form..
33848         if (cfg.xtype.match(/^Form$/)) {
33849             
33850             var el;
33851             //if (this.footer) {
33852             //    el = this.footer.container.insertSibling(false, 'before');
33853             //} else {
33854                 el = this.el.createChild();
33855             //}
33856
33857             this.form = new  Roo.form.Form(cfg);
33858             
33859             
33860             if ( this.form.allItems.length) {
33861                 this.form.render(el.dom);
33862             }
33863             return this.form;
33864         }
33865         // should only have one of theses..
33866         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33867             // views.. should not be just added - used named prop 'view''
33868             
33869             cfg.el = this.el.appendChild(document.createElement("div"));
33870             // factory?
33871             
33872             var ret = new Roo.factory(cfg);
33873              
33874              ret.render && ret.render(false, ''); // render blank..
33875             this.view = ret;
33876             return ret;
33877         }
33878         return false;
33879     }
33880     \*/
33881 });
33882  
33883 /**
33884  * @class Roo.bootstrap.panel.Grid
33885  * @extends Roo.bootstrap.panel.Content
33886  * @constructor
33887  * Create a new GridPanel.
33888  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33889  * @param {Object} config A the config object
33890   
33891  */
33892
33893
33894
33895 Roo.bootstrap.panel.Grid = function(config)
33896 {
33897     
33898       
33899     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33900         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
33901
33902     config.el = this.wrapper;
33903     //this.el = this.wrapper;
33904     
33905       if (config.container) {
33906         // ctor'ed from a Border/panel.grid
33907         
33908         
33909         this.wrapper.setStyle("overflow", "hidden");
33910         this.wrapper.addClass('roo-grid-container');
33911
33912     }
33913     
33914     
33915     if(config.toolbar){
33916         var tool_el = this.wrapper.createChild();    
33917         this.toolbar = Roo.factory(config.toolbar);
33918         var ti = [];
33919         if (config.toolbar.items) {
33920             ti = config.toolbar.items ;
33921             delete config.toolbar.items ;
33922         }
33923         
33924         var nitems = [];
33925         this.toolbar.render(tool_el);
33926         for(var i =0;i < ti.length;i++) {
33927           //  Roo.log(['add child', items[i]]);
33928             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33929         }
33930         this.toolbar.items = nitems;
33931         
33932         delete config.toolbar;
33933     }
33934     
33935     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
33936     config.grid.scrollBody = true;;
33937     config.grid.monitorWindowResize = false; // turn off autosizing
33938     config.grid.autoHeight = false;
33939     config.grid.autoWidth = false;
33940     
33941     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
33942     
33943     if (config.background) {
33944         // render grid on panel activation (if panel background)
33945         this.on('activate', function(gp) {
33946             if (!gp.grid.rendered) {
33947                 gp.grid.render(el);
33948                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
33949
33950             }
33951         });
33952             
33953     } else {
33954         this.grid.render(this.wrapper);
33955         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
33956
33957     }
33958     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
33959     // ??? needed ??? config.el = this.wrapper;
33960     
33961     
33962     
33963   
33964     // xtype created footer. - not sure if will work as we normally have to render first..
33965     if (this.footer && !this.footer.el && this.footer.xtype) {
33966         
33967         var ctr = this.grid.getView().getFooterPanel(true);
33968         this.footer.dataSource = this.grid.dataSource;
33969         this.footer = Roo.factory(this.footer, Roo);
33970         this.footer.render(ctr);
33971         
33972     }
33973     
33974     
33975     
33976     
33977      
33978 };
33979
33980 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
33981     getId : function(){
33982         return this.grid.id;
33983     },
33984     
33985     /**
33986      * Returns the grid for this panel
33987      * @return {Roo.bootstrap.Table} 
33988      */
33989     getGrid : function(){
33990         return this.grid;    
33991     },
33992     
33993     setSize : function(width, height){
33994         if(!this.ignoreResize(width, height)){
33995             var grid = this.grid;
33996             var size = this.adjustForComponents(width, height);
33997             var gridel = grid.getGridEl();
33998             gridel.setSize(size.width, size.height);
33999             /*
34000             var thd = grid.getGridEl().select('thead',true).first();
34001             var tbd = grid.getGridEl().select('tbody', true).first();
34002             if (tbd) {
34003                 tbd.setSize(width, height - thd.getHeight());
34004             }
34005             */
34006             grid.autoSize();
34007         }
34008     },
34009      
34010     
34011     
34012     beforeSlide : function(){
34013         this.grid.getView().scroller.clip();
34014     },
34015     
34016     afterSlide : function(){
34017         this.grid.getView().scroller.unclip();
34018     },
34019     
34020     destroy : function(){
34021         this.grid.destroy();
34022         delete this.grid;
34023         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34024     }
34025 });
34026
34027 /**
34028  * @class Roo.bootstrap.panel.Nest
34029  * @extends Roo.bootstrap.panel.Content
34030  * @constructor
34031  * Create a new Panel, that can contain a layout.Border.
34032  * 
34033  * 
34034  * @param {Roo.BorderLayout} layout The layout for this panel
34035  * @param {String/Object} config A string to set only the title or a config object
34036  */
34037 Roo.bootstrap.panel.Nest = function(config)
34038 {
34039     // construct with only one argument..
34040     /* FIXME - implement nicer consturctors
34041     if (layout.layout) {
34042         config = layout;
34043         layout = config.layout;
34044         delete config.layout;
34045     }
34046     if (layout.xtype && !layout.getEl) {
34047         // then layout needs constructing..
34048         layout = Roo.factory(layout, Roo);
34049     }
34050     */
34051     
34052     config.el =  config.layout.getEl();
34053     
34054     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34055     
34056     config.layout.monitorWindowResize = false; // turn off autosizing
34057     this.layout = config.layout;
34058     this.layout.getEl().addClass("roo-layout-nested-layout");
34059     
34060     
34061     
34062     
34063 };
34064
34065 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34066
34067     setSize : function(width, height){
34068         if(!this.ignoreResize(width, height)){
34069             var size = this.adjustForComponents(width, height);
34070             var el = this.layout.getEl();
34071             el.setSize(size.width, size.height);
34072             var touch = el.dom.offsetWidth;
34073             this.layout.layout();
34074             // ie requires a double layout on the first pass
34075             if(Roo.isIE && !this.initialized){
34076                 this.initialized = true;
34077                 this.layout.layout();
34078             }
34079         }
34080     },
34081     
34082     // activate all subpanels if not currently active..
34083     
34084     setActiveState : function(active){
34085         this.active = active;
34086         if(!active){
34087             this.fireEvent("deactivate", this);
34088             return;
34089         }
34090         
34091         this.fireEvent("activate", this);
34092         // not sure if this should happen before or after..
34093         if (!this.layout) {
34094             return; // should not happen..
34095         }
34096         var reg = false;
34097         for (var r in this.layout.regions) {
34098             reg = this.layout.getRegion(r);
34099             if (reg.getActivePanel()) {
34100                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34101                 reg.setActivePanel(reg.getActivePanel());
34102                 continue;
34103             }
34104             if (!reg.panels.length) {
34105                 continue;
34106             }
34107             reg.showPanel(reg.getPanel(0));
34108         }
34109         
34110         
34111         
34112         
34113     },
34114     
34115     /**
34116      * Returns the nested BorderLayout for this panel
34117      * @return {Roo.BorderLayout} 
34118      */
34119     getLayout : function(){
34120         return this.layout;
34121     },
34122     
34123      /**
34124      * Adds a xtype elements to the layout of the nested panel
34125      * <pre><code>
34126
34127 panel.addxtype({
34128        xtype : 'ContentPanel',
34129        region: 'west',
34130        items: [ .... ]
34131    }
34132 );
34133
34134 panel.addxtype({
34135         xtype : 'NestedLayoutPanel',
34136         region: 'west',
34137         layout: {
34138            center: { },
34139            west: { }   
34140         },
34141         items : [ ... list of content panels or nested layout panels.. ]
34142    }
34143 );
34144 </code></pre>
34145      * @param {Object} cfg Xtype definition of item to add.
34146      */
34147     addxtype : function(cfg) {
34148         return this.layout.addxtype(cfg);
34149     
34150     }
34151 });        /*
34152  * Based on:
34153  * Ext JS Library 1.1.1
34154  * Copyright(c) 2006-2007, Ext JS, LLC.
34155  *
34156  * Originally Released Under LGPL - original licence link has changed is not relivant.
34157  *
34158  * Fork - LGPL
34159  * <script type="text/javascript">
34160  */
34161 /**
34162  * @class Roo.TabPanel
34163  * @extends Roo.util.Observable
34164  * A lightweight tab container.
34165  * <br><br>
34166  * Usage:
34167  * <pre><code>
34168 // basic tabs 1, built from existing content
34169 var tabs = new Roo.TabPanel("tabs1");
34170 tabs.addTab("script", "View Script");
34171 tabs.addTab("markup", "View Markup");
34172 tabs.activate("script");
34173
34174 // more advanced tabs, built from javascript
34175 var jtabs = new Roo.TabPanel("jtabs");
34176 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34177
34178 // set up the UpdateManager
34179 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34180 var updater = tab2.getUpdateManager();
34181 updater.setDefaultUrl("ajax1.htm");
34182 tab2.on('activate', updater.refresh, updater, true);
34183
34184 // Use setUrl for Ajax loading
34185 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34186 tab3.setUrl("ajax2.htm", null, true);
34187
34188 // Disabled tab
34189 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34190 tab4.disable();
34191
34192 jtabs.activate("jtabs-1");
34193  * </code></pre>
34194  * @constructor
34195  * Create a new TabPanel.
34196  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34197  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34198  */
34199 Roo.bootstrap.panel.Tabs = function(config){
34200     /**
34201     * The container element for this TabPanel.
34202     * @type Roo.Element
34203     */
34204     this.el = Roo.get(config.el);
34205     delete config.el;
34206     if(config){
34207         if(typeof config == "boolean"){
34208             this.tabPosition = config ? "bottom" : "top";
34209         }else{
34210             Roo.apply(this, config);
34211         }
34212     }
34213     
34214     if(this.tabPosition == "bottom"){
34215         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34216         this.el.addClass("roo-tabs-bottom");
34217     }
34218     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34219     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34220     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34221     if(Roo.isIE){
34222         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34223     }
34224     if(this.tabPosition != "bottom"){
34225         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34226          * @type Roo.Element
34227          */
34228         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34229         this.el.addClass("roo-tabs-top");
34230     }
34231     this.items = [];
34232
34233     this.bodyEl.setStyle("position", "relative");
34234
34235     this.active = null;
34236     this.activateDelegate = this.activate.createDelegate(this);
34237
34238     this.addEvents({
34239         /**
34240          * @event tabchange
34241          * Fires when the active tab changes
34242          * @param {Roo.TabPanel} this
34243          * @param {Roo.TabPanelItem} activePanel The new active tab
34244          */
34245         "tabchange": true,
34246         /**
34247          * @event beforetabchange
34248          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34249          * @param {Roo.TabPanel} this
34250          * @param {Object} e Set cancel to true on this object to cancel the tab change
34251          * @param {Roo.TabPanelItem} tab The tab being changed to
34252          */
34253         "beforetabchange" : true
34254     });
34255
34256     Roo.EventManager.onWindowResize(this.onResize, this);
34257     this.cpad = this.el.getPadding("lr");
34258     this.hiddenCount = 0;
34259
34260
34261     // toolbar on the tabbar support...
34262     if (this.toolbar) {
34263         alert("no toolbar support yet");
34264         this.toolbar  = false;
34265         /*
34266         var tcfg = this.toolbar;
34267         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34268         this.toolbar = new Roo.Toolbar(tcfg);
34269         if (Roo.isSafari) {
34270             var tbl = tcfg.container.child('table', true);
34271             tbl.setAttribute('width', '100%');
34272         }
34273         */
34274         
34275     }
34276    
34277
34278
34279     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34280 };
34281
34282 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34283     /*
34284      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34285      */
34286     tabPosition : "top",
34287     /*
34288      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34289      */
34290     currentTabWidth : 0,
34291     /*
34292      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34293      */
34294     minTabWidth : 40,
34295     /*
34296      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34297      */
34298     maxTabWidth : 250,
34299     /*
34300      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34301      */
34302     preferredTabWidth : 175,
34303     /*
34304      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34305      */
34306     resizeTabs : false,
34307     /*
34308      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34309      */
34310     monitorResize : true,
34311     /*
34312      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34313      */
34314     toolbar : false,
34315
34316     /**
34317      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34318      * @param {String} id The id of the div to use <b>or create</b>
34319      * @param {String} text The text for the tab
34320      * @param {String} content (optional) Content to put in the TabPanelItem body
34321      * @param {Boolean} closable (optional) True to create a close icon on the tab
34322      * @return {Roo.TabPanelItem} The created TabPanelItem
34323      */
34324     addTab : function(id, text, content, closable)
34325     {
34326         var item = new Roo.bootstrap.panel.TabItem({
34327             panel: this,
34328             id : id,
34329             text : text,
34330             closable : closable
34331         });
34332         this.addTabItem(item);
34333         if(content){
34334             item.setContent(content);
34335         }
34336         return item;
34337     },
34338
34339     /**
34340      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34341      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34342      * @return {Roo.TabPanelItem}
34343      */
34344     getTab : function(id){
34345         return this.items[id];
34346     },
34347
34348     /**
34349      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34350      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34351      */
34352     hideTab : function(id){
34353         var t = this.items[id];
34354         if(!t.isHidden()){
34355            t.setHidden(true);
34356            this.hiddenCount++;
34357            this.autoSizeTabs();
34358         }
34359     },
34360
34361     /**
34362      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34363      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34364      */
34365     unhideTab : function(id){
34366         var t = this.items[id];
34367         if(t.isHidden()){
34368            t.setHidden(false);
34369            this.hiddenCount--;
34370            this.autoSizeTabs();
34371         }
34372     },
34373
34374     /**
34375      * Adds an existing {@link Roo.TabPanelItem}.
34376      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34377      */
34378     addTabItem : function(item){
34379         this.items[item.id] = item;
34380         this.items.push(item);
34381       //  if(this.resizeTabs){
34382     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34383   //         this.autoSizeTabs();
34384 //        }else{
34385 //            item.autoSize();
34386        // }
34387     },
34388
34389     /**
34390      * Removes a {@link Roo.TabPanelItem}.
34391      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34392      */
34393     removeTab : function(id){
34394         var items = this.items;
34395         var tab = items[id];
34396         if(!tab) { return; }
34397         var index = items.indexOf(tab);
34398         if(this.active == tab && items.length > 1){
34399             var newTab = this.getNextAvailable(index);
34400             if(newTab) {
34401                 newTab.activate();
34402             }
34403         }
34404         this.stripEl.dom.removeChild(tab.pnode.dom);
34405         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34406             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34407         }
34408         items.splice(index, 1);
34409         delete this.items[tab.id];
34410         tab.fireEvent("close", tab);
34411         tab.purgeListeners();
34412         this.autoSizeTabs();
34413     },
34414
34415     getNextAvailable : function(start){
34416         var items = this.items;
34417         var index = start;
34418         // look for a next tab that will slide over to
34419         // replace the one being removed
34420         while(index < items.length){
34421             var item = items[++index];
34422             if(item && !item.isHidden()){
34423                 return item;
34424             }
34425         }
34426         // if one isn't found select the previous tab (on the left)
34427         index = start;
34428         while(index >= 0){
34429             var item = items[--index];
34430             if(item && !item.isHidden()){
34431                 return item;
34432             }
34433         }
34434         return null;
34435     },
34436
34437     /**
34438      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34439      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34440      */
34441     disableTab : function(id){
34442         var tab = this.items[id];
34443         if(tab && this.active != tab){
34444             tab.disable();
34445         }
34446     },
34447
34448     /**
34449      * Enables a {@link Roo.TabPanelItem} that is disabled.
34450      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34451      */
34452     enableTab : function(id){
34453         var tab = this.items[id];
34454         tab.enable();
34455     },
34456
34457     /**
34458      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34459      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34460      * @return {Roo.TabPanelItem} The TabPanelItem.
34461      */
34462     activate : function(id){
34463         var tab = this.items[id];
34464         if(!tab){
34465             return null;
34466         }
34467         if(tab == this.active || tab.disabled){
34468             return tab;
34469         }
34470         var e = {};
34471         this.fireEvent("beforetabchange", this, e, tab);
34472         if(e.cancel !== true && !tab.disabled){
34473             if(this.active){
34474                 this.active.hide();
34475             }
34476             this.active = this.items[id];
34477             this.active.show();
34478             this.fireEvent("tabchange", this, this.active);
34479         }
34480         return tab;
34481     },
34482
34483     /**
34484      * Gets the active {@link Roo.TabPanelItem}.
34485      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34486      */
34487     getActiveTab : function(){
34488         return this.active;
34489     },
34490
34491     /**
34492      * Updates the tab body element to fit the height of the container element
34493      * for overflow scrolling
34494      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34495      */
34496     syncHeight : function(targetHeight){
34497         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34498         var bm = this.bodyEl.getMargins();
34499         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34500         this.bodyEl.setHeight(newHeight);
34501         return newHeight;
34502     },
34503
34504     onResize : function(){
34505         if(this.monitorResize){
34506             this.autoSizeTabs();
34507         }
34508     },
34509
34510     /**
34511      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34512      */
34513     beginUpdate : function(){
34514         this.updating = true;
34515     },
34516
34517     /**
34518      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34519      */
34520     endUpdate : function(){
34521         this.updating = false;
34522         this.autoSizeTabs();
34523     },
34524
34525     /**
34526      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34527      */
34528     autoSizeTabs : function(){
34529         var count = this.items.length;
34530         var vcount = count - this.hiddenCount;
34531         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34532             return;
34533         }
34534         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34535         var availWidth = Math.floor(w / vcount);
34536         var b = this.stripBody;
34537         if(b.getWidth() > w){
34538             var tabs = this.items;
34539             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34540             if(availWidth < this.minTabWidth){
34541                 /*if(!this.sleft){    // incomplete scrolling code
34542                     this.createScrollButtons();
34543                 }
34544                 this.showScroll();
34545                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34546             }
34547         }else{
34548             if(this.currentTabWidth < this.preferredTabWidth){
34549                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34550             }
34551         }
34552     },
34553
34554     /**
34555      * Returns the number of tabs in this TabPanel.
34556      * @return {Number}
34557      */
34558      getCount : function(){
34559          return this.items.length;
34560      },
34561
34562     /**
34563      * Resizes all the tabs to the passed width
34564      * @param {Number} The new width
34565      */
34566     setTabWidth : function(width){
34567         this.currentTabWidth = width;
34568         for(var i = 0, len = this.items.length; i < len; i++) {
34569                 if(!this.items[i].isHidden()) {
34570                 this.items[i].setWidth(width);
34571             }
34572         }
34573     },
34574
34575     /**
34576      * Destroys this TabPanel
34577      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34578      */
34579     destroy : function(removeEl){
34580         Roo.EventManager.removeResizeListener(this.onResize, this);
34581         for(var i = 0, len = this.items.length; i < len; i++){
34582             this.items[i].purgeListeners();
34583         }
34584         if(removeEl === true){
34585             this.el.update("");
34586             this.el.remove();
34587         }
34588     },
34589     
34590     createStrip : function(container)
34591     {
34592         var strip = document.createElement("nav");
34593         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34594         container.appendChild(strip);
34595         return strip;
34596     },
34597     
34598     createStripList : function(strip)
34599     {
34600         // div wrapper for retard IE
34601         // returns the "tr" element.
34602         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34603         //'<div class="x-tabs-strip-wrap">'+
34604           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34605           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34606         return strip.firstChild; //.firstChild.firstChild.firstChild;
34607     },
34608     createBody : function(container)
34609     {
34610         var body = document.createElement("div");
34611         Roo.id(body, "tab-body");
34612         //Roo.fly(body).addClass("x-tabs-body");
34613         Roo.fly(body).addClass("tab-content");
34614         container.appendChild(body);
34615         return body;
34616     },
34617     createItemBody :function(bodyEl, id){
34618         var body = Roo.getDom(id);
34619         if(!body){
34620             body = document.createElement("div");
34621             body.id = id;
34622         }
34623         //Roo.fly(body).addClass("x-tabs-item-body");
34624         Roo.fly(body).addClass("tab-pane");
34625          bodyEl.insertBefore(body, bodyEl.firstChild);
34626         return body;
34627     },
34628     /** @private */
34629     createStripElements :  function(stripEl, text, closable)
34630     {
34631         var td = document.createElement("li"); // was td..
34632         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34633         //stripEl.appendChild(td);
34634         /*if(closable){
34635             td.className = "x-tabs-closable";
34636             if(!this.closeTpl){
34637                 this.closeTpl = new Roo.Template(
34638                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34639                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34640                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34641                 );
34642             }
34643             var el = this.closeTpl.overwrite(td, {"text": text});
34644             var close = el.getElementsByTagName("div")[0];
34645             var inner = el.getElementsByTagName("em")[0];
34646             return {"el": el, "close": close, "inner": inner};
34647         } else {
34648         */
34649         // not sure what this is..
34650             if(!this.tabTpl){
34651                 //this.tabTpl = new Roo.Template(
34652                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34653                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34654                 //);
34655                 this.tabTpl = new Roo.Template(
34656                    '<a href="#">' +
34657                    '<span unselectable="on"' +
34658                             (this.disableTooltips ? '' : ' title="{text}"') +
34659                             ' >{text}</span></span></a>'
34660                 );
34661                 
34662             }
34663             var el = this.tabTpl.overwrite(td, {"text": text});
34664             var inner = el.getElementsByTagName("span")[0];
34665             return {"el": el, "inner": inner};
34666         //}
34667     }
34668         
34669     
34670 });
34671
34672 /**
34673  * @class Roo.TabPanelItem
34674  * @extends Roo.util.Observable
34675  * Represents an individual item (tab plus body) in a TabPanel.
34676  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34677  * @param {String} id The id of this TabPanelItem
34678  * @param {String} text The text for the tab of this TabPanelItem
34679  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34680  */
34681 Roo.bootstrap.panel.TabItem = function(config){
34682     /**
34683      * The {@link Roo.TabPanel} this TabPanelItem belongs to
34684      * @type Roo.TabPanel
34685      */
34686     this.tabPanel = config.panel;
34687     /**
34688      * The id for this TabPanelItem
34689      * @type String
34690      */
34691     this.id = config.id;
34692     /** @private */
34693     this.disabled = false;
34694     /** @private */
34695     this.text = config.text;
34696     /** @private */
34697     this.loaded = false;
34698     this.closable = config.closable;
34699
34700     /**
34701      * The body element for this TabPanelItem.
34702      * @type Roo.Element
34703      */
34704     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34705     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34706     this.bodyEl.setStyle("display", "block");
34707     this.bodyEl.setStyle("zoom", "1");
34708     //this.hideAction();
34709
34710     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34711     /** @private */
34712     this.el = Roo.get(els.el);
34713     this.inner = Roo.get(els.inner, true);
34714     this.textEl = Roo.get(this.el.dom.firstChild, true);
34715     this.pnode = Roo.get(els.el.parentNode, true);
34716     this.el.on("mousedown", this.onTabMouseDown, this);
34717     this.el.on("click", this.onTabClick, this);
34718     /** @private */
34719     if(config.closable){
34720         var c = Roo.get(els.close, true);
34721         c.dom.title = this.closeText;
34722         c.addClassOnOver("close-over");
34723         c.on("click", this.closeClick, this);
34724      }
34725
34726     this.addEvents({
34727          /**
34728          * @event activate
34729          * Fires when this tab becomes the active tab.
34730          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34731          * @param {Roo.TabPanelItem} this
34732          */
34733         "activate": true,
34734         /**
34735          * @event beforeclose
34736          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34737          * @param {Roo.TabPanelItem} this
34738          * @param {Object} e Set cancel to true on this object to cancel the close.
34739          */
34740         "beforeclose": true,
34741         /**
34742          * @event close
34743          * Fires when this tab is closed.
34744          * @param {Roo.TabPanelItem} this
34745          */
34746          "close": true,
34747         /**
34748          * @event deactivate
34749          * Fires when this tab is no longer the active tab.
34750          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34751          * @param {Roo.TabPanelItem} this
34752          */
34753          "deactivate" : true
34754     });
34755     this.hidden = false;
34756
34757     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34758 };
34759
34760 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34761            {
34762     purgeListeners : function(){
34763        Roo.util.Observable.prototype.purgeListeners.call(this);
34764        this.el.removeAllListeners();
34765     },
34766     /**
34767      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34768      */
34769     show : function(){
34770         this.pnode.addClass("active");
34771         this.showAction();
34772         if(Roo.isOpera){
34773             this.tabPanel.stripWrap.repaint();
34774         }
34775         this.fireEvent("activate", this.tabPanel, this);
34776     },
34777
34778     /**
34779      * Returns true if this tab is the active tab.
34780      * @return {Boolean}
34781      */
34782     isActive : function(){
34783         return this.tabPanel.getActiveTab() == this;
34784     },
34785
34786     /**
34787      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34788      */
34789     hide : function(){
34790         this.pnode.removeClass("active");
34791         this.hideAction();
34792         this.fireEvent("deactivate", this.tabPanel, this);
34793     },
34794
34795     hideAction : function(){
34796         this.bodyEl.hide();
34797         this.bodyEl.setStyle("position", "absolute");
34798         this.bodyEl.setLeft("-20000px");
34799         this.bodyEl.setTop("-20000px");
34800     },
34801
34802     showAction : function(){
34803         this.bodyEl.setStyle("position", "relative");
34804         this.bodyEl.setTop("");
34805         this.bodyEl.setLeft("");
34806         this.bodyEl.show();
34807     },
34808
34809     /**
34810      * Set the tooltip for the tab.
34811      * @param {String} tooltip The tab's tooltip
34812      */
34813     setTooltip : function(text){
34814         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34815             this.textEl.dom.qtip = text;
34816             this.textEl.dom.removeAttribute('title');
34817         }else{
34818             this.textEl.dom.title = text;
34819         }
34820     },
34821
34822     onTabClick : function(e){
34823         e.preventDefault();
34824         this.tabPanel.activate(this.id);
34825     },
34826
34827     onTabMouseDown : function(e){
34828         e.preventDefault();
34829         this.tabPanel.activate(this.id);
34830     },
34831 /*
34832     getWidth : function(){
34833         return this.inner.getWidth();
34834     },
34835
34836     setWidth : function(width){
34837         var iwidth = width - this.pnode.getPadding("lr");
34838         this.inner.setWidth(iwidth);
34839         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34840         this.pnode.setWidth(width);
34841     },
34842 */
34843     /**
34844      * Show or hide the tab
34845      * @param {Boolean} hidden True to hide or false to show.
34846      */
34847     setHidden : function(hidden){
34848         this.hidden = hidden;
34849         this.pnode.setStyle("display", hidden ? "none" : "");
34850     },
34851
34852     /**
34853      * Returns true if this tab is "hidden"
34854      * @return {Boolean}
34855      */
34856     isHidden : function(){
34857         return this.hidden;
34858     },
34859
34860     /**
34861      * Returns the text for this tab
34862      * @return {String}
34863      */
34864     getText : function(){
34865         return this.text;
34866     },
34867     /*
34868     autoSize : function(){
34869         //this.el.beginMeasure();
34870         this.textEl.setWidth(1);
34871         /*
34872          *  #2804 [new] Tabs in Roojs
34873          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34874          */
34875         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34876         //this.el.endMeasure();
34877     //},
34878
34879     /**
34880      * Sets the text for the tab (Note: this also sets the tooltip text)
34881      * @param {String} text The tab's text and tooltip
34882      */
34883     setText : function(text){
34884         this.text = text;
34885         this.textEl.update(text);
34886         this.setTooltip(text);
34887         //if(!this.tabPanel.resizeTabs){
34888         //    this.autoSize();
34889         //}
34890     },
34891     /**
34892      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34893      */
34894     activate : function(){
34895         this.tabPanel.activate(this.id);
34896     },
34897
34898     /**
34899      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34900      */
34901     disable : function(){
34902         if(this.tabPanel.active != this){
34903             this.disabled = true;
34904             this.pnode.addClass("disabled");
34905         }
34906     },
34907
34908     /**
34909      * Enables this TabPanelItem if it was previously disabled.
34910      */
34911     enable : function(){
34912         this.disabled = false;
34913         this.pnode.removeClass("disabled");
34914     },
34915
34916     /**
34917      * Sets the content for this TabPanelItem.
34918      * @param {String} content The content
34919      * @param {Boolean} loadScripts true to look for and load scripts
34920      */
34921     setContent : function(content, loadScripts){
34922         this.bodyEl.update(content, loadScripts);
34923     },
34924
34925     /**
34926      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
34927      * @return {Roo.UpdateManager} The UpdateManager
34928      */
34929     getUpdateManager : function(){
34930         return this.bodyEl.getUpdateManager();
34931     },
34932
34933     /**
34934      * Set a URL to be used to load the content for this TabPanelItem.
34935      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
34936      * @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)
34937      * @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)
34938      * @return {Roo.UpdateManager} The UpdateManager
34939      */
34940     setUrl : function(url, params, loadOnce){
34941         if(this.refreshDelegate){
34942             this.un('activate', this.refreshDelegate);
34943         }
34944         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34945         this.on("activate", this.refreshDelegate);
34946         return this.bodyEl.getUpdateManager();
34947     },
34948
34949     /** @private */
34950     _handleRefresh : function(url, params, loadOnce){
34951         if(!loadOnce || !this.loaded){
34952             var updater = this.bodyEl.getUpdateManager();
34953             updater.update(url, params, this._setLoaded.createDelegate(this));
34954         }
34955     },
34956
34957     /**
34958      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
34959      *   Will fail silently if the setUrl method has not been called.
34960      *   This does not activate the panel, just updates its content.
34961      */
34962     refresh : function(){
34963         if(this.refreshDelegate){
34964            this.loaded = false;
34965            this.refreshDelegate();
34966         }
34967     },
34968
34969     /** @private */
34970     _setLoaded : function(){
34971         this.loaded = true;
34972     },
34973
34974     /** @private */
34975     closeClick : function(e){
34976         var o = {};
34977         e.stopEvent();
34978         this.fireEvent("beforeclose", this, o);
34979         if(o.cancel !== true){
34980             this.tabPanel.removeTab(this.id);
34981         }
34982     },
34983     /**
34984      * The text displayed in the tooltip for the close icon.
34985      * @type String
34986      */
34987     closeText : "Close this tab"
34988 });