Roo/bootstrap/MasonryBrick.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  * 
2460  * @constructor
2461  * Create a new Modal Dialog
2462  * @param {Object} config The config object
2463  */
2464
2465 Roo.bootstrap.Modal = function(config){
2466     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2467     this.addEvents({
2468         // raw events
2469         /**
2470          * @event btnclick
2471          * The raw btnclick event for the button
2472          * @param {Roo.EventObject} e
2473          */
2474         "btnclick" : true
2475     });
2476     this.buttons = this.buttons || [];
2477      
2478     if (this.tmpl) {
2479         this.tmpl = Roo.factory(this.tmpl);
2480     }
2481     
2482 };
2483
2484 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2485     
2486     title : 'test dialog',
2487    
2488     buttons : false,
2489     
2490     // set on load...
2491      
2492     html: false,
2493     
2494     tmp: false,
2495     
2496     specificTitle: false,
2497     
2498     buttonPosition: 'right',
2499     
2500     allow_close : true,
2501     
2502     animate : true,
2503     
2504     
2505      // private
2506     bodyEl:  false,
2507     footerEl:  false,
2508     titleEl:  false,
2509     closeEl:  false,
2510     
2511     
2512     onRender : function(ct, position)
2513     {
2514         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2515      
2516         if(!this.el){
2517             var cfg = Roo.apply({},  this.getAutoCreate());
2518             cfg.id = Roo.id();
2519             //if(!cfg.name){
2520             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2521             //}
2522             //if (!cfg.name.length) {
2523             //    delete cfg.name;
2524            // }
2525             if (this.cls) {
2526                 cfg.cls += ' ' + this.cls;
2527             }
2528             if (this.style) {
2529                 cfg.style = this.style;
2530             }
2531             this.el = Roo.get(document.body).createChild(cfg, position);
2532         }
2533         //var type = this.el.dom.type;
2534         
2535         
2536         if(this.tabIndex !== undefined){
2537             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2538         }
2539         
2540         
2541         this.bodyEl = this.el.select('.modal-body',true).first();
2542         this.closeEl = this.el.select('.modal-header .close', true).first();
2543         this.footerEl = this.el.select('.modal-footer',true).first();
2544         this.titleEl = this.el.select('.modal-title',true).first();
2545         
2546         
2547          
2548         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2549         this.maskEl.enableDisplayMode("block");
2550         this.maskEl.hide();
2551         //this.el.addClass("x-dlg-modal");
2552     
2553         if (this.buttons.length) {
2554             Roo.each(this.buttons, function(bb) {
2555                 var b = Roo.apply({}, bb);
2556                 b.xns = b.xns || Roo.bootstrap;
2557                 b.xtype = b.xtype || 'Button';
2558                 if (typeof(b.listeners) == 'undefined') {
2559                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2560                 }
2561                 
2562                 var btn = Roo.factory(b);
2563                 
2564                 btn.render(this.el.select('.modal-footer div').first());
2565                 
2566             },this);
2567         }
2568         // render the children.
2569         var nitems = [];
2570         
2571         if(typeof(this.items) != 'undefined'){
2572             var items = this.items;
2573             delete this.items;
2574
2575             for(var i =0;i < items.length;i++) {
2576                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2577             }
2578         }
2579         
2580         this.items = nitems;
2581         
2582         // where are these used - they used to be body/close/footer
2583         
2584        
2585         this.initEvents();
2586         //this.el.addClass([this.fieldClass, this.cls]);
2587         
2588     },
2589     
2590     getAutoCreate : function(){
2591         
2592         
2593         var bdy = {
2594                 cls : 'modal-body',
2595                 html : this.html || ''
2596         };
2597         
2598         var title = {
2599             tag: 'h4',
2600             cls : 'modal-title',
2601             html : this.title
2602         };
2603         
2604         if(this.specificTitle){
2605             title = this.title;
2606             
2607         };
2608         
2609         var header = [];
2610         if (this.allow_close) {
2611             header.push({
2612                 tag: 'button',
2613                 cls : 'close',
2614                 html : '&times'
2615             });
2616         }
2617         header.push(title);
2618         
2619         var modal = {
2620             cls: "modal",
2621             style : 'display: none',
2622             cn : [
2623                 {
2624                     cls: "modal-dialog",
2625                     cn : [
2626                         {
2627                             cls : "modal-content",
2628                             cn : [
2629                                 {
2630                                     cls : 'modal-header',
2631                                     cn : header
2632                                 },
2633                                 bdy,
2634                                 {
2635                                     cls : 'modal-footer',
2636                                     cn : [
2637                                         {
2638                                             tag: 'div',
2639                                             cls: 'btn-' + this.buttonPosition
2640                                         }
2641                                     ]
2642                                     
2643                                 }
2644                                 
2645                                 
2646                             ]
2647                             
2648                         }
2649                     ]
2650                         
2651                 }
2652             ]
2653         };
2654         
2655         if(this.animate){
2656             modal.cls += ' fade';
2657         }
2658         
2659         return modal;
2660           
2661     },
2662     getChildContainer : function() {
2663          
2664          return this.bodyEl;
2665         
2666     },
2667     getButtonContainer : function() {
2668          return this.el.select('.modal-footer div',true).first();
2669         
2670     },
2671     initEvents : function()
2672     {
2673         if (this.allow_close) {
2674             this.closeEl.on('click', this.hide, this);
2675         }
2676         
2677         var _this = this;
2678         
2679         window.addEventListener("resize", function() { _this.resize(); } );
2680
2681     },
2682     
2683     resize : function()
2684     {
2685         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2686     },
2687     
2688     show : function() {
2689         
2690         if (!this.rendered) {
2691             this.render();
2692         }
2693         
2694         this.el.setStyle('display', 'block');
2695         
2696         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2697             var _this = this;
2698             (function(){
2699                 this.el.addClass('in');
2700             }).defer(50, this);
2701         }else{
2702             this.el.addClass('in');
2703             
2704         }
2705         
2706         // not sure how we can show data in here.. 
2707         //if (this.tmpl) {
2708         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2709         //}
2710         
2711         Roo.get(document.body).addClass("x-body-masked");
2712         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2713         this.maskEl.show();
2714         this.el.setStyle('zIndex', '10001');
2715        
2716         this.fireEvent('show', this);
2717          
2718         
2719         
2720     },
2721     hide : function()
2722     {
2723         this.maskEl.hide();
2724         Roo.get(document.body).removeClass("x-body-masked");
2725         this.el.removeClass('in');
2726         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2727         
2728         if(this.animate){ // why
2729             var _this = this;
2730             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2731         }else{
2732             this.el.setStyle('display', 'none');
2733         }
2734         
2735         this.fireEvent('hide', this);
2736     },
2737     
2738     addButton : function(str, cb)
2739     {
2740          
2741         
2742         var b = Roo.apply({}, { html : str } );
2743         b.xns = b.xns || Roo.bootstrap;
2744         b.xtype = b.xtype || 'Button';
2745         if (typeof(b.listeners) == 'undefined') {
2746             b.listeners = { click : cb.createDelegate(this)  };
2747         }
2748         
2749         var btn = Roo.factory(b);
2750            
2751         btn.render(this.el.select('.modal-footer div').first());
2752         
2753         return btn;   
2754        
2755     },
2756     
2757     setDefaultButton : function(btn)
2758     {
2759         //this.el.select('.modal-footer').()
2760     },
2761     diff : false,
2762     
2763     resizeTo: function(w,h)
2764     {
2765         // skip.. ?? why??
2766         
2767         this.el.select('.modal-dialog',true).first().setWidth(w);
2768         if (this.diff === false) {
2769             this.diff = this.el.select('.modal-dialog',true).first().getHeight() - this.el.select('.modal-body',true).first().getHeight();
2770         }
2771         
2772         this.el.select('.modal-body',true).first().setHeight(h-this.diff);
2773         
2774         
2775     },
2776     setContentSize  : function(w, h)
2777     {
2778         
2779     },
2780     onButtonClick: function(btn,e)
2781     {
2782         //Roo.log([a,b,c]);
2783         this.fireEvent('btnclick', btn.name, e);
2784     },
2785      /**
2786      * Set the title of the Dialog
2787      * @param {String} str new Title
2788      */
2789     setTitle: function(str) {
2790         this.titleEl.dom.innerHTML = str;    
2791     },
2792     /**
2793      * Set the body of the Dialog
2794      * @param {String} str new Title
2795      */
2796     setBody: function(str) {
2797         this.bodyEl.dom.innerHTML = str;    
2798     },
2799     /**
2800      * Set the body of the Dialog using the template
2801      * @param {Obj} data - apply this data to the template and replace the body contents.
2802      */
2803     applyBody: function(obj)
2804     {
2805         if (!this.tmpl) {
2806             Roo.log("Error - using apply Body without a template");
2807             //code
2808         }
2809         this.tmpl.overwrite(this.bodyEl, obj);
2810     }
2811     
2812 });
2813
2814
2815 Roo.apply(Roo.bootstrap.Modal,  {
2816     /**
2817          * Button config that displays a single OK button
2818          * @type Object
2819          */
2820         OK :  [{
2821             name : 'ok',
2822             weight : 'primary',
2823             html : 'OK'
2824         }], 
2825         /**
2826          * Button config that displays Yes and No buttons
2827          * @type Object
2828          */
2829         YESNO : [
2830             {
2831                 name  : 'no',
2832                 html : 'No'
2833             },
2834             {
2835                 name  :'yes',
2836                 weight : 'primary',
2837                 html : 'Yes'
2838             }
2839         ],
2840         
2841         /**
2842          * Button config that displays OK and Cancel buttons
2843          * @type Object
2844          */
2845         OKCANCEL : [
2846             {
2847                name : 'cancel',
2848                 html : 'Cancel'
2849             },
2850             {
2851                 name : 'ok',
2852                 weight : 'primary',
2853                 html : 'OK'
2854             }
2855         ],
2856         /**
2857          * Button config that displays Yes, No and Cancel buttons
2858          * @type Object
2859          */
2860         YESNOCANCEL : [
2861             {
2862                 name : 'yes',
2863                 weight : 'primary',
2864                 html : 'Yes'
2865             },
2866             {
2867                 name : 'no',
2868                 html : 'No'
2869             },
2870             {
2871                 name : 'cancel',
2872                 html : 'Cancel'
2873             }
2874         ]
2875 });
2876  
2877  /*
2878  * - LGPL
2879  *
2880  * messagebox - can be used as a replace
2881  * 
2882  */
2883 /**
2884  * @class Roo.MessageBox
2885  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2886  * Example usage:
2887  *<pre><code>
2888 // Basic alert:
2889 Roo.Msg.alert('Status', 'Changes saved successfully.');
2890
2891 // Prompt for user data:
2892 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2893     if (btn == 'ok'){
2894         // process text value...
2895     }
2896 });
2897
2898 // Show a dialog using config options:
2899 Roo.Msg.show({
2900    title:'Save Changes?',
2901    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2902    buttons: Roo.Msg.YESNOCANCEL,
2903    fn: processResult,
2904    animEl: 'elId'
2905 });
2906 </code></pre>
2907  * @singleton
2908  */
2909 Roo.bootstrap.MessageBox = function(){
2910     var dlg, opt, mask, waitTimer;
2911     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2912     var buttons, activeTextEl, bwidth;
2913
2914     
2915     // private
2916     var handleButton = function(button){
2917         dlg.hide();
2918         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2919     };
2920
2921     // private
2922     var handleHide = function(){
2923         if(opt && opt.cls){
2924             dlg.el.removeClass(opt.cls);
2925         }
2926         //if(waitTimer){
2927         //    Roo.TaskMgr.stop(waitTimer);
2928         //    waitTimer = null;
2929         //}
2930     };
2931
2932     // private
2933     var updateButtons = function(b){
2934         var width = 0;
2935         if(!b){
2936             buttons["ok"].hide();
2937             buttons["cancel"].hide();
2938             buttons["yes"].hide();
2939             buttons["no"].hide();
2940             //dlg.footer.dom.style.display = 'none';
2941             return width;
2942         }
2943         dlg.footerEl.dom.style.display = '';
2944         for(var k in buttons){
2945             if(typeof buttons[k] != "function"){
2946                 if(b[k]){
2947                     buttons[k].show();
2948                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2949                     width += buttons[k].el.getWidth()+15;
2950                 }else{
2951                     buttons[k].hide();
2952                 }
2953             }
2954         }
2955         return width;
2956     };
2957
2958     // private
2959     var handleEsc = function(d, k, e){
2960         if(opt && opt.closable !== false){
2961             dlg.hide();
2962         }
2963         if(e){
2964             e.stopEvent();
2965         }
2966     };
2967
2968     return {
2969         /**
2970          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2971          * @return {Roo.BasicDialog} The BasicDialog element
2972          */
2973         getDialog : function(){
2974            if(!dlg){
2975                 dlg = new Roo.bootstrap.Modal( {
2976                     //draggable: true,
2977                     //resizable:false,
2978                     //constraintoviewport:false,
2979                     //fixedcenter:true,
2980                     //collapsible : false,
2981                     //shim:true,
2982                     //modal: true,
2983                   //  width:400,
2984                   //  height:100,
2985                     //buttonAlign:"center",
2986                     closeClick : function(){
2987                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2988                             handleButton("no");
2989                         }else{
2990                             handleButton("cancel");
2991                         }
2992                     }
2993                 });
2994                 dlg.render();
2995                 dlg.on("hide", handleHide);
2996                 mask = dlg.mask;
2997                 //dlg.addKeyListener(27, handleEsc);
2998                 buttons = {};
2999                 this.buttons = buttons;
3000                 var bt = this.buttonText;
3001                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3002                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3003                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3004                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3005                 //Roo.log(buttons);
3006                 bodyEl = dlg.bodyEl.createChild({
3007
3008                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3009                         '<textarea class="roo-mb-textarea"></textarea>' +
3010                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3011                 });
3012                 msgEl = bodyEl.dom.firstChild;
3013                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3014                 textboxEl.enableDisplayMode();
3015                 textboxEl.addKeyListener([10,13], function(){
3016                     if(dlg.isVisible() && opt && opt.buttons){
3017                         if(opt.buttons.ok){
3018                             handleButton("ok");
3019                         }else if(opt.buttons.yes){
3020                             handleButton("yes");
3021                         }
3022                     }
3023                 });
3024                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3025                 textareaEl.enableDisplayMode();
3026                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3027                 progressEl.enableDisplayMode();
3028                 var pf = progressEl.dom.firstChild;
3029                 if (pf) {
3030                     pp = Roo.get(pf.firstChild);
3031                     pp.setHeight(pf.offsetHeight);
3032                 }
3033                 
3034             }
3035             return dlg;
3036         },
3037
3038         /**
3039          * Updates the message box body text
3040          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3041          * the XHTML-compliant non-breaking space character '&amp;#160;')
3042          * @return {Roo.MessageBox} This message box
3043          */
3044         updateText : function(text){
3045             if(!dlg.isVisible() && !opt.width){
3046                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3047             }
3048             msgEl.innerHTML = text || '&#160;';
3049       
3050             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3051             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3052             var w = Math.max(
3053                     Math.min(opt.width || cw , this.maxWidth), 
3054                     Math.max(opt.minWidth || this.minWidth, bwidth)
3055             );
3056             if(opt.prompt){
3057                 activeTextEl.setWidth(w);
3058             }
3059             if(dlg.isVisible()){
3060                 dlg.fixedcenter = false;
3061             }
3062             // to big, make it scroll. = But as usual stupid IE does not support
3063             // !important..
3064             
3065             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3066                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3067                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3068             } else {
3069                 bodyEl.dom.style.height = '';
3070                 bodyEl.dom.style.overflowY = '';
3071             }
3072             if (cw > w) {
3073                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3074             } else {
3075                 bodyEl.dom.style.overflowX = '';
3076             }
3077             
3078             dlg.setContentSize(w, bodyEl.getHeight());
3079             if(dlg.isVisible()){
3080                 dlg.fixedcenter = true;
3081             }
3082             return this;
3083         },
3084
3085         /**
3086          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3087          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3088          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3089          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3090          * @return {Roo.MessageBox} This message box
3091          */
3092         updateProgress : function(value, text){
3093             if(text){
3094                 this.updateText(text);
3095             }
3096             if (pp) { // weird bug on my firefox - for some reason this is not defined
3097                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3098             }
3099             return this;
3100         },        
3101
3102         /**
3103          * Returns true if the message box is currently displayed
3104          * @return {Boolean} True if the message box is visible, else false
3105          */
3106         isVisible : function(){
3107             return dlg && dlg.isVisible();  
3108         },
3109
3110         /**
3111          * Hides the message box if it is displayed
3112          */
3113         hide : function(){
3114             if(this.isVisible()){
3115                 dlg.hide();
3116             }  
3117         },
3118
3119         /**
3120          * Displays a new message box, or reinitializes an existing message box, based on the config options
3121          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3122          * The following config object properties are supported:
3123          * <pre>
3124 Property    Type             Description
3125 ----------  ---------------  ------------------------------------------------------------------------------------
3126 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3127                                    closes (defaults to undefined)
3128 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3129                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3130 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3131                                    progress and wait dialogs will ignore this property and always hide the
3132                                    close button as they can only be closed programmatically.
3133 cls               String           A custom CSS class to apply to the message box element
3134 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3135                                    displayed (defaults to 75)
3136 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3137                                    function will be btn (the name of the button that was clicked, if applicable,
3138                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3139                                    Progress and wait dialogs will ignore this option since they do not respond to
3140                                    user actions and can only be closed programmatically, so any required function
3141                                    should be called by the same code after it closes the dialog.
3142 icon              String           A CSS class that provides a background image to be used as an icon for
3143                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3144 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3145 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3146 modal             Boolean          False to allow user interaction with the page while the message box is
3147                                    displayed (defaults to true)
3148 msg               String           A string that will replace the existing message box body text (defaults
3149                                    to the XHTML-compliant non-breaking space character '&#160;')
3150 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3151 progress          Boolean          True to display a progress bar (defaults to false)
3152 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3153 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3154 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3155 title             String           The title text
3156 value             String           The string value to set into the active textbox element if displayed
3157 wait              Boolean          True to display a progress bar (defaults to false)
3158 width             Number           The width of the dialog in pixels
3159 </pre>
3160          *
3161          * Example usage:
3162          * <pre><code>
3163 Roo.Msg.show({
3164    title: 'Address',
3165    msg: 'Please enter your address:',
3166    width: 300,
3167    buttons: Roo.MessageBox.OKCANCEL,
3168    multiline: true,
3169    fn: saveAddress,
3170    animEl: 'addAddressBtn'
3171 });
3172 </code></pre>
3173          * @param {Object} config Configuration options
3174          * @return {Roo.MessageBox} This message box
3175          */
3176         show : function(options)
3177         {
3178             
3179             // this causes nightmares if you show one dialog after another
3180             // especially on callbacks..
3181              
3182             if(this.isVisible()){
3183                 
3184                 this.hide();
3185                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3186                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3187                 Roo.log("New Dialog Message:" +  options.msg )
3188                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3189                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3190                 
3191             }
3192             var d = this.getDialog();
3193             opt = options;
3194             d.setTitle(opt.title || "&#160;");
3195             d.closeEl.setDisplayed(opt.closable !== false);
3196             activeTextEl = textboxEl;
3197             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3198             if(opt.prompt){
3199                 if(opt.multiline){
3200                     textboxEl.hide();
3201                     textareaEl.show();
3202                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3203                         opt.multiline : this.defaultTextHeight);
3204                     activeTextEl = textareaEl;
3205                 }else{
3206                     textboxEl.show();
3207                     textareaEl.hide();
3208                 }
3209             }else{
3210                 textboxEl.hide();
3211                 textareaEl.hide();
3212             }
3213             progressEl.setDisplayed(opt.progress === true);
3214             this.updateProgress(0);
3215             activeTextEl.dom.value = opt.value || "";
3216             if(opt.prompt){
3217                 dlg.setDefaultButton(activeTextEl);
3218             }else{
3219                 var bs = opt.buttons;
3220                 var db = null;
3221                 if(bs && bs.ok){
3222                     db = buttons["ok"];
3223                 }else if(bs && bs.yes){
3224                     db = buttons["yes"];
3225                 }
3226                 dlg.setDefaultButton(db);
3227             }
3228             bwidth = updateButtons(opt.buttons);
3229             this.updateText(opt.msg);
3230             if(opt.cls){
3231                 d.el.addClass(opt.cls);
3232             }
3233             d.proxyDrag = opt.proxyDrag === true;
3234             d.modal = opt.modal !== false;
3235             d.mask = opt.modal !== false ? mask : false;
3236             if(!d.isVisible()){
3237                 // force it to the end of the z-index stack so it gets a cursor in FF
3238                 document.body.appendChild(dlg.el.dom);
3239                 d.animateTarget = null;
3240                 d.show(options.animEl);
3241             }
3242             return this;
3243         },
3244
3245         /**
3246          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3247          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3248          * and closing the message box when the process is complete.
3249          * @param {String} title The title bar text
3250          * @param {String} msg The message box body text
3251          * @return {Roo.MessageBox} This message box
3252          */
3253         progress : function(title, msg){
3254             this.show({
3255                 title : title,
3256                 msg : msg,
3257                 buttons: false,
3258                 progress:true,
3259                 closable:false,
3260                 minWidth: this.minProgressWidth,
3261                 modal : true
3262             });
3263             return this;
3264         },
3265
3266         /**
3267          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3268          * If a callback function is passed it will be called after the user clicks the button, and the
3269          * id of the button that was clicked will be passed as the only parameter to the callback
3270          * (could also be the top-right close button).
3271          * @param {String} title The title bar text
3272          * @param {String} msg The message box body text
3273          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3274          * @param {Object} scope (optional) The scope of the callback function
3275          * @return {Roo.MessageBox} This message box
3276          */
3277         alert : function(title, msg, fn, scope){
3278             this.show({
3279                 title : title,
3280                 msg : msg,
3281                 buttons: this.OK,
3282                 fn: fn,
3283                 scope : scope,
3284                 modal : true
3285             });
3286             return this;
3287         },
3288
3289         /**
3290          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3291          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3292          * You are responsible for closing the message box when the process is complete.
3293          * @param {String} msg The message box body text
3294          * @param {String} title (optional) The title bar text
3295          * @return {Roo.MessageBox} This message box
3296          */
3297         wait : function(msg, title){
3298             this.show({
3299                 title : title,
3300                 msg : msg,
3301                 buttons: false,
3302                 closable:false,
3303                 progress:true,
3304                 modal:true,
3305                 width:300,
3306                 wait:true
3307             });
3308             waitTimer = Roo.TaskMgr.start({
3309                 run: function(i){
3310                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3311                 },
3312                 interval: 1000
3313             });
3314             return this;
3315         },
3316
3317         /**
3318          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3319          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3320          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3321          * @param {String} title The title bar text
3322          * @param {String} msg The message box body text
3323          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3324          * @param {Object} scope (optional) The scope of the callback function
3325          * @return {Roo.MessageBox} This message box
3326          */
3327         confirm : function(title, msg, fn, scope){
3328             this.show({
3329                 title : title,
3330                 msg : msg,
3331                 buttons: this.YESNO,
3332                 fn: fn,
3333                 scope : scope,
3334                 modal : true
3335             });
3336             return this;
3337         },
3338
3339         /**
3340          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3341          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3342          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3343          * (could also be the top-right close button) and the text that was entered will be passed as the two
3344          * parameters to the callback.
3345          * @param {String} title The title bar text
3346          * @param {String} msg The message box body text
3347          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3348          * @param {Object} scope (optional) The scope of the callback function
3349          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3350          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3351          * @return {Roo.MessageBox} This message box
3352          */
3353         prompt : function(title, msg, fn, scope, multiline){
3354             this.show({
3355                 title : title,
3356                 msg : msg,
3357                 buttons: this.OKCANCEL,
3358                 fn: fn,
3359                 minWidth:250,
3360                 scope : scope,
3361                 prompt:true,
3362                 multiline: multiline,
3363                 modal : true
3364             });
3365             return this;
3366         },
3367
3368         /**
3369          * Button config that displays a single OK button
3370          * @type Object
3371          */
3372         OK : {ok:true},
3373         /**
3374          * Button config that displays Yes and No buttons
3375          * @type Object
3376          */
3377         YESNO : {yes:true, no:true},
3378         /**
3379          * Button config that displays OK and Cancel buttons
3380          * @type Object
3381          */
3382         OKCANCEL : {ok:true, cancel:true},
3383         /**
3384          * Button config that displays Yes, No and Cancel buttons
3385          * @type Object
3386          */
3387         YESNOCANCEL : {yes:true, no:true, cancel:true},
3388
3389         /**
3390          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3391          * @type Number
3392          */
3393         defaultTextHeight : 75,
3394         /**
3395          * The maximum width in pixels of the message box (defaults to 600)
3396          * @type Number
3397          */
3398         maxWidth : 600,
3399         /**
3400          * The minimum width in pixels of the message box (defaults to 100)
3401          * @type Number
3402          */
3403         minWidth : 100,
3404         /**
3405          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3406          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3407          * @type Number
3408          */
3409         minProgressWidth : 250,
3410         /**
3411          * An object containing the default button text strings that can be overriden for localized language support.
3412          * Supported properties are: ok, cancel, yes and no.
3413          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3414          * @type Object
3415          */
3416         buttonText : {
3417             ok : "OK",
3418             cancel : "Cancel",
3419             yes : "Yes",
3420             no : "No"
3421         }
3422     };
3423 }();
3424
3425 /**
3426  * Shorthand for {@link Roo.MessageBox}
3427  */
3428 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3429 Roo.Msg = Roo.Msg || Roo.MessageBox;
3430 /*
3431  * - LGPL
3432  *
3433  * navbar
3434  * 
3435  */
3436
3437 /**
3438  * @class Roo.bootstrap.Navbar
3439  * @extends Roo.bootstrap.Component
3440  * Bootstrap Navbar class
3441
3442  * @constructor
3443  * Create a new Navbar
3444  * @param {Object} config The config object
3445  */
3446
3447
3448 Roo.bootstrap.Navbar = function(config){
3449     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3450     this.addEvents({
3451         // raw events
3452         /**
3453          * @event beforetoggle
3454          * Fire before toggle the menu
3455          * @param {Roo.EventObject} e
3456          */
3457         "beforetoggle" : true
3458     });
3459 };
3460
3461 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3462     
3463     
3464    
3465     // private
3466     navItems : false,
3467     loadMask : false,
3468     
3469     
3470     getAutoCreate : function(){
3471         
3472         
3473         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3474         
3475     },
3476     
3477     initEvents :function ()
3478     {
3479         //Roo.log(this.el.select('.navbar-toggle',true));
3480         this.el.select('.navbar-toggle',true).on('click', function() {
3481             if(this.fireEvent('beforetoggle', this) !== false){
3482                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3483             }
3484             
3485         }, this);
3486         
3487         var mark = {
3488             tag: "div",
3489             cls:"x-dlg-mask"
3490         };
3491         
3492         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3493         
3494         var size = this.el.getSize();
3495         this.maskEl.setSize(size.width, size.height);
3496         this.maskEl.enableDisplayMode("block");
3497         this.maskEl.hide();
3498         
3499         if(this.loadMask){
3500             this.maskEl.show();
3501         }
3502     },
3503     
3504     
3505     getChildContainer : function()
3506     {
3507         if (this.el.select('.collapse').getCount()) {
3508             return this.el.select('.collapse',true).first();
3509         }
3510         
3511         return this.el;
3512     },
3513     
3514     mask : function()
3515     {
3516         this.maskEl.show();
3517     },
3518     
3519     unmask : function()
3520     {
3521         this.maskEl.hide();
3522     } 
3523     
3524     
3525     
3526     
3527 });
3528
3529
3530
3531  
3532
3533  /*
3534  * - LGPL
3535  *
3536  * navbar
3537  * 
3538  */
3539
3540 /**
3541  * @class Roo.bootstrap.NavSimplebar
3542  * @extends Roo.bootstrap.Navbar
3543  * Bootstrap Sidebar class
3544  *
3545  * @cfg {Boolean} inverse is inverted color
3546  * 
3547  * @cfg {String} type (nav | pills | tabs)
3548  * @cfg {Boolean} arrangement stacked | justified
3549  * @cfg {String} align (left | right) alignment
3550  * 
3551  * @cfg {Boolean} main (true|false) main nav bar? default false
3552  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3553  * 
3554  * @cfg {String} tag (header|footer|nav|div) default is nav 
3555
3556  * 
3557  * 
3558  * 
3559  * @constructor
3560  * Create a new Sidebar
3561  * @param {Object} config The config object
3562  */
3563
3564
3565 Roo.bootstrap.NavSimplebar = function(config){
3566     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3567 };
3568
3569 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3570     
3571     inverse: false,
3572     
3573     type: false,
3574     arrangement: '',
3575     align : false,
3576     
3577     
3578     
3579     main : false,
3580     
3581     
3582     tag : false,
3583     
3584     
3585     getAutoCreate : function(){
3586         
3587         
3588         var cfg = {
3589             tag : this.tag || 'div',
3590             cls : 'navbar'
3591         };
3592           
3593         
3594         cfg.cn = [
3595             {
3596                 cls: 'nav',
3597                 tag : 'ul'
3598             }
3599         ];
3600         
3601          
3602         this.type = this.type || 'nav';
3603         if (['tabs','pills'].indexOf(this.type)!==-1) {
3604             cfg.cn[0].cls += ' nav-' + this.type
3605         
3606         
3607         } else {
3608             if (this.type!=='nav') {
3609                 Roo.log('nav type must be nav/tabs/pills')
3610             }
3611             cfg.cn[0].cls += ' navbar-nav'
3612         }
3613         
3614         
3615         
3616         
3617         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3618             cfg.cn[0].cls += ' nav-' + this.arrangement;
3619         }
3620         
3621         
3622         if (this.align === 'right') {
3623             cfg.cn[0].cls += ' navbar-right';
3624         }
3625         
3626         if (this.inverse) {
3627             cfg.cls += ' navbar-inverse';
3628             
3629         }
3630         
3631         
3632         return cfg;
3633     
3634         
3635     }
3636     
3637     
3638     
3639 });
3640
3641
3642
3643  
3644
3645  
3646        /*
3647  * - LGPL
3648  *
3649  * navbar
3650  * 
3651  */
3652
3653 /**
3654  * @class Roo.bootstrap.NavHeaderbar
3655  * @extends Roo.bootstrap.NavSimplebar
3656  * Bootstrap Sidebar class
3657  *
3658  * @cfg {String} brand what is brand
3659  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3660  * @cfg {String} brand_href href of the brand
3661  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3662  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3663  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3664  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3665  * 
3666  * @constructor
3667  * Create a new Sidebar
3668  * @param {Object} config The config object
3669  */
3670
3671
3672 Roo.bootstrap.NavHeaderbar = function(config){
3673     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3674       
3675 };
3676
3677 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3678     
3679     position: '',
3680     brand: '',
3681     brand_href: false,
3682     srButton : true,
3683     autohide : false,
3684     desktopCenter : false,
3685    
3686     
3687     getAutoCreate : function(){
3688         
3689         var   cfg = {
3690             tag: this.nav || 'nav',
3691             cls: 'navbar',
3692             role: 'navigation',
3693             cn: []
3694         };
3695         
3696         var cn = cfg.cn;
3697         if (this.desktopCenter) {
3698             cn.push({cls : 'container', cn : []});
3699             cn = cn[0].cn;
3700         }
3701         
3702         if(this.srButton){
3703             cn.push({
3704                 tag: 'div',
3705                 cls: 'navbar-header',
3706                 cn: [
3707                     {
3708                         tag: 'button',
3709                         type: 'button',
3710                         cls: 'navbar-toggle',
3711                         'data-toggle': 'collapse',
3712                         cn: [
3713                             {
3714                                 tag: 'span',
3715                                 cls: 'sr-only',
3716                                 html: 'Toggle navigation'
3717                             },
3718                             {
3719                                 tag: 'span',
3720                                 cls: 'icon-bar'
3721                             },
3722                             {
3723                                 tag: 'span',
3724                                 cls: 'icon-bar'
3725                             },
3726                             {
3727                                 tag: 'span',
3728                                 cls: 'icon-bar'
3729                             }
3730                         ]
3731                     }
3732                 ]
3733             });
3734         }
3735         
3736         cn.push({
3737             tag: 'div',
3738             cls: 'collapse navbar-collapse',
3739             cn : []
3740         });
3741         
3742         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3743         
3744         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3745             cfg.cls += ' navbar-' + this.position;
3746             
3747             // tag can override this..
3748             
3749             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3750         }
3751         
3752         if (this.brand !== '') {
3753             cn[0].cn.push({
3754                 tag: 'a',
3755                 href: this.brand_href ? this.brand_href : '#',
3756                 cls: 'navbar-brand',
3757                 cn: [
3758                 this.brand
3759                 ]
3760             });
3761         }
3762         
3763         if(this.main){
3764             cfg.cls += ' main-nav';
3765         }
3766         
3767         
3768         return cfg;
3769
3770         
3771     },
3772     getHeaderChildContainer : function()
3773     {
3774         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3775             return this.el.select('.navbar-header',true).first();
3776         }
3777         
3778         return this.getChildContainer();
3779     },
3780     
3781     
3782     initEvents : function()
3783     {
3784         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3785         
3786         if (this.autohide) {
3787             
3788             var prevScroll = 0;
3789             var ft = this.el;
3790             
3791             Roo.get(document).on('scroll',function(e) {
3792                 var ns = Roo.get(document).getScroll().top;
3793                 var os = prevScroll;
3794                 prevScroll = ns;
3795                 
3796                 if(ns > os){
3797                     ft.removeClass('slideDown');
3798                     ft.addClass('slideUp');
3799                     return;
3800                 }
3801                 ft.removeClass('slideUp');
3802                 ft.addClass('slideDown');
3803                  
3804               
3805           },this);
3806         }
3807     }    
3808     
3809 });
3810
3811
3812
3813  
3814
3815  /*
3816  * - LGPL
3817  *
3818  * navbar
3819  * 
3820  */
3821
3822 /**
3823  * @class Roo.bootstrap.NavSidebar
3824  * @extends Roo.bootstrap.Navbar
3825  * Bootstrap Sidebar class
3826  * 
3827  * @constructor
3828  * Create a new Sidebar
3829  * @param {Object} config The config object
3830  */
3831
3832
3833 Roo.bootstrap.NavSidebar = function(config){
3834     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3835 };
3836
3837 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3838     
3839     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3840     
3841     getAutoCreate : function(){
3842         
3843         
3844         return  {
3845             tag: 'div',
3846             cls: 'sidebar sidebar-nav'
3847         };
3848     
3849         
3850     }
3851     
3852     
3853     
3854 });
3855
3856
3857
3858  
3859
3860  /*
3861  * - LGPL
3862  *
3863  * nav group
3864  * 
3865  */
3866
3867 /**
3868  * @class Roo.bootstrap.NavGroup
3869  * @extends Roo.bootstrap.Component
3870  * Bootstrap NavGroup class
3871  * @cfg {String} align (left|right)
3872  * @cfg {Boolean} inverse
3873  * @cfg {String} type (nav|pills|tab) default nav
3874  * @cfg {String} navId - reference Id for navbar.
3875
3876  * 
3877  * @constructor
3878  * Create a new nav group
3879  * @param {Object} config The config object
3880  */
3881
3882 Roo.bootstrap.NavGroup = function(config){
3883     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3884     this.navItems = [];
3885    
3886     Roo.bootstrap.NavGroup.register(this);
3887      this.addEvents({
3888         /**
3889              * @event changed
3890              * Fires when the active item changes
3891              * @param {Roo.bootstrap.NavGroup} this
3892              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3893              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3894          */
3895         'changed': true
3896      });
3897     
3898 };
3899
3900 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3901     
3902     align: '',
3903     inverse: false,
3904     form: false,
3905     type: 'nav',
3906     navId : '',
3907     // private
3908     
3909     navItems : false, 
3910     
3911     getAutoCreate : function()
3912     {
3913         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3914         
3915         cfg = {
3916             tag : 'ul',
3917             cls: 'nav' 
3918         };
3919         
3920         if (['tabs','pills'].indexOf(this.type)!==-1) {
3921             cfg.cls += ' nav-' + this.type
3922         } else {
3923             if (this.type!=='nav') {
3924                 Roo.log('nav type must be nav/tabs/pills')
3925             }
3926             cfg.cls += ' navbar-nav'
3927         }
3928         
3929         if (this.parent().sidebar) {
3930             cfg = {
3931                 tag: 'ul',
3932                 cls: 'dashboard-menu sidebar-menu'
3933             };
3934             
3935             return cfg;
3936         }
3937         
3938         if (this.form === true) {
3939             cfg = {
3940                 tag: 'form',
3941                 cls: 'navbar-form'
3942             };
3943             
3944             if (this.align === 'right') {
3945                 cfg.cls += ' navbar-right';
3946             } else {
3947                 cfg.cls += ' navbar-left';
3948             }
3949         }
3950         
3951         if (this.align === 'right') {
3952             cfg.cls += ' navbar-right';
3953         }
3954         
3955         if (this.inverse) {
3956             cfg.cls += ' navbar-inverse';
3957             
3958         }
3959         
3960         
3961         return cfg;
3962     },
3963     /**
3964     * sets the active Navigation item
3965     * @param {Roo.bootstrap.NavItem} the new current navitem
3966     */
3967     setActiveItem : function(item)
3968     {
3969         var prev = false;
3970         Roo.each(this.navItems, function(v){
3971             if (v == item) {
3972                 return ;
3973             }
3974             if (v.isActive()) {
3975                 v.setActive(false, true);
3976                 prev = v;
3977                 
3978             }
3979             
3980         });
3981
3982         item.setActive(true, true);
3983         this.fireEvent('changed', this, item, prev);
3984         
3985         
3986     },
3987     /**
3988     * gets the active Navigation item
3989     * @return {Roo.bootstrap.NavItem} the current navitem
3990     */
3991     getActive : function()
3992     {
3993         
3994         var prev = false;
3995         Roo.each(this.navItems, function(v){
3996             
3997             if (v.isActive()) {
3998                 prev = v;
3999                 
4000             }
4001             
4002         });
4003         return prev;
4004     },
4005     
4006     indexOfNav : function()
4007     {
4008         
4009         var prev = false;
4010         Roo.each(this.navItems, function(v,i){
4011             
4012             if (v.isActive()) {
4013                 prev = i;
4014                 
4015             }
4016             
4017         });
4018         return prev;
4019     },
4020     /**
4021     * adds a Navigation item
4022     * @param {Roo.bootstrap.NavItem} the navitem to add
4023     */
4024     addItem : function(cfg)
4025     {
4026         var cn = new Roo.bootstrap.NavItem(cfg);
4027         this.register(cn);
4028         cn.parentId = this.id;
4029         cn.onRender(this.el, null);
4030         return cn;
4031     },
4032     /**
4033     * register a Navigation item
4034     * @param {Roo.bootstrap.NavItem} the navitem to add
4035     */
4036     register : function(item)
4037     {
4038         this.navItems.push( item);
4039         item.navId = this.navId;
4040     
4041     },
4042     
4043     /**
4044     * clear all the Navigation item
4045     */
4046    
4047     clearAll : function()
4048     {
4049         this.navItems = [];
4050         this.el.dom.innerHTML = '';
4051     },
4052     
4053     getNavItem: function(tabId)
4054     {
4055         var ret = false;
4056         Roo.each(this.navItems, function(e) {
4057             if (e.tabId == tabId) {
4058                ret =  e;
4059                return false;
4060             }
4061             return true;
4062             
4063         });
4064         return ret;
4065     },
4066     
4067     setActiveNext : function()
4068     {
4069         var i = this.indexOfNav(this.getActive());
4070         if (i > this.navItems.length) {
4071             return;
4072         }
4073         this.setActiveItem(this.navItems[i+1]);
4074     },
4075     setActivePrev : function()
4076     {
4077         var i = this.indexOfNav(this.getActive());
4078         if (i  < 1) {
4079             return;
4080         }
4081         this.setActiveItem(this.navItems[i-1]);
4082     },
4083     clearWasActive : function(except) {
4084         Roo.each(this.navItems, function(e) {
4085             if (e.tabId != except.tabId && e.was_active) {
4086                e.was_active = false;
4087                return false;
4088             }
4089             return true;
4090             
4091         });
4092     },
4093     getWasActive : function ()
4094     {
4095         var r = false;
4096         Roo.each(this.navItems, function(e) {
4097             if (e.was_active) {
4098                r = e;
4099                return false;
4100             }
4101             return true;
4102             
4103         });
4104         return r;
4105     }
4106     
4107     
4108 });
4109
4110  
4111 Roo.apply(Roo.bootstrap.NavGroup, {
4112     
4113     groups: {},
4114      /**
4115     * register a Navigation Group
4116     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4117     */
4118     register : function(navgrp)
4119     {
4120         this.groups[navgrp.navId] = navgrp;
4121         
4122     },
4123     /**
4124     * fetch a Navigation Group based on the navigation ID
4125     * @param {string} the navgroup to add
4126     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4127     */
4128     get: function(navId) {
4129         if (typeof(this.groups[navId]) == 'undefined') {
4130             return false;
4131             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4132         }
4133         return this.groups[navId] ;
4134     }
4135     
4136     
4137     
4138 });
4139
4140  /*
4141  * - LGPL
4142  *
4143  * row
4144  * 
4145  */
4146
4147 /**
4148  * @class Roo.bootstrap.NavItem
4149  * @extends Roo.bootstrap.Component
4150  * Bootstrap Navbar.NavItem class
4151  * @cfg {String} href  link to
4152  * @cfg {String} html content of button
4153  * @cfg {String} badge text inside badge
4154  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4155  * @cfg {String} glyphicon name of glyphicon
4156  * @cfg {String} icon name of font awesome icon
4157  * @cfg {Boolean} active Is item active
4158  * @cfg {Boolean} disabled Is item disabled
4159  
4160  * @cfg {Boolean} preventDefault (true | false) default false
4161  * @cfg {String} tabId the tab that this item activates.
4162  * @cfg {String} tagtype (a|span) render as a href or span?
4163  * @cfg {Boolean} animateRef (true|false) link to element default false  
4164   
4165  * @constructor
4166  * Create a new Navbar Item
4167  * @param {Object} config The config object
4168  */
4169 Roo.bootstrap.NavItem = function(config){
4170     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4171     this.addEvents({
4172         // raw events
4173         /**
4174          * @event click
4175          * The raw click event for the entire grid.
4176          * @param {Roo.EventObject} e
4177          */
4178         "click" : true,
4179          /**
4180             * @event changed
4181             * Fires when the active item active state changes
4182             * @param {Roo.bootstrap.NavItem} this
4183             * @param {boolean} state the new state
4184              
4185          */
4186         'changed': true,
4187         /**
4188             * @event scrollto
4189             * Fires when scroll to element
4190             * @param {Roo.bootstrap.NavItem} this
4191             * @param {Object} options
4192             * @param {Roo.EventObject} e
4193              
4194          */
4195         'scrollto': true
4196     });
4197    
4198 };
4199
4200 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4201     
4202     href: false,
4203     html: '',
4204     badge: '',
4205     icon: false,
4206     glyphicon: false,
4207     active: false,
4208     preventDefault : false,
4209     tabId : false,
4210     tagtype : 'a',
4211     disabled : false,
4212     animateRef : false,
4213     was_active : false,
4214     
4215     getAutoCreate : function(){
4216          
4217         var cfg = {
4218             tag: 'li',
4219             cls: 'nav-item'
4220             
4221         };
4222         
4223         if (this.active) {
4224             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4225         }
4226         if (this.disabled) {
4227             cfg.cls += ' disabled';
4228         }
4229         
4230         if (this.href || this.html || this.glyphicon || this.icon) {
4231             cfg.cn = [
4232                 {
4233                     tag: this.tagtype,
4234                     href : this.href || "#",
4235                     html: this.html || ''
4236                 }
4237             ];
4238             
4239             if (this.icon) {
4240                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4241             }
4242
4243             if(this.glyphicon) {
4244                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4245             }
4246             
4247             if (this.menu) {
4248                 
4249                 cfg.cn[0].html += " <span class='caret'></span>";
4250              
4251             }
4252             
4253             if (this.badge !== '') {
4254                  
4255                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4256             }
4257         }
4258         
4259         
4260         
4261         return cfg;
4262     },
4263     initEvents: function() 
4264     {
4265         if (typeof (this.menu) != 'undefined') {
4266             this.menu.parentType = this.xtype;
4267             this.menu.triggerEl = this.el;
4268             this.menu = this.addxtype(Roo.apply({}, this.menu));
4269         }
4270         
4271         this.el.select('a',true).on('click', this.onClick, this);
4272         
4273         if(this.tagtype == 'span'){
4274             this.el.select('span',true).on('click', this.onClick, this);
4275         }
4276        
4277         // at this point parent should be available..
4278         this.parent().register(this);
4279     },
4280     
4281     onClick : function(e)
4282     {
4283         if (e.getTarget('.dropdown-menu-item')) {
4284             // did you click on a menu itemm.... - then don't trigger onclick..
4285             return;
4286         }
4287         
4288         if(
4289                 this.preventDefault || 
4290                 this.href == '#' 
4291         ){
4292             Roo.log("NavItem - prevent Default?");
4293             e.preventDefault();
4294         }
4295         
4296         if (this.disabled) {
4297             return;
4298         }
4299         
4300         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4301         if (tg && tg.transition) {
4302             Roo.log("waiting for the transitionend");
4303             return;
4304         }
4305         
4306         
4307         
4308         //Roo.log("fire event clicked");
4309         if(this.fireEvent('click', this, e) === false){
4310             return;
4311         };
4312         
4313         if(this.tagtype == 'span'){
4314             return;
4315         }
4316         
4317         //Roo.log(this.href);
4318         var ael = this.el.select('a',true).first();
4319         //Roo.log(ael);
4320         
4321         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4322             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4323             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4324                 return; // ignore... - it's a 'hash' to another page.
4325             }
4326             Roo.log("NavItem - prevent Default?");
4327             e.preventDefault();
4328             this.scrollToElement(e);
4329         }
4330         
4331         
4332         var p =  this.parent();
4333    
4334         if (['tabs','pills'].indexOf(p.type)!==-1) {
4335             if (typeof(p.setActiveItem) !== 'undefined') {
4336                 p.setActiveItem(this);
4337             }
4338         }
4339         
4340         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4341         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4342             // remove the collapsed menu expand...
4343             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4344         }
4345     },
4346     
4347     isActive: function () {
4348         return this.active
4349     },
4350     setActive : function(state, fire, is_was_active)
4351     {
4352         if (this.active && !state && this.navId) {
4353             this.was_active = true;
4354             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4355             if (nv) {
4356                 nv.clearWasActive(this);
4357             }
4358             
4359         }
4360         this.active = state;
4361         
4362         if (!state ) {
4363             this.el.removeClass('active');
4364         } else if (!this.el.hasClass('active')) {
4365             this.el.addClass('active');
4366         }
4367         if (fire) {
4368             this.fireEvent('changed', this, state);
4369         }
4370         
4371         // show a panel if it's registered and related..
4372         
4373         if (!this.navId || !this.tabId || !state || is_was_active) {
4374             return;
4375         }
4376         
4377         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4378         if (!tg) {
4379             return;
4380         }
4381         var pan = tg.getPanelByName(this.tabId);
4382         if (!pan) {
4383             return;
4384         }
4385         // if we can not flip to new panel - go back to old nav highlight..
4386         if (false == tg.showPanel(pan)) {
4387             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4388             if (nv) {
4389                 var onav = nv.getWasActive();
4390                 if (onav) {
4391                     onav.setActive(true, false, true);
4392                 }
4393             }
4394             
4395         }
4396         
4397         
4398         
4399     },
4400      // this should not be here...
4401     setDisabled : function(state)
4402     {
4403         this.disabled = state;
4404         if (!state ) {
4405             this.el.removeClass('disabled');
4406         } else if (!this.el.hasClass('disabled')) {
4407             this.el.addClass('disabled');
4408         }
4409         
4410     },
4411     
4412     /**
4413      * Fetch the element to display the tooltip on.
4414      * @return {Roo.Element} defaults to this.el
4415      */
4416     tooltipEl : function()
4417     {
4418         return this.el.select('' + this.tagtype + '', true).first();
4419     },
4420     
4421     scrollToElement : function(e)
4422     {
4423         var c = document.body;
4424         
4425         /*
4426          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4427          */
4428         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4429             c = document.documentElement;
4430         }
4431         
4432         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4433         
4434         if(!target){
4435             return;
4436         }
4437
4438         var o = target.calcOffsetsTo(c);
4439         
4440         var options = {
4441             target : target,
4442             value : o[1]
4443         };
4444         
4445         this.fireEvent('scrollto', this, options, e);
4446         
4447         Roo.get(c).scrollTo('top', options.value, true);
4448         
4449         return;
4450     }
4451 });
4452  
4453
4454  /*
4455  * - LGPL
4456  *
4457  * sidebar item
4458  *
4459  *  li
4460  *    <span> icon </span>
4461  *    <span> text </span>
4462  *    <span>badge </span>
4463  */
4464
4465 /**
4466  * @class Roo.bootstrap.NavSidebarItem
4467  * @extends Roo.bootstrap.NavItem
4468  * Bootstrap Navbar.NavSidebarItem class
4469  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4470  * {bool} open is the menu open
4471  * @constructor
4472  * Create a new Navbar Button
4473  * @param {Object} config The config object
4474  */
4475 Roo.bootstrap.NavSidebarItem = function(config){
4476     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4477     this.addEvents({
4478         // raw events
4479         /**
4480          * @event click
4481          * The raw click event for the entire grid.
4482          * @param {Roo.EventObject} e
4483          */
4484         "click" : true,
4485          /**
4486             * @event changed
4487             * Fires when the active item active state changes
4488             * @param {Roo.bootstrap.NavSidebarItem} this
4489             * @param {boolean} state the new state
4490              
4491          */
4492         'changed': true
4493     });
4494    
4495 };
4496
4497 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4498     
4499     badgeWeight : 'default',
4500     
4501     open: false,
4502     
4503     getAutoCreate : function(){
4504         
4505         
4506         var a = {
4507                 tag: 'a',
4508                 href : this.href || '#',
4509                 cls: '',
4510                 html : '',
4511                 cn : []
4512         };
4513         var cfg = {
4514             tag: 'li',
4515             cls: '',
4516             cn: [ a ]
4517         };
4518         var span = {
4519             tag: 'span',
4520             html : this.html || ''
4521         };
4522         
4523         
4524         if (this.active) {
4525             cfg.cls += ' active';
4526         }
4527         
4528         if (this.disabled) {
4529             cfg.cls += ' disabled';
4530         }
4531         if (this.open) {
4532             cfg.cls += ' open x-open';
4533         }
4534         // left icon..
4535         if (this.glyphicon || this.icon) {
4536             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4537             a.cn.push({ tag : 'i', cls : c }) ;
4538         }
4539         // html..
4540         a.cn.push(span);
4541         // then badge..
4542         if (this.badge !== '') {
4543             
4544             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4545         }
4546         // fi
4547         if (this.menu) {
4548             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4549             a.cls += 'dropdown-toggle treeview' ;
4550         }
4551         
4552         return cfg;
4553          
4554            
4555     },
4556     
4557     initEvents : function()
4558     { 
4559         if (typeof (this.menu) != 'undefined') {
4560             this.menu.parentType = this.xtype;
4561             this.menu.triggerEl = this.el;
4562             this.menu = this.addxtype(Roo.apply({}, this.menu));
4563         }
4564         
4565         this.el.on('click', this.onClick, this);
4566        
4567     
4568         if(this.badge !== ''){
4569  
4570             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4571         }
4572         
4573     },
4574     
4575     onClick : function(e)
4576     {
4577         if(this.disabled){
4578             e.preventDefault();
4579             return;
4580         }
4581         
4582         if(this.preventDefault){
4583             e.preventDefault();
4584         }
4585         
4586         this.fireEvent('click', this);
4587     },
4588     
4589     disable : function()
4590     {
4591         this.setDisabled(true);
4592     },
4593     
4594     enable : function()
4595     {
4596         this.setDisabled(false);
4597     },
4598     
4599     setDisabled : function(state)
4600     {
4601         if(this.disabled == state){
4602             return;
4603         }
4604         
4605         this.disabled = state;
4606         
4607         if (state) {
4608             this.el.addClass('disabled');
4609             return;
4610         }
4611         
4612         this.el.removeClass('disabled');
4613         
4614         return;
4615     },
4616     
4617     setActive : function(state)
4618     {
4619         if(this.active == state){
4620             return;
4621         }
4622         
4623         this.active = state;
4624         
4625         if (state) {
4626             this.el.addClass('active');
4627             return;
4628         }
4629         
4630         this.el.removeClass('active');
4631         
4632         return;
4633     },
4634     
4635     isActive: function () 
4636     {
4637         return this.active;
4638     },
4639     
4640     setBadge : function(str)
4641     {
4642         if(!this.badgeEl){
4643             return;
4644         }
4645         
4646         this.badgeEl.dom.innerHTML = str;
4647     }
4648     
4649    
4650      
4651  
4652 });
4653  
4654
4655  /*
4656  * - LGPL
4657  *
4658  * row
4659  * 
4660  */
4661
4662 /**
4663  * @class Roo.bootstrap.Row
4664  * @extends Roo.bootstrap.Component
4665  * Bootstrap Row class (contains columns...)
4666  * 
4667  * @constructor
4668  * Create a new Row
4669  * @param {Object} config The config object
4670  */
4671
4672 Roo.bootstrap.Row = function(config){
4673     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4674 };
4675
4676 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4677     
4678     getAutoCreate : function(){
4679        return {
4680             cls: 'row clearfix'
4681        };
4682     }
4683     
4684     
4685 });
4686
4687  
4688
4689  /*
4690  * - LGPL
4691  *
4692  * element
4693  * 
4694  */
4695
4696 /**
4697  * @class Roo.bootstrap.Element
4698  * @extends Roo.bootstrap.Component
4699  * Bootstrap Element class
4700  * @cfg {String} html contents of the element
4701  * @cfg {String} tag tag of the element
4702  * @cfg {String} cls class of the element
4703  * @cfg {Boolean} preventDefault (true|false) default false
4704  * @cfg {Boolean} clickable (true|false) default false
4705  * 
4706  * @constructor
4707  * Create a new Element
4708  * @param {Object} config The config object
4709  */
4710
4711 Roo.bootstrap.Element = function(config){
4712     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4713     
4714     this.addEvents({
4715         // raw events
4716         /**
4717          * @event click
4718          * When a element is chick
4719          * @param {Roo.bootstrap.Element} this
4720          * @param {Roo.EventObject} e
4721          */
4722         "click" : true
4723     });
4724 };
4725
4726 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4727     
4728     tag: 'div',
4729     cls: '',
4730     html: '',
4731     preventDefault: false, 
4732     clickable: false,
4733     
4734     getAutoCreate : function(){
4735         
4736         var cfg = {
4737             tag: this.tag,
4738             cls: this.cls,
4739             html: this.html
4740         };
4741         
4742         return cfg;
4743     },
4744     
4745     initEvents: function() 
4746     {
4747         Roo.bootstrap.Element.superclass.initEvents.call(this);
4748         
4749         if(this.clickable){
4750             this.el.on('click', this.onClick, this);
4751         }
4752         
4753     },
4754     
4755     onClick : function(e)
4756     {
4757         if(this.preventDefault){
4758             e.preventDefault();
4759         }
4760         
4761         this.fireEvent('click', this, e);
4762     },
4763     
4764     getValue : function()
4765     {
4766         return this.el.dom.innerHTML;
4767     },
4768     
4769     setValue : function(value)
4770     {
4771         this.el.dom.innerHTML = value;
4772     }
4773    
4774 });
4775
4776  
4777
4778  /*
4779  * - LGPL
4780  *
4781  * pagination
4782  * 
4783  */
4784
4785 /**
4786  * @class Roo.bootstrap.Pagination
4787  * @extends Roo.bootstrap.Component
4788  * Bootstrap Pagination class
4789  * @cfg {String} size xs | sm | md | lg
4790  * @cfg {Boolean} inverse false | true
4791  * 
4792  * @constructor
4793  * Create a new Pagination
4794  * @param {Object} config The config object
4795  */
4796
4797 Roo.bootstrap.Pagination = function(config){
4798     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4799 };
4800
4801 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4802     
4803     cls: false,
4804     size: false,
4805     inverse: false,
4806     
4807     getAutoCreate : function(){
4808         var cfg = {
4809             tag: 'ul',
4810                 cls: 'pagination'
4811         };
4812         if (this.inverse) {
4813             cfg.cls += ' inverse';
4814         }
4815         if (this.html) {
4816             cfg.html=this.html;
4817         }
4818         if (this.cls) {
4819             cfg.cls += " " + this.cls;
4820         }
4821         return cfg;
4822     }
4823    
4824 });
4825
4826  
4827
4828  /*
4829  * - LGPL
4830  *
4831  * Pagination item
4832  * 
4833  */
4834
4835
4836 /**
4837  * @class Roo.bootstrap.PaginationItem
4838  * @extends Roo.bootstrap.Component
4839  * Bootstrap PaginationItem class
4840  * @cfg {String} html text
4841  * @cfg {String} href the link
4842  * @cfg {Boolean} preventDefault (true | false) default true
4843  * @cfg {Boolean} active (true | false) default false
4844  * @cfg {Boolean} disabled default false
4845  * 
4846  * 
4847  * @constructor
4848  * Create a new PaginationItem
4849  * @param {Object} config The config object
4850  */
4851
4852
4853 Roo.bootstrap.PaginationItem = function(config){
4854     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4855     this.addEvents({
4856         // raw events
4857         /**
4858          * @event click
4859          * The raw click event for the entire grid.
4860          * @param {Roo.EventObject} e
4861          */
4862         "click" : true
4863     });
4864 };
4865
4866 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4867     
4868     href : false,
4869     html : false,
4870     preventDefault: true,
4871     active : false,
4872     cls : false,
4873     disabled: false,
4874     
4875     getAutoCreate : function(){
4876         var cfg= {
4877             tag: 'li',
4878             cn: [
4879                 {
4880                     tag : 'a',
4881                     href : this.href ? this.href : '#',
4882                     html : this.html ? this.html : ''
4883                 }
4884             ]
4885         };
4886         
4887         if(this.cls){
4888             cfg.cls = this.cls;
4889         }
4890         
4891         if(this.disabled){
4892             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4893         }
4894         
4895         if(this.active){
4896             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4897         }
4898         
4899         return cfg;
4900     },
4901     
4902     initEvents: function() {
4903         
4904         this.el.on('click', this.onClick, this);
4905         
4906     },
4907     onClick : function(e)
4908     {
4909         Roo.log('PaginationItem on click ');
4910         if(this.preventDefault){
4911             e.preventDefault();
4912         }
4913         
4914         if(this.disabled){
4915             return;
4916         }
4917         
4918         this.fireEvent('click', this, e);
4919     }
4920    
4921 });
4922
4923  
4924
4925  /*
4926  * - LGPL
4927  *
4928  * slider
4929  * 
4930  */
4931
4932
4933 /**
4934  * @class Roo.bootstrap.Slider
4935  * @extends Roo.bootstrap.Component
4936  * Bootstrap Slider class
4937  *    
4938  * @constructor
4939  * Create a new Slider
4940  * @param {Object} config The config object
4941  */
4942
4943 Roo.bootstrap.Slider = function(config){
4944     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4945 };
4946
4947 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4948     
4949     getAutoCreate : function(){
4950         
4951         var cfg = {
4952             tag: 'div',
4953             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4954             cn: [
4955                 {
4956                     tag: 'a',
4957                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4958                 }
4959             ]
4960         };
4961         
4962         return cfg;
4963     }
4964    
4965 });
4966
4967  /*
4968  * Based on:
4969  * Ext JS Library 1.1.1
4970  * Copyright(c) 2006-2007, Ext JS, LLC.
4971  *
4972  * Originally Released Under LGPL - original licence link has changed is not relivant.
4973  *
4974  * Fork - LGPL
4975  * <script type="text/javascript">
4976  */
4977  
4978
4979 /**
4980  * @class Roo.grid.ColumnModel
4981  * @extends Roo.util.Observable
4982  * This is the default implementation of a ColumnModel used by the Grid. It defines
4983  * the columns in the grid.
4984  * <br>Usage:<br>
4985  <pre><code>
4986  var colModel = new Roo.grid.ColumnModel([
4987         {header: "Ticker", width: 60, sortable: true, locked: true},
4988         {header: "Company Name", width: 150, sortable: true},
4989         {header: "Market Cap.", width: 100, sortable: true},
4990         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4991         {header: "Employees", width: 100, sortable: true, resizable: false}
4992  ]);
4993  </code></pre>
4994  * <p>
4995  
4996  * The config options listed for this class are options which may appear in each
4997  * individual column definition.
4998  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4999  * @constructor
5000  * @param {Object} config An Array of column config objects. See this class's
5001  * config objects for details.
5002 */
5003 Roo.grid.ColumnModel = function(config){
5004         /**
5005      * The config passed into the constructor
5006      */
5007     this.config = config;
5008     this.lookup = {};
5009
5010     // if no id, create one
5011     // if the column does not have a dataIndex mapping,
5012     // map it to the order it is in the config
5013     for(var i = 0, len = config.length; i < len; i++){
5014         var c = config[i];
5015         if(typeof c.dataIndex == "undefined"){
5016             c.dataIndex = i;
5017         }
5018         if(typeof c.renderer == "string"){
5019             c.renderer = Roo.util.Format[c.renderer];
5020         }
5021         if(typeof c.id == "undefined"){
5022             c.id = Roo.id();
5023         }
5024         if(c.editor && c.editor.xtype){
5025             c.editor  = Roo.factory(c.editor, Roo.grid);
5026         }
5027         if(c.editor && c.editor.isFormField){
5028             c.editor = new Roo.grid.GridEditor(c.editor);
5029         }
5030         this.lookup[c.id] = c;
5031     }
5032
5033     /**
5034      * The width of columns which have no width specified (defaults to 100)
5035      * @type Number
5036      */
5037     this.defaultWidth = 100;
5038
5039     /**
5040      * Default sortable of columns which have no sortable specified (defaults to false)
5041      * @type Boolean
5042      */
5043     this.defaultSortable = false;
5044
5045     this.addEvents({
5046         /**
5047              * @event widthchange
5048              * Fires when the width of a column changes.
5049              * @param {ColumnModel} this
5050              * @param {Number} columnIndex The column index
5051              * @param {Number} newWidth The new width
5052              */
5053             "widthchange": true,
5054         /**
5055              * @event headerchange
5056              * Fires when the text of a header changes.
5057              * @param {ColumnModel} this
5058              * @param {Number} columnIndex The column index
5059              * @param {Number} newText The new header text
5060              */
5061             "headerchange": true,
5062         /**
5063              * @event hiddenchange
5064              * Fires when a column is hidden or "unhidden".
5065              * @param {ColumnModel} this
5066              * @param {Number} columnIndex The column index
5067              * @param {Boolean} hidden true if hidden, false otherwise
5068              */
5069             "hiddenchange": true,
5070             /**
5071          * @event columnmoved
5072          * Fires when a column is moved.
5073          * @param {ColumnModel} this
5074          * @param {Number} oldIndex
5075          * @param {Number} newIndex
5076          */
5077         "columnmoved" : true,
5078         /**
5079          * @event columlockchange
5080          * Fires when a column's locked state is changed
5081          * @param {ColumnModel} this
5082          * @param {Number} colIndex
5083          * @param {Boolean} locked true if locked
5084          */
5085         "columnlockchange" : true
5086     });
5087     Roo.grid.ColumnModel.superclass.constructor.call(this);
5088 };
5089 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5090     /**
5091      * @cfg {String} header The header text to display in the Grid view.
5092      */
5093     /**
5094      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5095      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5096      * specified, the column's index is used as an index into the Record's data Array.
5097      */
5098     /**
5099      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5100      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5101      */
5102     /**
5103      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5104      * Defaults to the value of the {@link #defaultSortable} property.
5105      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5106      */
5107     /**
5108      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5109      */
5110     /**
5111      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5112      */
5113     /**
5114      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5115      */
5116     /**
5117      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5118      */
5119     /**
5120      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5121      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5122      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5123      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5124      */
5125        /**
5126      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5127      */
5128     /**
5129      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5130      */
5131     /**
5132      * @cfg {String} cursor (Optional)
5133      */
5134     /**
5135      * @cfg {String} tooltip (Optional)
5136      */
5137     /**
5138      * @cfg {Number} xs (Optional)
5139      */
5140     /**
5141      * @cfg {Number} sm (Optional)
5142      */
5143     /**
5144      * @cfg {Number} md (Optional)
5145      */
5146     /**
5147      * @cfg {Number} lg (Optional)
5148      */
5149     /**
5150      * Returns the id of the column at the specified index.
5151      * @param {Number} index The column index
5152      * @return {String} the id
5153      */
5154     getColumnId : function(index){
5155         return this.config[index].id;
5156     },
5157
5158     /**
5159      * Returns the column for a specified id.
5160      * @param {String} id The column id
5161      * @return {Object} the column
5162      */
5163     getColumnById : function(id){
5164         return this.lookup[id];
5165     },
5166
5167     
5168     /**
5169      * Returns the column for a specified dataIndex.
5170      * @param {String} dataIndex The column dataIndex
5171      * @return {Object|Boolean} the column or false if not found
5172      */
5173     getColumnByDataIndex: function(dataIndex){
5174         var index = this.findColumnIndex(dataIndex);
5175         return index > -1 ? this.config[index] : false;
5176     },
5177     
5178     /**
5179      * Returns the index for a specified column id.
5180      * @param {String} id The column id
5181      * @return {Number} the index, or -1 if not found
5182      */
5183     getIndexById : function(id){
5184         for(var i = 0, len = this.config.length; i < len; i++){
5185             if(this.config[i].id == id){
5186                 return i;
5187             }
5188         }
5189         return -1;
5190     },
5191     
5192     /**
5193      * Returns the index for a specified column dataIndex.
5194      * @param {String} dataIndex The column dataIndex
5195      * @return {Number} the index, or -1 if not found
5196      */
5197     
5198     findColumnIndex : function(dataIndex){
5199         for(var i = 0, len = this.config.length; i < len; i++){
5200             if(this.config[i].dataIndex == dataIndex){
5201                 return i;
5202             }
5203         }
5204         return -1;
5205     },
5206     
5207     
5208     moveColumn : function(oldIndex, newIndex){
5209         var c = this.config[oldIndex];
5210         this.config.splice(oldIndex, 1);
5211         this.config.splice(newIndex, 0, c);
5212         this.dataMap = null;
5213         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5214     },
5215
5216     isLocked : function(colIndex){
5217         return this.config[colIndex].locked === true;
5218     },
5219
5220     setLocked : function(colIndex, value, suppressEvent){
5221         if(this.isLocked(colIndex) == value){
5222             return;
5223         }
5224         this.config[colIndex].locked = value;
5225         if(!suppressEvent){
5226             this.fireEvent("columnlockchange", this, colIndex, value);
5227         }
5228     },
5229
5230     getTotalLockedWidth : function(){
5231         var totalWidth = 0;
5232         for(var i = 0; i < this.config.length; i++){
5233             if(this.isLocked(i) && !this.isHidden(i)){
5234                 this.totalWidth += this.getColumnWidth(i);
5235             }
5236         }
5237         return totalWidth;
5238     },
5239
5240     getLockedCount : function(){
5241         for(var i = 0, len = this.config.length; i < len; i++){
5242             if(!this.isLocked(i)){
5243                 return i;
5244             }
5245         }
5246         
5247         return this.config.length;
5248     },
5249
5250     /**
5251      * Returns the number of columns.
5252      * @return {Number}
5253      */
5254     getColumnCount : function(visibleOnly){
5255         if(visibleOnly === true){
5256             var c = 0;
5257             for(var i = 0, len = this.config.length; i < len; i++){
5258                 if(!this.isHidden(i)){
5259                     c++;
5260                 }
5261             }
5262             return c;
5263         }
5264         return this.config.length;
5265     },
5266
5267     /**
5268      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5269      * @param {Function} fn
5270      * @param {Object} scope (optional)
5271      * @return {Array} result
5272      */
5273     getColumnsBy : function(fn, scope){
5274         var r = [];
5275         for(var i = 0, len = this.config.length; i < len; i++){
5276             var c = this.config[i];
5277             if(fn.call(scope||this, c, i) === true){
5278                 r[r.length] = c;
5279             }
5280         }
5281         return r;
5282     },
5283
5284     /**
5285      * Returns true if the specified column is sortable.
5286      * @param {Number} col The column index
5287      * @return {Boolean}
5288      */
5289     isSortable : function(col){
5290         if(typeof this.config[col].sortable == "undefined"){
5291             return this.defaultSortable;
5292         }
5293         return this.config[col].sortable;
5294     },
5295
5296     /**
5297      * Returns the rendering (formatting) function defined for the column.
5298      * @param {Number} col The column index.
5299      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5300      */
5301     getRenderer : function(col){
5302         if(!this.config[col].renderer){
5303             return Roo.grid.ColumnModel.defaultRenderer;
5304         }
5305         return this.config[col].renderer;
5306     },
5307
5308     /**
5309      * Sets the rendering (formatting) function for a column.
5310      * @param {Number} col The column index
5311      * @param {Function} fn The function to use to process the cell's raw data
5312      * to return HTML markup for the grid view. The render function is called with
5313      * the following parameters:<ul>
5314      * <li>Data value.</li>
5315      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5316      * <li>css A CSS style string to apply to the table cell.</li>
5317      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5318      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5319      * <li>Row index</li>
5320      * <li>Column index</li>
5321      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5322      */
5323     setRenderer : function(col, fn){
5324         this.config[col].renderer = fn;
5325     },
5326
5327     /**
5328      * Returns the width for the specified column.
5329      * @param {Number} col The column index
5330      * @return {Number}
5331      */
5332     getColumnWidth : function(col){
5333         return this.config[col].width * 1 || this.defaultWidth;
5334     },
5335
5336     /**
5337      * Sets the width for a column.
5338      * @param {Number} col The column index
5339      * @param {Number} width The new width
5340      */
5341     setColumnWidth : function(col, width, suppressEvent){
5342         this.config[col].width = width;
5343         this.totalWidth = null;
5344         if(!suppressEvent){
5345              this.fireEvent("widthchange", this, col, width);
5346         }
5347     },
5348
5349     /**
5350      * Returns the total width of all columns.
5351      * @param {Boolean} includeHidden True to include hidden column widths
5352      * @return {Number}
5353      */
5354     getTotalWidth : function(includeHidden){
5355         if(!this.totalWidth){
5356             this.totalWidth = 0;
5357             for(var i = 0, len = this.config.length; i < len; i++){
5358                 if(includeHidden || !this.isHidden(i)){
5359                     this.totalWidth += this.getColumnWidth(i);
5360                 }
5361             }
5362         }
5363         return this.totalWidth;
5364     },
5365
5366     /**
5367      * Returns the header for the specified column.
5368      * @param {Number} col The column index
5369      * @return {String}
5370      */
5371     getColumnHeader : function(col){
5372         return this.config[col].header;
5373     },
5374
5375     /**
5376      * Sets the header for a column.
5377      * @param {Number} col The column index
5378      * @param {String} header The new header
5379      */
5380     setColumnHeader : function(col, header){
5381         this.config[col].header = header;
5382         this.fireEvent("headerchange", this, col, header);
5383     },
5384
5385     /**
5386      * Returns the tooltip for the specified column.
5387      * @param {Number} col The column index
5388      * @return {String}
5389      */
5390     getColumnTooltip : function(col){
5391             return this.config[col].tooltip;
5392     },
5393     /**
5394      * Sets the tooltip for a column.
5395      * @param {Number} col The column index
5396      * @param {String} tooltip The new tooltip
5397      */
5398     setColumnTooltip : function(col, tooltip){
5399             this.config[col].tooltip = tooltip;
5400     },
5401
5402     /**
5403      * Returns the dataIndex for the specified column.
5404      * @param {Number} col The column index
5405      * @return {Number}
5406      */
5407     getDataIndex : function(col){
5408         return this.config[col].dataIndex;
5409     },
5410
5411     /**
5412      * Sets the dataIndex for a column.
5413      * @param {Number} col The column index
5414      * @param {Number} dataIndex The new dataIndex
5415      */
5416     setDataIndex : function(col, dataIndex){
5417         this.config[col].dataIndex = dataIndex;
5418     },
5419
5420     
5421     
5422     /**
5423      * Returns true if the cell is editable.
5424      * @param {Number} colIndex The column index
5425      * @param {Number} rowIndex The row index - this is nto actually used..?
5426      * @return {Boolean}
5427      */
5428     isCellEditable : function(colIndex, rowIndex){
5429         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5430     },
5431
5432     /**
5433      * Returns the editor defined for the cell/column.
5434      * return false or null to disable editing.
5435      * @param {Number} colIndex The column index
5436      * @param {Number} rowIndex The row index
5437      * @return {Object}
5438      */
5439     getCellEditor : function(colIndex, rowIndex){
5440         return this.config[colIndex].editor;
5441     },
5442
5443     /**
5444      * Sets if a column is editable.
5445      * @param {Number} col The column index
5446      * @param {Boolean} editable True if the column is editable
5447      */
5448     setEditable : function(col, editable){
5449         this.config[col].editable = editable;
5450     },
5451
5452
5453     /**
5454      * Returns true if the column is hidden.
5455      * @param {Number} colIndex The column index
5456      * @return {Boolean}
5457      */
5458     isHidden : function(colIndex){
5459         return this.config[colIndex].hidden;
5460     },
5461
5462
5463     /**
5464      * Returns true if the column width cannot be changed
5465      */
5466     isFixed : function(colIndex){
5467         return this.config[colIndex].fixed;
5468     },
5469
5470     /**
5471      * Returns true if the column can be resized
5472      * @return {Boolean}
5473      */
5474     isResizable : function(colIndex){
5475         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5476     },
5477     /**
5478      * Sets if a column is hidden.
5479      * @param {Number} colIndex The column index
5480      * @param {Boolean} hidden True if the column is hidden
5481      */
5482     setHidden : function(colIndex, hidden){
5483         this.config[colIndex].hidden = hidden;
5484         this.totalWidth = null;
5485         this.fireEvent("hiddenchange", this, colIndex, hidden);
5486     },
5487
5488     /**
5489      * Sets the editor for a column.
5490      * @param {Number} col The column index
5491      * @param {Object} editor The editor object
5492      */
5493     setEditor : function(col, editor){
5494         this.config[col].editor = editor;
5495     }
5496 });
5497
5498 Roo.grid.ColumnModel.defaultRenderer = function(value){
5499         if(typeof value == "string" && value.length < 1){
5500             return "&#160;";
5501         }
5502         return value;
5503 };
5504
5505 // Alias for backwards compatibility
5506 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5507 /*
5508  * Based on:
5509  * Ext JS Library 1.1.1
5510  * Copyright(c) 2006-2007, Ext JS, LLC.
5511  *
5512  * Originally Released Under LGPL - original licence link has changed is not relivant.
5513  *
5514  * Fork - LGPL
5515  * <script type="text/javascript">
5516  */
5517  
5518 /**
5519  * @class Roo.LoadMask
5520  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5521  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5522  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5523  * element's UpdateManager load indicator and will be destroyed after the initial load.
5524  * @constructor
5525  * Create a new LoadMask
5526  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5527  * @param {Object} config The config object
5528  */
5529 Roo.LoadMask = function(el, config){
5530     this.el = Roo.get(el);
5531     Roo.apply(this, config);
5532     if(this.store){
5533         this.store.on('beforeload', this.onBeforeLoad, this);
5534         this.store.on('load', this.onLoad, this);
5535         this.store.on('loadexception', this.onLoadException, this);
5536         this.removeMask = false;
5537     }else{
5538         var um = this.el.getUpdateManager();
5539         um.showLoadIndicator = false; // disable the default indicator
5540         um.on('beforeupdate', this.onBeforeLoad, this);
5541         um.on('update', this.onLoad, this);
5542         um.on('failure', this.onLoad, this);
5543         this.removeMask = true;
5544     }
5545 };
5546
5547 Roo.LoadMask.prototype = {
5548     /**
5549      * @cfg {Boolean} removeMask
5550      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5551      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5552      */
5553     /**
5554      * @cfg {String} msg
5555      * The text to display in a centered loading message box (defaults to 'Loading...')
5556      */
5557     msg : 'Loading...',
5558     /**
5559      * @cfg {String} msgCls
5560      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5561      */
5562     msgCls : 'x-mask-loading',
5563
5564     /**
5565      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5566      * @type Boolean
5567      */
5568     disabled: false,
5569
5570     /**
5571      * Disables the mask to prevent it from being displayed
5572      */
5573     disable : function(){
5574        this.disabled = true;
5575     },
5576
5577     /**
5578      * Enables the mask so that it can be displayed
5579      */
5580     enable : function(){
5581         this.disabled = false;
5582     },
5583     
5584     onLoadException : function()
5585     {
5586         Roo.log(arguments);
5587         
5588         if (typeof(arguments[3]) != 'undefined') {
5589             Roo.MessageBox.alert("Error loading",arguments[3]);
5590         } 
5591         /*
5592         try {
5593             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5594                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5595             }   
5596         } catch(e) {
5597             
5598         }
5599         */
5600     
5601         
5602         
5603         this.el.unmask(this.removeMask);
5604     },
5605     // private
5606     onLoad : function()
5607     {
5608         this.el.unmask(this.removeMask);
5609     },
5610
5611     // private
5612     onBeforeLoad : function(){
5613         if(!this.disabled){
5614             this.el.mask(this.msg, this.msgCls);
5615         }
5616     },
5617
5618     // private
5619     destroy : function(){
5620         if(this.store){
5621             this.store.un('beforeload', this.onBeforeLoad, this);
5622             this.store.un('load', this.onLoad, this);
5623             this.store.un('loadexception', this.onLoadException, this);
5624         }else{
5625             var um = this.el.getUpdateManager();
5626             um.un('beforeupdate', this.onBeforeLoad, this);
5627             um.un('update', this.onLoad, this);
5628             um.un('failure', this.onLoad, this);
5629         }
5630     }
5631 };/*
5632  * - LGPL
5633  *
5634  * table
5635  * 
5636  */
5637
5638 /**
5639  * @class Roo.bootstrap.Table
5640  * @extends Roo.bootstrap.Component
5641  * Bootstrap Table class
5642  * @cfg {String} cls table class
5643  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5644  * @cfg {String} bgcolor Specifies the background color for a table
5645  * @cfg {Number} border Specifies whether the table cells should have borders or not
5646  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5647  * @cfg {Number} cellspacing Specifies the space between cells
5648  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5649  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5650  * @cfg {String} sortable Specifies that the table should be sortable
5651  * @cfg {String} summary Specifies a summary of the content of a table
5652  * @cfg {Number} width Specifies the width of a table
5653  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5654  * 
5655  * @cfg {boolean} striped Should the rows be alternative striped
5656  * @cfg {boolean} bordered Add borders to the table
5657  * @cfg {boolean} hover Add hover highlighting
5658  * @cfg {boolean} condensed Format condensed
5659  * @cfg {boolean} responsive Format condensed
5660  * @cfg {Boolean} loadMask (true|false) default false
5661  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5662  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5663  * @cfg {Boolean} rowSelection (true|false) default false
5664  * @cfg {Boolean} cellSelection (true|false) default false
5665  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5666  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5667  
5668  * 
5669  * @constructor
5670  * Create a new Table
5671  * @param {Object} config The config object
5672  */
5673
5674 Roo.bootstrap.Table = function(config){
5675     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5676     
5677     if (config.container) {
5678         // ctor'ed from a Border/panel.grid
5679         this.container = Roo.get(config.container);
5680         this.container.update("");
5681         this.container.setStyle("overflow", "hidden");
5682         this.container.addClass('x-grid-container');
5683
5684     }
5685     
5686     // BC...
5687     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5688     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5689     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5690     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5691     
5692     
5693     if (this.sm) {
5694         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5695         this.sm = this.selModel;
5696         this.sm.xmodule = this.xmodule || false;
5697     }
5698     if (this.cm && typeof(this.cm.config) == 'undefined') {
5699         this.colModel = new Roo.grid.ColumnModel(this.cm);
5700         this.cm = this.colModel;
5701         this.cm.xmodule = this.xmodule || false;
5702     }
5703     if (this.store) {
5704         this.store= Roo.factory(this.store, Roo.data);
5705         this.ds = this.store;
5706         this.ds.xmodule = this.xmodule || false;
5707          
5708     }
5709     if (this.footer && this.store) {
5710         this.footer.dataSource = this.ds;
5711         this.footer = Roo.factory(this.footer);
5712     }
5713     
5714     /** @private */
5715     this.addEvents({
5716         /**
5717          * @event cellclick
5718          * Fires when a cell is clicked
5719          * @param {Roo.bootstrap.Table} this
5720          * @param {Roo.Element} el
5721          * @param {Number} rowIndex
5722          * @param {Number} columnIndex
5723          * @param {Roo.EventObject} e
5724          */
5725         "cellclick" : true,
5726         /**
5727          * @event celldblclick
5728          * Fires when a cell is double clicked
5729          * @param {Roo.bootstrap.Table} this
5730          * @param {Roo.Element} el
5731          * @param {Number} rowIndex
5732          * @param {Number} columnIndex
5733          * @param {Roo.EventObject} e
5734          */
5735         "celldblclick" : true,
5736         /**
5737          * @event rowclick
5738          * Fires when a row is clicked
5739          * @param {Roo.bootstrap.Table} this
5740          * @param {Roo.Element} el
5741          * @param {Number} rowIndex
5742          * @param {Roo.EventObject} e
5743          */
5744         "rowclick" : true,
5745         /**
5746          * @event rowdblclick
5747          * Fires when a row is double clicked
5748          * @param {Roo.bootstrap.Table} this
5749          * @param {Roo.Element} el
5750          * @param {Number} rowIndex
5751          * @param {Roo.EventObject} e
5752          */
5753         "rowdblclick" : true,
5754         /**
5755          * @event mouseover
5756          * Fires when a mouseover occur
5757          * @param {Roo.bootstrap.Table} this
5758          * @param {Roo.Element} el
5759          * @param {Number} rowIndex
5760          * @param {Number} columnIndex
5761          * @param {Roo.EventObject} e
5762          */
5763         "mouseover" : true,
5764         /**
5765          * @event mouseout
5766          * Fires when a mouseout occur
5767          * @param {Roo.bootstrap.Table} this
5768          * @param {Roo.Element} el
5769          * @param {Number} rowIndex
5770          * @param {Number} columnIndex
5771          * @param {Roo.EventObject} e
5772          */
5773         "mouseout" : true,
5774         /**
5775          * @event rowclass
5776          * Fires when a row is rendered, so you can change add a style to it.
5777          * @param {Roo.bootstrap.Table} this
5778          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5779          */
5780         'rowclass' : true,
5781           /**
5782          * @event rowsrendered
5783          * Fires when all the  rows have been rendered
5784          * @param {Roo.bootstrap.Table} this
5785          */
5786         'rowsrendered' : true,
5787         /**
5788          * @event contextmenu
5789          * The raw contextmenu event for the entire grid.
5790          * @param {Roo.EventObject} e
5791          */
5792         "contextmenu" : true,
5793         /**
5794          * @event rowcontextmenu
5795          * Fires when a row is right clicked
5796          * @param {Roo.bootstrap.Table} this
5797          * @param {Number} rowIndex
5798          * @param {Roo.EventObject} e
5799          */
5800         "rowcontextmenu" : true,
5801         /**
5802          * @event cellcontextmenu
5803          * Fires when a cell is right clicked
5804          * @param {Roo.bootstrap.Table} this
5805          * @param {Number} rowIndex
5806          * @param {Number} cellIndex
5807          * @param {Roo.EventObject} e
5808          */
5809          "cellcontextmenu" : true,
5810          /**
5811          * @event headercontextmenu
5812          * Fires when a header is right clicked
5813          * @param {Roo.bootstrap.Table} this
5814          * @param {Number} columnIndex
5815          * @param {Roo.EventObject} e
5816          */
5817         "headercontextmenu" : true
5818     });
5819 };
5820
5821 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5822     
5823     cls: false,
5824     align: false,
5825     bgcolor: false,
5826     border: false,
5827     cellpadding: false,
5828     cellspacing: false,
5829     frame: false,
5830     rules: false,
5831     sortable: false,
5832     summary: false,
5833     width: false,
5834     striped : false,
5835     scrollBody : false,
5836     bordered: false,
5837     hover:  false,
5838     condensed : false,
5839     responsive : false,
5840     sm : false,
5841     cm : false,
5842     store : false,
5843     loadMask : false,
5844     footerShow : true,
5845     headerShow : true,
5846   
5847     rowSelection : false,
5848     cellSelection : false,
5849     layout : false,
5850     
5851     // Roo.Element - the tbody
5852     mainBody: false,
5853     // Roo.Element - thead element
5854     mainHead: false,
5855     
5856     container: false, // used by gridpanel...
5857     
5858     getAutoCreate : function()
5859     {
5860         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5861         
5862         cfg = {
5863             tag: 'table',
5864             cls : 'table',
5865             cn : []
5866         };
5867         if (this.scrollBody) {
5868             cfg.cls += ' table-body-fixed';
5869         }    
5870         if (this.striped) {
5871             cfg.cls += ' table-striped';
5872         }
5873         
5874         if (this.hover) {
5875             cfg.cls += ' table-hover';
5876         }
5877         if (this.bordered) {
5878             cfg.cls += ' table-bordered';
5879         }
5880         if (this.condensed) {
5881             cfg.cls += ' table-condensed';
5882         }
5883         if (this.responsive) {
5884             cfg.cls += ' table-responsive';
5885         }
5886         
5887         if (this.cls) {
5888             cfg.cls+=  ' ' +this.cls;
5889         }
5890         
5891         // this lot should be simplifed...
5892         
5893         if (this.align) {
5894             cfg.align=this.align;
5895         }
5896         if (this.bgcolor) {
5897             cfg.bgcolor=this.bgcolor;
5898         }
5899         if (this.border) {
5900             cfg.border=this.border;
5901         }
5902         if (this.cellpadding) {
5903             cfg.cellpadding=this.cellpadding;
5904         }
5905         if (this.cellspacing) {
5906             cfg.cellspacing=this.cellspacing;
5907         }
5908         if (this.frame) {
5909             cfg.frame=this.frame;
5910         }
5911         if (this.rules) {
5912             cfg.rules=this.rules;
5913         }
5914         if (this.sortable) {
5915             cfg.sortable=this.sortable;
5916         }
5917         if (this.summary) {
5918             cfg.summary=this.summary;
5919         }
5920         if (this.width) {
5921             cfg.width=this.width;
5922         }
5923         if (this.layout) {
5924             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5925         }
5926         
5927         if(this.store || this.cm){
5928             if(this.headerShow){
5929                 cfg.cn.push(this.renderHeader());
5930             }
5931             
5932             cfg.cn.push(this.renderBody());
5933             
5934             if(this.footerShow){
5935                 cfg.cn.push(this.renderFooter());
5936             }
5937             // where does this come from?
5938             //cfg.cls+=  ' TableGrid';
5939         }
5940         
5941         return { cn : [ cfg ] };
5942     },
5943     
5944     initEvents : function()
5945     {   
5946         if(!this.store || !this.cm){
5947             return;
5948         }
5949         
5950         //Roo.log('initEvents with ds!!!!');
5951         
5952         this.mainBody = this.el.select('tbody', true).first();
5953         this.mainHead = this.el.select('thead', true).first();
5954         
5955         
5956         
5957         var _this = this;
5958         
5959         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5960             e.on('click', _this.sort, _this);
5961         });
5962         
5963         this.el.on("click", this.onClick, this);
5964         this.el.on("dblclick", this.onDblClick, this);
5965         
5966         // why is this done????? = it breaks dialogs??
5967         //this.parent().el.setStyle('position', 'relative');
5968         
5969         
5970         if (this.footer) {
5971             this.footer.parentId = this.id;
5972             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5973         }
5974         
5975         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5976         
5977         this.store.on('load', this.onLoad, this);
5978         this.store.on('beforeload', this.onBeforeLoad, this);
5979         this.store.on('update', this.onUpdate, this);
5980         this.store.on('add', this.onAdd, this);
5981         
5982         this.el.on("contextmenu", this.onContextMenu, this);
5983         
5984         this.mainBody.on('scroll', this.onBodyScroll, this);
5985         
5986         
5987     },
5988     
5989     onContextMenu : function(e, t)
5990     {
5991         this.processEvent("contextmenu", e);
5992     },
5993     
5994     processEvent : function(name, e)
5995     {
5996         if (name != 'touchstart' ) {
5997             this.fireEvent(name, e);    
5998         }
5999         
6000         var t = e.getTarget();
6001         
6002         var cell = Roo.get(t);
6003         
6004         if(!cell){
6005             return;
6006         }
6007         
6008         if(cell.findParent('tfoot', false, true)){
6009             return;
6010         }
6011         
6012         if(cell.findParent('thead', false, true)){
6013             
6014             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6015                 cell = Roo.get(t).findParent('th', false, true);
6016                 if (!cell) {
6017                     Roo.log("failed to find th in thead?");
6018                     Roo.log(e.getTarget());
6019                     return;
6020                 }
6021             }
6022             
6023             var cellIndex = cell.dom.cellIndex;
6024             
6025             var ename = name == 'touchstart' ? 'click' : name;
6026             this.fireEvent("header" + ename, this, cellIndex, e);
6027             
6028             return;
6029         }
6030         
6031         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6032             cell = Roo.get(t).findParent('td', false, true);
6033             if (!cell) {
6034                 Roo.log("failed to find th in tbody?");
6035                 Roo.log(e.getTarget());
6036                 return;
6037             }
6038         }
6039         
6040         var row = cell.findParent('tr', false, true);
6041         var cellIndex = cell.dom.cellIndex;
6042         var rowIndex = row.dom.rowIndex - 1;
6043         
6044         if(row !== false){
6045             
6046             this.fireEvent("row" + name, this, rowIndex, e);
6047             
6048             if(cell !== false){
6049             
6050                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6051             }
6052         }
6053         
6054     },
6055     
6056     onMouseover : function(e, el)
6057     {
6058         var cell = Roo.get(el);
6059         
6060         if(!cell){
6061             return;
6062         }
6063         
6064         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6065             cell = cell.findParent('td', false, true);
6066         }
6067         
6068         var row = cell.findParent('tr', false, true);
6069         var cellIndex = cell.dom.cellIndex;
6070         var rowIndex = row.dom.rowIndex - 1; // start from 0
6071         
6072         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6073         
6074     },
6075     
6076     onMouseout : function(e, el)
6077     {
6078         var cell = Roo.get(el);
6079         
6080         if(!cell){
6081             return;
6082         }
6083         
6084         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6085             cell = cell.findParent('td', false, true);
6086         }
6087         
6088         var row = cell.findParent('tr', false, true);
6089         var cellIndex = cell.dom.cellIndex;
6090         var rowIndex = row.dom.rowIndex - 1; // start from 0
6091         
6092         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6093         
6094     },
6095     
6096     onClick : function(e, el)
6097     {
6098         var cell = Roo.get(el);
6099         
6100         if(!cell || (!this.cellSelection && !this.rowSelection)){
6101             return;
6102         }
6103         
6104         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6105             cell = cell.findParent('td', false, true);
6106         }
6107         
6108         if(!cell || typeof(cell) == 'undefined'){
6109             return;
6110         }
6111         
6112         var row = cell.findParent('tr', false, true);
6113         
6114         if(!row || typeof(row) == 'undefined'){
6115             return;
6116         }
6117         
6118         var cellIndex = cell.dom.cellIndex;
6119         var rowIndex = this.getRowIndex(row);
6120         
6121         // why??? - should these not be based on SelectionModel?
6122         if(this.cellSelection){
6123             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6124         }
6125         
6126         if(this.rowSelection){
6127             this.fireEvent('rowclick', this, row, rowIndex, e);
6128         }
6129         
6130         
6131     },
6132     
6133     onDblClick : function(e,el)
6134     {
6135         var cell = Roo.get(el);
6136         
6137         if(!cell || (!this.CellSelection && !this.RowSelection)){
6138             return;
6139         }
6140         
6141         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6142             cell = cell.findParent('td', false, true);
6143         }
6144         
6145         if(!cell || typeof(cell) == 'undefined'){
6146             return;
6147         }
6148         
6149         var row = cell.findParent('tr', false, true);
6150         
6151         if(!row || typeof(row) == 'undefined'){
6152             return;
6153         }
6154         
6155         var cellIndex = cell.dom.cellIndex;
6156         var rowIndex = this.getRowIndex(row);
6157         
6158         if(this.CellSelection){
6159             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6160         }
6161         
6162         if(this.RowSelection){
6163             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6164         }
6165     },
6166     
6167     sort : function(e,el)
6168     {
6169         var col = Roo.get(el);
6170         
6171         if(!col.hasClass('sortable')){
6172             return;
6173         }
6174         
6175         var sort = col.attr('sort');
6176         var dir = 'ASC';
6177         
6178         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6179             dir = 'DESC';
6180         }
6181         
6182         this.store.sortInfo = {field : sort, direction : dir};
6183         
6184         if (this.footer) {
6185             Roo.log("calling footer first");
6186             this.footer.onClick('first');
6187         } else {
6188         
6189             this.store.load({ params : { start : 0 } });
6190         }
6191     },
6192     
6193     renderHeader : function()
6194     {
6195         var header = {
6196             tag: 'thead',
6197             cn : []
6198         };
6199         
6200         var cm = this.cm;
6201         this.totalWidth = 0;
6202         
6203         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6204             
6205             var config = cm.config[i];
6206             
6207             var c = {
6208                 tag: 'th',
6209                 style : '',
6210                 html: cm.getColumnHeader(i)
6211             };
6212             
6213             var hh = '';
6214             
6215             if(typeof(config.sortable) != 'undefined' && config.sortable){
6216                 c.cls = 'sortable';
6217                 c.html = '<i class="glyphicon"></i>' + c.html;
6218             }
6219             
6220             if(typeof(config.lgHeader) != 'undefined'){
6221                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6222             }
6223             
6224             if(typeof(config.mdHeader) != 'undefined'){
6225                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6226             }
6227             
6228             if(typeof(config.smHeader) != 'undefined'){
6229                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6230             }
6231             
6232             if(typeof(config.xsHeader) != 'undefined'){
6233                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6234             }
6235             
6236             if(hh.length){
6237                 c.html = hh;
6238             }
6239             
6240             if(typeof(config.tooltip) != 'undefined'){
6241                 c.tooltip = config.tooltip;
6242             }
6243             
6244             if(typeof(config.colspan) != 'undefined'){
6245                 c.colspan = config.colspan;
6246             }
6247             
6248             if(typeof(config.hidden) != 'undefined' && config.hidden){
6249                 c.style += ' display:none;';
6250             }
6251             
6252             if(typeof(config.dataIndex) != 'undefined'){
6253                 c.sort = config.dataIndex;
6254             }
6255             
6256            
6257             
6258             if(typeof(config.align) != 'undefined' && config.align.length){
6259                 c.style += ' text-align:' + config.align + ';';
6260             }
6261             
6262             if(typeof(config.width) != 'undefined'){
6263                 c.style += ' width:' + config.width + 'px;';
6264                 this.totalWidth += config.width;
6265             } else {
6266                 this.totalWidth += 100; // assume minimum of 100 per column?
6267             }
6268             
6269             if(typeof(config.cls) != 'undefined'){
6270                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6271             }
6272             
6273             ['xs','sm','md','lg'].map(function(size){
6274                 
6275                 if(typeof(config[size]) == 'undefined'){
6276                     return;
6277                 }
6278                 
6279                 if (!config[size]) { // 0 = hidden
6280                     c.cls += ' hidden-' + size;
6281                     return;
6282                 }
6283                 
6284                 c.cls += ' col-' + size + '-' + config[size];
6285
6286             });
6287             
6288             header.cn.push(c)
6289         }
6290         
6291         return header;
6292     },
6293     
6294     renderBody : function()
6295     {
6296         var body = {
6297             tag: 'tbody',
6298             cn : [
6299                 {
6300                     tag: 'tr',
6301                     cn : [
6302                         {
6303                             tag : 'td',
6304                             colspan :  this.cm.getColumnCount()
6305                         }
6306                     ]
6307                 }
6308             ]
6309         };
6310         
6311         return body;
6312     },
6313     
6314     renderFooter : function()
6315     {
6316         var footer = {
6317             tag: 'tfoot',
6318             cn : [
6319                 {
6320                     tag: 'tr',
6321                     cn : [
6322                         {
6323                             tag : 'td',
6324                             colspan :  this.cm.getColumnCount()
6325                         }
6326                     ]
6327                 }
6328             ]
6329         };
6330         
6331         return footer;
6332     },
6333     
6334     
6335     
6336     onLoad : function()
6337     {
6338 //        Roo.log('ds onload');
6339         this.clear();
6340         
6341         var _this = this;
6342         var cm = this.cm;
6343         var ds = this.store;
6344         
6345         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6346             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6347             if (_this.store.sortInfo) {
6348                     
6349                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6350                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6351                 }
6352                 
6353                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6354                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6355                 }
6356             }
6357         });
6358         
6359         var tbody =  this.mainBody;
6360               
6361         if(ds.getCount() > 0){
6362             ds.data.each(function(d,rowIndex){
6363                 var row =  this.renderRow(cm, ds, rowIndex);
6364                 
6365                 tbody.createChild(row);
6366                 
6367                 var _this = this;
6368                 
6369                 if(row.cellObjects.length){
6370                     Roo.each(row.cellObjects, function(r){
6371                         _this.renderCellObject(r);
6372                     })
6373                 }
6374                 
6375             }, this);
6376         }
6377         
6378         Roo.each(this.el.select('tbody td', true).elements, function(e){
6379             e.on('mouseover', _this.onMouseover, _this);
6380         });
6381         
6382         Roo.each(this.el.select('tbody td', true).elements, function(e){
6383             e.on('mouseout', _this.onMouseout, _this);
6384         });
6385         this.fireEvent('rowsrendered', this);
6386         //if(this.loadMask){
6387         //    this.maskEl.hide();
6388         //}
6389         
6390         this.autoSize();
6391     },
6392     
6393     
6394     onUpdate : function(ds,record)
6395     {
6396         this.refreshRow(record);
6397     },
6398     
6399     onRemove : function(ds, record, index, isUpdate){
6400         if(isUpdate !== true){
6401             this.fireEvent("beforerowremoved", this, index, record);
6402         }
6403         var bt = this.mainBody.dom;
6404         
6405         var rows = this.el.select('tbody > tr', true).elements;
6406         
6407         if(typeof(rows[index]) != 'undefined'){
6408             bt.removeChild(rows[index].dom);
6409         }
6410         
6411 //        if(bt.rows[index]){
6412 //            bt.removeChild(bt.rows[index]);
6413 //        }
6414         
6415         if(isUpdate !== true){
6416             //this.stripeRows(index);
6417             //this.syncRowHeights(index, index);
6418             //this.layout();
6419             this.fireEvent("rowremoved", this, index, record);
6420         }
6421     },
6422     
6423     onAdd : function(ds, records, rowIndex)
6424     {
6425         //Roo.log('on Add called');
6426         // - note this does not handle multiple adding very well..
6427         var bt = this.mainBody.dom;
6428         for (var i =0 ; i < records.length;i++) {
6429             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6430             //Roo.log(records[i]);
6431             //Roo.log(this.store.getAt(rowIndex+i));
6432             this.insertRow(this.store, rowIndex + i, false);
6433             return;
6434         }
6435         
6436     },
6437     
6438     
6439     refreshRow : function(record){
6440         var ds = this.store, index;
6441         if(typeof record == 'number'){
6442             index = record;
6443             record = ds.getAt(index);
6444         }else{
6445             index = ds.indexOf(record);
6446         }
6447         this.insertRow(ds, index, true);
6448         this.onRemove(ds, record, index+1, true);
6449         //this.syncRowHeights(index, index);
6450         //this.layout();
6451         this.fireEvent("rowupdated", this, index, record);
6452     },
6453     
6454     insertRow : function(dm, rowIndex, isUpdate){
6455         
6456         if(!isUpdate){
6457             this.fireEvent("beforerowsinserted", this, rowIndex);
6458         }
6459             //var s = this.getScrollState();
6460         var row = this.renderRow(this.cm, this.store, rowIndex);
6461         // insert before rowIndex..
6462         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6463         
6464         var _this = this;
6465                 
6466         if(row.cellObjects.length){
6467             Roo.each(row.cellObjects, function(r){
6468                 _this.renderCellObject(r);
6469             })
6470         }
6471             
6472         if(!isUpdate){
6473             this.fireEvent("rowsinserted", this, rowIndex);
6474             //this.syncRowHeights(firstRow, lastRow);
6475             //this.stripeRows(firstRow);
6476             //this.layout();
6477         }
6478         
6479     },
6480     
6481     
6482     getRowDom : function(rowIndex)
6483     {
6484         var rows = this.el.select('tbody > tr', true).elements;
6485         
6486         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6487         
6488     },
6489     // returns the object tree for a tr..
6490   
6491     
6492     renderRow : function(cm, ds, rowIndex) 
6493     {
6494         
6495         var d = ds.getAt(rowIndex);
6496         
6497         var row = {
6498             tag : 'tr',
6499             cn : []
6500         };
6501             
6502         var cellObjects = [];
6503         
6504         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6505             var config = cm.config[i];
6506             
6507             var renderer = cm.getRenderer(i);
6508             var value = '';
6509             var id = false;
6510             
6511             if(typeof(renderer) !== 'undefined'){
6512                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6513             }
6514             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6515             // and are rendered into the cells after the row is rendered - using the id for the element.
6516             
6517             if(typeof(value) === 'object'){
6518                 id = Roo.id();
6519                 cellObjects.push({
6520                     container : id,
6521                     cfg : value 
6522                 })
6523             }
6524             
6525             var rowcfg = {
6526                 record: d,
6527                 rowIndex : rowIndex,
6528                 colIndex : i,
6529                 rowClass : ''
6530             };
6531
6532             this.fireEvent('rowclass', this, rowcfg);
6533             
6534             var td = {
6535                 tag: 'td',
6536                 cls : rowcfg.rowClass,
6537                 style: '',
6538                 html: (typeof(value) === 'object') ? '' : value
6539             };
6540             
6541             if (id) {
6542                 td.id = id;
6543             }
6544             
6545             if(typeof(config.colspan) != 'undefined'){
6546                 td.colspan = config.colspan;
6547             }
6548             
6549             if(typeof(config.hidden) != 'undefined' && config.hidden){
6550                 td.style += ' display:none;';
6551             }
6552             
6553             if(typeof(config.align) != 'undefined' && config.align.length){
6554                 td.style += ' text-align:' + config.align + ';';
6555             }
6556             
6557             if(typeof(config.width) != 'undefined'){
6558                 td.style += ' width:' +  config.width + 'px;';
6559             }
6560             
6561             if(typeof(config.cursor) != 'undefined'){
6562                 td.style += ' cursor:' +  config.cursor + ';';
6563             }
6564             
6565             if(typeof(config.cls) != 'undefined'){
6566                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6567             }
6568             
6569             ['xs','sm','md','lg'].map(function(size){
6570                 
6571                 if(typeof(config[size]) == 'undefined'){
6572                     return;
6573                 }
6574                 
6575                 if (!config[size]) { // 0 = hidden
6576                     td.cls += ' hidden-' + size;
6577                     return;
6578                 }
6579                 
6580                 td.cls += ' col-' + size + '-' + config[size];
6581
6582             });
6583              
6584             row.cn.push(td);
6585            
6586         }
6587         
6588         row.cellObjects = cellObjects;
6589         
6590         return row;
6591           
6592     },
6593     
6594     
6595     
6596     onBeforeLoad : function()
6597     {
6598         //Roo.log('ds onBeforeLoad');
6599         
6600         //this.clear();
6601         
6602         //if(this.loadMask){
6603         //    this.maskEl.show();
6604         //}
6605     },
6606      /**
6607      * Remove all rows
6608      */
6609     clear : function()
6610     {
6611         this.el.select('tbody', true).first().dom.innerHTML = '';
6612     },
6613     /**
6614      * Show or hide a row.
6615      * @param {Number} rowIndex to show or hide
6616      * @param {Boolean} state hide
6617      */
6618     setRowVisibility : function(rowIndex, state)
6619     {
6620         var bt = this.mainBody.dom;
6621         
6622         var rows = this.el.select('tbody > tr', true).elements;
6623         
6624         if(typeof(rows[rowIndex]) == 'undefined'){
6625             return;
6626         }
6627         rows[rowIndex].dom.style.display = state ? '' : 'none';
6628     },
6629     
6630     
6631     getSelectionModel : function(){
6632         if(!this.selModel){
6633             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6634         }
6635         return this.selModel;
6636     },
6637     /*
6638      * Render the Roo.bootstrap object from renderder
6639      */
6640     renderCellObject : function(r)
6641     {
6642         var _this = this;
6643         
6644         var t = r.cfg.render(r.container);
6645         
6646         if(r.cfg.cn){
6647             Roo.each(r.cfg.cn, function(c){
6648                 var child = {
6649                     container: t.getChildContainer(),
6650                     cfg: c
6651                 };
6652                 _this.renderCellObject(child);
6653             })
6654         }
6655     },
6656     
6657     getRowIndex : function(row)
6658     {
6659         var rowIndex = -1;
6660         
6661         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6662             if(el != row){
6663                 return;
6664             }
6665             
6666             rowIndex = index;
6667         });
6668         
6669         return rowIndex;
6670     },
6671      /**
6672      * Returns the grid's underlying element = used by panel.Grid
6673      * @return {Element} The element
6674      */
6675     getGridEl : function(){
6676         return this.container;
6677     },
6678      /**
6679      * Forces a resize - used by panel.Grid
6680      * @return {Element} The element
6681      */
6682     autoSize : function(){
6683         var ctr = Roo.get(this.container.dom.parentElement);
6684         
6685         var thd = this.getGridEl().select('thead',true).first();
6686         var tbd = this.getGridEl().select('tbody', true).first();
6687         
6688         
6689         var cw = ctr.getWidth();
6690         
6691         if (tbd) {
6692             
6693             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6694             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6695             cw -= barsize;
6696         }
6697         cw = Math.max(cw, this.totalWidth);
6698         this.getGridEl().select('tr',true).setWidth(cw);
6699         
6700         return; // we doe not have a view in this design..
6701         if(this.rendered){
6702             this.view.layout();
6703             if(this.view.adjustForScroll){
6704                 this.view.adjustForScroll();
6705             }
6706         }
6707     },
6708     onBodyScroll: function()
6709     {
6710         
6711         Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6712         this.mainHead.setStyle({
6713                     'position' : 'relative',
6714                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6715         });
6716         
6717         
6718     }
6719 });
6720
6721  
6722
6723  /*
6724  * - LGPL
6725  *
6726  * table cell
6727  * 
6728  */
6729
6730 /**
6731  * @class Roo.bootstrap.TableCell
6732  * @extends Roo.bootstrap.Component
6733  * Bootstrap TableCell class
6734  * @cfg {String} html cell contain text
6735  * @cfg {String} cls cell class
6736  * @cfg {String} tag cell tag (td|th) default td
6737  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6738  * @cfg {String} align Aligns the content in a cell
6739  * @cfg {String} axis Categorizes cells
6740  * @cfg {String} bgcolor Specifies the background color of a cell
6741  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6742  * @cfg {Number} colspan Specifies the number of columns a cell should span
6743  * @cfg {String} headers Specifies one or more header cells a cell is related to
6744  * @cfg {Number} height Sets the height of a cell
6745  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6746  * @cfg {Number} rowspan Sets the number of rows a cell should span
6747  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6748  * @cfg {String} valign Vertical aligns the content in a cell
6749  * @cfg {Number} width Specifies the width of a cell
6750  * 
6751  * @constructor
6752  * Create a new TableCell
6753  * @param {Object} config The config object
6754  */
6755
6756 Roo.bootstrap.TableCell = function(config){
6757     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6758 };
6759
6760 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6761     
6762     html: false,
6763     cls: false,
6764     tag: false,
6765     abbr: false,
6766     align: false,
6767     axis: false,
6768     bgcolor: false,
6769     charoff: false,
6770     colspan: false,
6771     headers: false,
6772     height: false,
6773     nowrap: false,
6774     rowspan: false,
6775     scope: false,
6776     valign: false,
6777     width: false,
6778     
6779     
6780     getAutoCreate : function(){
6781         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6782         
6783         cfg = {
6784             tag: 'td'
6785         };
6786         
6787         if(this.tag){
6788             cfg.tag = this.tag;
6789         }
6790         
6791         if (this.html) {
6792             cfg.html=this.html
6793         }
6794         if (this.cls) {
6795             cfg.cls=this.cls
6796         }
6797         if (this.abbr) {
6798             cfg.abbr=this.abbr
6799         }
6800         if (this.align) {
6801             cfg.align=this.align
6802         }
6803         if (this.axis) {
6804             cfg.axis=this.axis
6805         }
6806         if (this.bgcolor) {
6807             cfg.bgcolor=this.bgcolor
6808         }
6809         if (this.charoff) {
6810             cfg.charoff=this.charoff
6811         }
6812         if (this.colspan) {
6813             cfg.colspan=this.colspan
6814         }
6815         if (this.headers) {
6816             cfg.headers=this.headers
6817         }
6818         if (this.height) {
6819             cfg.height=this.height
6820         }
6821         if (this.nowrap) {
6822             cfg.nowrap=this.nowrap
6823         }
6824         if (this.rowspan) {
6825             cfg.rowspan=this.rowspan
6826         }
6827         if (this.scope) {
6828             cfg.scope=this.scope
6829         }
6830         if (this.valign) {
6831             cfg.valign=this.valign
6832         }
6833         if (this.width) {
6834             cfg.width=this.width
6835         }
6836         
6837         
6838         return cfg;
6839     }
6840    
6841 });
6842
6843  
6844
6845  /*
6846  * - LGPL
6847  *
6848  * table row
6849  * 
6850  */
6851
6852 /**
6853  * @class Roo.bootstrap.TableRow
6854  * @extends Roo.bootstrap.Component
6855  * Bootstrap TableRow class
6856  * @cfg {String} cls row class
6857  * @cfg {String} align Aligns the content in a table row
6858  * @cfg {String} bgcolor Specifies a background color for a table row
6859  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6860  * @cfg {String} valign Vertical aligns the content in a table row
6861  * 
6862  * @constructor
6863  * Create a new TableRow
6864  * @param {Object} config The config object
6865  */
6866
6867 Roo.bootstrap.TableRow = function(config){
6868     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6869 };
6870
6871 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6872     
6873     cls: false,
6874     align: false,
6875     bgcolor: false,
6876     charoff: false,
6877     valign: false,
6878     
6879     getAutoCreate : function(){
6880         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6881         
6882         cfg = {
6883             tag: 'tr'
6884         };
6885             
6886         if(this.cls){
6887             cfg.cls = this.cls;
6888         }
6889         if(this.align){
6890             cfg.align = this.align;
6891         }
6892         if(this.bgcolor){
6893             cfg.bgcolor = this.bgcolor;
6894         }
6895         if(this.charoff){
6896             cfg.charoff = this.charoff;
6897         }
6898         if(this.valign){
6899             cfg.valign = this.valign;
6900         }
6901         
6902         return cfg;
6903     }
6904    
6905 });
6906
6907  
6908
6909  /*
6910  * - LGPL
6911  *
6912  * table body
6913  * 
6914  */
6915
6916 /**
6917  * @class Roo.bootstrap.TableBody
6918  * @extends Roo.bootstrap.Component
6919  * Bootstrap TableBody class
6920  * @cfg {String} cls element class
6921  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6922  * @cfg {String} align Aligns the content inside the element
6923  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6924  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6925  * 
6926  * @constructor
6927  * Create a new TableBody
6928  * @param {Object} config The config object
6929  */
6930
6931 Roo.bootstrap.TableBody = function(config){
6932     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6933 };
6934
6935 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6936     
6937     cls: false,
6938     tag: false,
6939     align: false,
6940     charoff: false,
6941     valign: false,
6942     
6943     getAutoCreate : function(){
6944         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6945         
6946         cfg = {
6947             tag: 'tbody'
6948         };
6949             
6950         if (this.cls) {
6951             cfg.cls=this.cls
6952         }
6953         if(this.tag){
6954             cfg.tag = this.tag;
6955         }
6956         
6957         if(this.align){
6958             cfg.align = this.align;
6959         }
6960         if(this.charoff){
6961             cfg.charoff = this.charoff;
6962         }
6963         if(this.valign){
6964             cfg.valign = this.valign;
6965         }
6966         
6967         return cfg;
6968     }
6969     
6970     
6971 //    initEvents : function()
6972 //    {
6973 //        
6974 //        if(!this.store){
6975 //            return;
6976 //        }
6977 //        
6978 //        this.store = Roo.factory(this.store, Roo.data);
6979 //        this.store.on('load', this.onLoad, this);
6980 //        
6981 //        this.store.load();
6982 //        
6983 //    },
6984 //    
6985 //    onLoad: function () 
6986 //    {   
6987 //        this.fireEvent('load', this);
6988 //    }
6989 //    
6990 //   
6991 });
6992
6993  
6994
6995  /*
6996  * Based on:
6997  * Ext JS Library 1.1.1
6998  * Copyright(c) 2006-2007, Ext JS, LLC.
6999  *
7000  * Originally Released Under LGPL - original licence link has changed is not relivant.
7001  *
7002  * Fork - LGPL
7003  * <script type="text/javascript">
7004  */
7005
7006 // as we use this in bootstrap.
7007 Roo.namespace('Roo.form');
7008  /**
7009  * @class Roo.form.Action
7010  * Internal Class used to handle form actions
7011  * @constructor
7012  * @param {Roo.form.BasicForm} el The form element or its id
7013  * @param {Object} config Configuration options
7014  */
7015
7016  
7017  
7018 // define the action interface
7019 Roo.form.Action = function(form, options){
7020     this.form = form;
7021     this.options = options || {};
7022 };
7023 /**
7024  * Client Validation Failed
7025  * @const 
7026  */
7027 Roo.form.Action.CLIENT_INVALID = 'client';
7028 /**
7029  * Server Validation Failed
7030  * @const 
7031  */
7032 Roo.form.Action.SERVER_INVALID = 'server';
7033  /**
7034  * Connect to Server Failed
7035  * @const 
7036  */
7037 Roo.form.Action.CONNECT_FAILURE = 'connect';
7038 /**
7039  * Reading Data from Server Failed
7040  * @const 
7041  */
7042 Roo.form.Action.LOAD_FAILURE = 'load';
7043
7044 Roo.form.Action.prototype = {
7045     type : 'default',
7046     failureType : undefined,
7047     response : undefined,
7048     result : undefined,
7049
7050     // interface method
7051     run : function(options){
7052
7053     },
7054
7055     // interface method
7056     success : function(response){
7057
7058     },
7059
7060     // interface method
7061     handleResponse : function(response){
7062
7063     },
7064
7065     // default connection failure
7066     failure : function(response){
7067         
7068         this.response = response;
7069         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7070         this.form.afterAction(this, false);
7071     },
7072
7073     processResponse : function(response){
7074         this.response = response;
7075         if(!response.responseText){
7076             return true;
7077         }
7078         this.result = this.handleResponse(response);
7079         return this.result;
7080     },
7081
7082     // utility functions used internally
7083     getUrl : function(appendParams){
7084         var url = this.options.url || this.form.url || this.form.el.dom.action;
7085         if(appendParams){
7086             var p = this.getParams();
7087             if(p){
7088                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7089             }
7090         }
7091         return url;
7092     },
7093
7094     getMethod : function(){
7095         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7096     },
7097
7098     getParams : function(){
7099         var bp = this.form.baseParams;
7100         var p = this.options.params;
7101         if(p){
7102             if(typeof p == "object"){
7103                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7104             }else if(typeof p == 'string' && bp){
7105                 p += '&' + Roo.urlEncode(bp);
7106             }
7107         }else if(bp){
7108             p = Roo.urlEncode(bp);
7109         }
7110         return p;
7111     },
7112
7113     createCallback : function(){
7114         return {
7115             success: this.success,
7116             failure: this.failure,
7117             scope: this,
7118             timeout: (this.form.timeout*1000),
7119             upload: this.form.fileUpload ? this.success : undefined
7120         };
7121     }
7122 };
7123
7124 Roo.form.Action.Submit = function(form, options){
7125     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7126 };
7127
7128 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7129     type : 'submit',
7130
7131     haveProgress : false,
7132     uploadComplete : false,
7133     
7134     // uploadProgress indicator.
7135     uploadProgress : function()
7136     {
7137         if (!this.form.progressUrl) {
7138             return;
7139         }
7140         
7141         if (!this.haveProgress) {
7142             Roo.MessageBox.progress("Uploading", "Uploading");
7143         }
7144         if (this.uploadComplete) {
7145            Roo.MessageBox.hide();
7146            return;
7147         }
7148         
7149         this.haveProgress = true;
7150    
7151         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7152         
7153         var c = new Roo.data.Connection();
7154         c.request({
7155             url : this.form.progressUrl,
7156             params: {
7157                 id : uid
7158             },
7159             method: 'GET',
7160             success : function(req){
7161                //console.log(data);
7162                 var rdata = false;
7163                 var edata;
7164                 try  {
7165                    rdata = Roo.decode(req.responseText)
7166                 } catch (e) {
7167                     Roo.log("Invalid data from server..");
7168                     Roo.log(edata);
7169                     return;
7170                 }
7171                 if (!rdata || !rdata.success) {
7172                     Roo.log(rdata);
7173                     Roo.MessageBox.alert(Roo.encode(rdata));
7174                     return;
7175                 }
7176                 var data = rdata.data;
7177                 
7178                 if (this.uploadComplete) {
7179                    Roo.MessageBox.hide();
7180                    return;
7181                 }
7182                    
7183                 if (data){
7184                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7185                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7186                     );
7187                 }
7188                 this.uploadProgress.defer(2000,this);
7189             },
7190        
7191             failure: function(data) {
7192                 Roo.log('progress url failed ');
7193                 Roo.log(data);
7194             },
7195             scope : this
7196         });
7197            
7198     },
7199     
7200     
7201     run : function()
7202     {
7203         // run get Values on the form, so it syncs any secondary forms.
7204         this.form.getValues();
7205         
7206         var o = this.options;
7207         var method = this.getMethod();
7208         var isPost = method == 'POST';
7209         if(o.clientValidation === false || this.form.isValid()){
7210             
7211             if (this.form.progressUrl) {
7212                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7213                     (new Date() * 1) + '' + Math.random());
7214                     
7215             } 
7216             
7217             
7218             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7219                 form:this.form.el.dom,
7220                 url:this.getUrl(!isPost),
7221                 method: method,
7222                 params:isPost ? this.getParams() : null,
7223                 isUpload: this.form.fileUpload
7224             }));
7225             
7226             this.uploadProgress();
7227
7228         }else if (o.clientValidation !== false){ // client validation failed
7229             this.failureType = Roo.form.Action.CLIENT_INVALID;
7230             this.form.afterAction(this, false);
7231         }
7232     },
7233
7234     success : function(response)
7235     {
7236         this.uploadComplete= true;
7237         if (this.haveProgress) {
7238             Roo.MessageBox.hide();
7239         }
7240         
7241         
7242         var result = this.processResponse(response);
7243         if(result === true || result.success){
7244             this.form.afterAction(this, true);
7245             return;
7246         }
7247         if(result.errors){
7248             this.form.markInvalid(result.errors);
7249             this.failureType = Roo.form.Action.SERVER_INVALID;
7250         }
7251         this.form.afterAction(this, false);
7252     },
7253     failure : function(response)
7254     {
7255         this.uploadComplete= true;
7256         if (this.haveProgress) {
7257             Roo.MessageBox.hide();
7258         }
7259         
7260         this.response = response;
7261         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7262         this.form.afterAction(this, false);
7263     },
7264     
7265     handleResponse : function(response){
7266         if(this.form.errorReader){
7267             var rs = this.form.errorReader.read(response);
7268             var errors = [];
7269             if(rs.records){
7270                 for(var i = 0, len = rs.records.length; i < len; i++) {
7271                     var r = rs.records[i];
7272                     errors[i] = r.data;
7273                 }
7274             }
7275             if(errors.length < 1){
7276                 errors = null;
7277             }
7278             return {
7279                 success : rs.success,
7280                 errors : errors
7281             };
7282         }
7283         var ret = false;
7284         try {
7285             ret = Roo.decode(response.responseText);
7286         } catch (e) {
7287             ret = {
7288                 success: false,
7289                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7290                 errors : []
7291             };
7292         }
7293         return ret;
7294         
7295     }
7296 });
7297
7298
7299 Roo.form.Action.Load = function(form, options){
7300     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7301     this.reader = this.form.reader;
7302 };
7303
7304 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7305     type : 'load',
7306
7307     run : function(){
7308         
7309         Roo.Ajax.request(Roo.apply(
7310                 this.createCallback(), {
7311                     method:this.getMethod(),
7312                     url:this.getUrl(false),
7313                     params:this.getParams()
7314         }));
7315     },
7316
7317     success : function(response){
7318         
7319         var result = this.processResponse(response);
7320         if(result === true || !result.success || !result.data){
7321             this.failureType = Roo.form.Action.LOAD_FAILURE;
7322             this.form.afterAction(this, false);
7323             return;
7324         }
7325         this.form.clearInvalid();
7326         this.form.setValues(result.data);
7327         this.form.afterAction(this, true);
7328     },
7329
7330     handleResponse : function(response){
7331         if(this.form.reader){
7332             var rs = this.form.reader.read(response);
7333             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7334             return {
7335                 success : rs.success,
7336                 data : data
7337             };
7338         }
7339         return Roo.decode(response.responseText);
7340     }
7341 });
7342
7343 Roo.form.Action.ACTION_TYPES = {
7344     'load' : Roo.form.Action.Load,
7345     'submit' : Roo.form.Action.Submit
7346 };/*
7347  * - LGPL
7348  *
7349  * form
7350  * 
7351  */
7352
7353 /**
7354  * @class Roo.bootstrap.Form
7355  * @extends Roo.bootstrap.Component
7356  * Bootstrap Form class
7357  * @cfg {String} method  GET | POST (default POST)
7358  * @cfg {String} labelAlign top | left (default top)
7359  * @cfg {String} align left  | right - for navbars
7360  * @cfg {Boolean} loadMask load mask when submit (default true)
7361
7362  * 
7363  * @constructor
7364  * Create a new Form
7365  * @param {Object} config The config object
7366  */
7367
7368
7369 Roo.bootstrap.Form = function(config){
7370     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7371     this.addEvents({
7372         /**
7373          * @event clientvalidation
7374          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7375          * @param {Form} this
7376          * @param {Boolean} valid true if the form has passed client-side validation
7377          */
7378         clientvalidation: true,
7379         /**
7380          * @event beforeaction
7381          * Fires before any action is performed. Return false to cancel the action.
7382          * @param {Form} this
7383          * @param {Action} action The action to be performed
7384          */
7385         beforeaction: true,
7386         /**
7387          * @event actionfailed
7388          * Fires when an action fails.
7389          * @param {Form} this
7390          * @param {Action} action The action that failed
7391          */
7392         actionfailed : true,
7393         /**
7394          * @event actioncomplete
7395          * Fires when an action is completed.
7396          * @param {Form} this
7397          * @param {Action} action The action that completed
7398          */
7399         actioncomplete : true
7400     });
7401     
7402 };
7403
7404 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7405       
7406      /**
7407      * @cfg {String} method
7408      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7409      */
7410     method : 'POST',
7411     /**
7412      * @cfg {String} url
7413      * The URL to use for form actions if one isn't supplied in the action options.
7414      */
7415     /**
7416      * @cfg {Boolean} fileUpload
7417      * Set to true if this form is a file upload.
7418      */
7419      
7420     /**
7421      * @cfg {Object} baseParams
7422      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7423      */
7424       
7425     /**
7426      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7427      */
7428     timeout: 30,
7429     /**
7430      * @cfg {Sting} align (left|right) for navbar forms
7431      */
7432     align : 'left',
7433
7434     // private
7435     activeAction : null,
7436  
7437     /**
7438      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7439      * element by passing it or its id or mask the form itself by passing in true.
7440      * @type Mixed
7441      */
7442     waitMsgTarget : false,
7443     
7444     loadMask : true,
7445     
7446     getAutoCreate : function(){
7447         
7448         var cfg = {
7449             tag: 'form',
7450             method : this.method || 'POST',
7451             id : this.id || Roo.id(),
7452             cls : ''
7453         };
7454         if (this.parent().xtype.match(/^Nav/)) {
7455             cfg.cls = 'navbar-form navbar-' + this.align;
7456             
7457         }
7458         
7459         if (this.labelAlign == 'left' ) {
7460             cfg.cls += ' form-horizontal';
7461         }
7462         
7463         
7464         return cfg;
7465     },
7466     initEvents : function()
7467     {
7468         this.el.on('submit', this.onSubmit, this);
7469         // this was added as random key presses on the form where triggering form submit.
7470         this.el.on('keypress', function(e) {
7471             if (e.getCharCode() != 13) {
7472                 return true;
7473             }
7474             // we might need to allow it for textareas.. and some other items.
7475             // check e.getTarget().
7476             
7477             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7478                 return true;
7479             }
7480         
7481             Roo.log("keypress blocked");
7482             
7483             e.preventDefault();
7484             return false;
7485         });
7486         
7487     },
7488     // private
7489     onSubmit : function(e){
7490         e.stopEvent();
7491     },
7492     
7493      /**
7494      * Returns true if client-side validation on the form is successful.
7495      * @return Boolean
7496      */
7497     isValid : function(){
7498         var items = this.getItems();
7499         var valid = true;
7500         items.each(function(f){
7501            if(!f.validate()){
7502                valid = false;
7503                
7504            }
7505         });
7506         return valid;
7507     },
7508     /**
7509      * Returns true if any fields in this form have changed since their original load.
7510      * @return Boolean
7511      */
7512     isDirty : function(){
7513         var dirty = false;
7514         var items = this.getItems();
7515         items.each(function(f){
7516            if(f.isDirty()){
7517                dirty = true;
7518                return false;
7519            }
7520            return true;
7521         });
7522         return dirty;
7523     },
7524      /**
7525      * Performs a predefined action (submit or load) or custom actions you define on this form.
7526      * @param {String} actionName The name of the action type
7527      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7528      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7529      * accept other config options):
7530      * <pre>
7531 Property          Type             Description
7532 ----------------  ---------------  ----------------------------------------------------------------------------------
7533 url               String           The url for the action (defaults to the form's url)
7534 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7535 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7536 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7537                                    validate the form on the client (defaults to false)
7538      * </pre>
7539      * @return {BasicForm} this
7540      */
7541     doAction : function(action, options){
7542         if(typeof action == 'string'){
7543             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7544         }
7545         if(this.fireEvent('beforeaction', this, action) !== false){
7546             this.beforeAction(action);
7547             action.run.defer(100, action);
7548         }
7549         return this;
7550     },
7551     
7552     // private
7553     beforeAction : function(action){
7554         var o = action.options;
7555         
7556         if(this.loadMask){
7557             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7558         }
7559         // not really supported yet.. ??
7560         
7561         //if(this.waitMsgTarget === true){
7562         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7563         //}else if(this.waitMsgTarget){
7564         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7565         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7566         //}else {
7567         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7568        // }
7569          
7570     },
7571
7572     // private
7573     afterAction : function(action, success){
7574         this.activeAction = null;
7575         var o = action.options;
7576         
7577         //if(this.waitMsgTarget === true){
7578             this.el.unmask();
7579         //}else if(this.waitMsgTarget){
7580         //    this.waitMsgTarget.unmask();
7581         //}else{
7582         //    Roo.MessageBox.updateProgress(1);
7583         //    Roo.MessageBox.hide();
7584        // }
7585         // 
7586         if(success){
7587             if(o.reset){
7588                 this.reset();
7589             }
7590             Roo.callback(o.success, o.scope, [this, action]);
7591             this.fireEvent('actioncomplete', this, action);
7592             
7593         }else{
7594             
7595             // failure condition..
7596             // we have a scenario where updates need confirming.
7597             // eg. if a locking scenario exists..
7598             // we look for { errors : { needs_confirm : true }} in the response.
7599             if (
7600                 (typeof(action.result) != 'undefined')  &&
7601                 (typeof(action.result.errors) != 'undefined')  &&
7602                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7603            ){
7604                 var _t = this;
7605                 Roo.log("not supported yet");
7606                  /*
7607                 
7608                 Roo.MessageBox.confirm(
7609                     "Change requires confirmation",
7610                     action.result.errorMsg,
7611                     function(r) {
7612                         if (r != 'yes') {
7613                             return;
7614                         }
7615                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7616                     }
7617                     
7618                 );
7619                 */
7620                 
7621                 
7622                 return;
7623             }
7624             
7625             Roo.callback(o.failure, o.scope, [this, action]);
7626             // show an error message if no failed handler is set..
7627             if (!this.hasListener('actionfailed')) {
7628                 Roo.log("need to add dialog support");
7629                 /*
7630                 Roo.MessageBox.alert("Error",
7631                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7632                         action.result.errorMsg :
7633                         "Saving Failed, please check your entries or try again"
7634                 );
7635                 */
7636             }
7637             
7638             this.fireEvent('actionfailed', this, action);
7639         }
7640         
7641     },
7642     /**
7643      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7644      * @param {String} id The value to search for
7645      * @return Field
7646      */
7647     findField : function(id){
7648         var items = this.getItems();
7649         var field = items.get(id);
7650         if(!field){
7651              items.each(function(f){
7652                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7653                     field = f;
7654                     return false;
7655                 }
7656                 return true;
7657             });
7658         }
7659         return field || null;
7660     },
7661      /**
7662      * Mark fields in this form invalid in bulk.
7663      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7664      * @return {BasicForm} this
7665      */
7666     markInvalid : function(errors){
7667         if(errors instanceof Array){
7668             for(var i = 0, len = errors.length; i < len; i++){
7669                 var fieldError = errors[i];
7670                 var f = this.findField(fieldError.id);
7671                 if(f){
7672                     f.markInvalid(fieldError.msg);
7673                 }
7674             }
7675         }else{
7676             var field, id;
7677             for(id in errors){
7678                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7679                     field.markInvalid(errors[id]);
7680                 }
7681             }
7682         }
7683         //Roo.each(this.childForms || [], function (f) {
7684         //    f.markInvalid(errors);
7685         //});
7686         
7687         return this;
7688     },
7689
7690     /**
7691      * Set values for fields in this form in bulk.
7692      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7693      * @return {BasicForm} this
7694      */
7695     setValues : function(values){
7696         if(values instanceof Array){ // array of objects
7697             for(var i = 0, len = values.length; i < len; i++){
7698                 var v = values[i];
7699                 var f = this.findField(v.id);
7700                 if(f){
7701                     f.setValue(v.value);
7702                     if(this.trackResetOnLoad){
7703                         f.originalValue = f.getValue();
7704                     }
7705                 }
7706             }
7707         }else{ // object hash
7708             var field, id;
7709             for(id in values){
7710                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7711                     
7712                     if (field.setFromData && 
7713                         field.valueField && 
7714                         field.displayField &&
7715                         // combos' with local stores can 
7716                         // be queried via setValue()
7717                         // to set their value..
7718                         (field.store && !field.store.isLocal)
7719                         ) {
7720                         // it's a combo
7721                         var sd = { };
7722                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7723                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7724                         field.setFromData(sd);
7725                         
7726                     } else {
7727                         field.setValue(values[id]);
7728                     }
7729                     
7730                     
7731                     if(this.trackResetOnLoad){
7732                         field.originalValue = field.getValue();
7733                     }
7734                 }
7735             }
7736         }
7737          
7738         //Roo.each(this.childForms || [], function (f) {
7739         //    f.setValues(values);
7740         //});
7741                 
7742         return this;
7743     },
7744
7745     /**
7746      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7747      * they are returned as an array.
7748      * @param {Boolean} asString
7749      * @return {Object}
7750      */
7751     getValues : function(asString){
7752         //if (this.childForms) {
7753             // copy values from the child forms
7754         //    Roo.each(this.childForms, function (f) {
7755         //        this.setValues(f.getValues());
7756         //    }, this);
7757         //}
7758         
7759         
7760         
7761         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7762         if(asString === true){
7763             return fs;
7764         }
7765         return Roo.urlDecode(fs);
7766     },
7767     
7768     /**
7769      * Returns the fields in this form as an object with key/value pairs. 
7770      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7771      * @return {Object}
7772      */
7773     getFieldValues : function(with_hidden)
7774     {
7775         var items = this.getItems();
7776         var ret = {};
7777         items.each(function(f){
7778             if (!f.getName()) {
7779                 return;
7780             }
7781             var v = f.getValue();
7782             if (f.inputType =='radio') {
7783                 if (typeof(ret[f.getName()]) == 'undefined') {
7784                     ret[f.getName()] = ''; // empty..
7785                 }
7786                 
7787                 if (!f.el.dom.checked) {
7788                     return;
7789                     
7790                 }
7791                 v = f.el.dom.value;
7792                 
7793             }
7794             
7795             // not sure if this supported any more..
7796             if ((typeof(v) == 'object') && f.getRawValue) {
7797                 v = f.getRawValue() ; // dates..
7798             }
7799             // combo boxes where name != hiddenName...
7800             if (f.name != f.getName()) {
7801                 ret[f.name] = f.getRawValue();
7802             }
7803             ret[f.getName()] = v;
7804         });
7805         
7806         return ret;
7807     },
7808
7809     /**
7810      * Clears all invalid messages in this form.
7811      * @return {BasicForm} this
7812      */
7813     clearInvalid : function(){
7814         var items = this.getItems();
7815         
7816         items.each(function(f){
7817            f.clearInvalid();
7818         });
7819         
7820         
7821         
7822         return this;
7823     },
7824
7825     /**
7826      * Resets this form.
7827      * @return {BasicForm} this
7828      */
7829     reset : function(){
7830         var items = this.getItems();
7831         items.each(function(f){
7832             f.reset();
7833         });
7834         
7835         Roo.each(this.childForms || [], function (f) {
7836             f.reset();
7837         });
7838        
7839         
7840         return this;
7841     },
7842     getItems : function()
7843     {
7844         var r=new Roo.util.MixedCollection(false, function(o){
7845             return o.id || (o.id = Roo.id());
7846         });
7847         var iter = function(el) {
7848             if (el.inputEl) {
7849                 r.add(el);
7850             }
7851             if (!el.items) {
7852                 return;
7853             }
7854             Roo.each(el.items,function(e) {
7855                 iter(e);
7856             });
7857             
7858             
7859         };
7860         
7861         iter(this);
7862         return r;
7863         
7864         
7865         
7866         
7867     }
7868     
7869 });
7870
7871  
7872 /*
7873  * Based on:
7874  * Ext JS Library 1.1.1
7875  * Copyright(c) 2006-2007, Ext JS, LLC.
7876  *
7877  * Originally Released Under LGPL - original licence link has changed is not relivant.
7878  *
7879  * Fork - LGPL
7880  * <script type="text/javascript">
7881  */
7882 /**
7883  * @class Roo.form.VTypes
7884  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7885  * @singleton
7886  */
7887 Roo.form.VTypes = function(){
7888     // closure these in so they are only created once.
7889     var alpha = /^[a-zA-Z_]+$/;
7890     var alphanum = /^[a-zA-Z0-9_]+$/;
7891     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7892     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7893
7894     // All these messages and functions are configurable
7895     return {
7896         /**
7897          * The function used to validate email addresses
7898          * @param {String} value The email address
7899          */
7900         'email' : function(v){
7901             return email.test(v);
7902         },
7903         /**
7904          * The error text to display when the email validation function returns false
7905          * @type String
7906          */
7907         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7908         /**
7909          * The keystroke filter mask to be applied on email input
7910          * @type RegExp
7911          */
7912         'emailMask' : /[a-z0-9_\.\-@]/i,
7913
7914         /**
7915          * The function used to validate URLs
7916          * @param {String} value The URL
7917          */
7918         'url' : function(v){
7919             return url.test(v);
7920         },
7921         /**
7922          * The error text to display when the url validation function returns false
7923          * @type String
7924          */
7925         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7926         
7927         /**
7928          * The function used to validate alpha values
7929          * @param {String} value The value
7930          */
7931         'alpha' : function(v){
7932             return alpha.test(v);
7933         },
7934         /**
7935          * The error text to display when the alpha validation function returns false
7936          * @type String
7937          */
7938         'alphaText' : 'This field should only contain letters and _',
7939         /**
7940          * The keystroke filter mask to be applied on alpha input
7941          * @type RegExp
7942          */
7943         'alphaMask' : /[a-z_]/i,
7944
7945         /**
7946          * The function used to validate alphanumeric values
7947          * @param {String} value The value
7948          */
7949         'alphanum' : function(v){
7950             return alphanum.test(v);
7951         },
7952         /**
7953          * The error text to display when the alphanumeric validation function returns false
7954          * @type String
7955          */
7956         'alphanumText' : 'This field should only contain letters, numbers and _',
7957         /**
7958          * The keystroke filter mask to be applied on alphanumeric input
7959          * @type RegExp
7960          */
7961         'alphanumMask' : /[a-z0-9_]/i
7962     };
7963 }();/*
7964  * - LGPL
7965  *
7966  * Input
7967  * 
7968  */
7969
7970 /**
7971  * @class Roo.bootstrap.Input
7972  * @extends Roo.bootstrap.Component
7973  * Bootstrap Input class
7974  * @cfg {Boolean} disabled is it disabled
7975  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7976  * @cfg {String} name name of the input
7977  * @cfg {string} fieldLabel - the label associated
7978  * @cfg {string} placeholder - placeholder to put in text.
7979  * @cfg {string}  before - input group add on before
7980  * @cfg {string} after - input group add on after
7981  * @cfg {string} size - (lg|sm) or leave empty..
7982  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7983  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7984  * @cfg {Number} md colspan out of 12 for computer-sized screens
7985  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7986  * @cfg {string} value default value of the input
7987  * @cfg {Number} labelWidth set the width of label (0-12)
7988  * @cfg {String} labelAlign (top|left)
7989  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7990  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7991
7992  * @cfg {String} align (left|center|right) Default left
7993  * @cfg {Boolean} forceFeedback (true|false) Default false
7994  * 
7995  * 
7996  * 
7997  * 
7998  * @constructor
7999  * Create a new Input
8000  * @param {Object} config The config object
8001  */
8002
8003 Roo.bootstrap.Input = function(config){
8004     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8005    
8006         this.addEvents({
8007             /**
8008              * @event focus
8009              * Fires when this field receives input focus.
8010              * @param {Roo.form.Field} this
8011              */
8012             focus : true,
8013             /**
8014              * @event blur
8015              * Fires when this field loses input focus.
8016              * @param {Roo.form.Field} this
8017              */
8018             blur : true,
8019             /**
8020              * @event specialkey
8021              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8022              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8023              * @param {Roo.form.Field} this
8024              * @param {Roo.EventObject} e The event object
8025              */
8026             specialkey : true,
8027             /**
8028              * @event change
8029              * Fires just before the field blurs if the field value has changed.
8030              * @param {Roo.form.Field} this
8031              * @param {Mixed} newValue The new value
8032              * @param {Mixed} oldValue The original value
8033              */
8034             change : true,
8035             /**
8036              * @event invalid
8037              * Fires after the field has been marked as invalid.
8038              * @param {Roo.form.Field} this
8039              * @param {String} msg The validation message
8040              */
8041             invalid : true,
8042             /**
8043              * @event valid
8044              * Fires after the field has been validated with no errors.
8045              * @param {Roo.form.Field} this
8046              */
8047             valid : true,
8048              /**
8049              * @event keyup
8050              * Fires after the key up
8051              * @param {Roo.form.Field} this
8052              * @param {Roo.EventObject}  e The event Object
8053              */
8054             keyup : true
8055         });
8056 };
8057
8058 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8059      /**
8060      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8061       automatic validation (defaults to "keyup").
8062      */
8063     validationEvent : "keyup",
8064      /**
8065      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8066      */
8067     validateOnBlur : true,
8068     /**
8069      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8070      */
8071     validationDelay : 250,
8072      /**
8073      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8074      */
8075     focusClass : "x-form-focus",  // not needed???
8076     
8077        
8078     /**
8079      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8080      */
8081     invalidClass : "has-warning",
8082     
8083     /**
8084      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8085      */
8086     validClass : "has-success",
8087     
8088     /**
8089      * @cfg {Boolean} hasFeedback (true|false) default true
8090      */
8091     hasFeedback : true,
8092     
8093     /**
8094      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8095      */
8096     invalidFeedbackClass : "glyphicon-warning-sign",
8097     
8098     /**
8099      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8100      */
8101     validFeedbackClass : "glyphicon-ok",
8102     
8103     /**
8104      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8105      */
8106     selectOnFocus : false,
8107     
8108      /**
8109      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8110      */
8111     maskRe : null,
8112        /**
8113      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8114      */
8115     vtype : null,
8116     
8117       /**
8118      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8119      */
8120     disableKeyFilter : false,
8121     
8122        /**
8123      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8124      */
8125     disabled : false,
8126      /**
8127      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8128      */
8129     allowBlank : true,
8130     /**
8131      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8132      */
8133     blankText : "This field is required",
8134     
8135      /**
8136      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8137      */
8138     minLength : 0,
8139     /**
8140      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8141      */
8142     maxLength : Number.MAX_VALUE,
8143     /**
8144      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8145      */
8146     minLengthText : "The minimum length for this field is {0}",
8147     /**
8148      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8149      */
8150     maxLengthText : "The maximum length for this field is {0}",
8151   
8152     
8153     /**
8154      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8155      * If available, this function will be called only after the basic validators all return true, and will be passed the
8156      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8157      */
8158     validator : null,
8159     /**
8160      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8161      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8162      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8163      */
8164     regex : null,
8165     /**
8166      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8167      */
8168     regexText : "",
8169     
8170     autocomplete: false,
8171     
8172     
8173     fieldLabel : '',
8174     inputType : 'text',
8175     
8176     name : false,
8177     placeholder: false,
8178     before : false,
8179     after : false,
8180     size : false,
8181     hasFocus : false,
8182     preventMark: false,
8183     isFormField : true,
8184     value : '',
8185     labelWidth : 2,
8186     labelAlign : false,
8187     readOnly : false,
8188     align : false,
8189     formatedValue : false,
8190     forceFeedback : false,
8191     
8192     parentLabelAlign : function()
8193     {
8194         var parent = this;
8195         while (parent.parent()) {
8196             parent = parent.parent();
8197             if (typeof(parent.labelAlign) !='undefined') {
8198                 return parent.labelAlign;
8199             }
8200         }
8201         return 'left';
8202         
8203     },
8204     
8205     getAutoCreate : function(){
8206         
8207         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8208         
8209         var id = Roo.id();
8210         
8211         var cfg = {};
8212         
8213        
8214         
8215         if(this.inputType != 'hidden'){
8216             cfg.cls = 'form-group' //input-group
8217         }
8218         
8219         var input =  {
8220             tag: 'input',
8221             id : id,
8222             type : this.inputType,
8223             value : this.value,
8224             cls : 'form-control',
8225             placeholder : this.placeholder || '',
8226             autocomplete : this.autocomplete || 'new-password'
8227         };
8228         
8229         
8230         if(this.align){
8231             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8232         }
8233         
8234         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8235             input.maxLength = this.maxLength;
8236         }
8237         
8238         if (this.disabled) {
8239             input.disabled=true;
8240         }
8241         
8242         if (this.readOnly) {
8243             input.readonly=true;
8244         }
8245         
8246         if (this.name) {
8247             input.name = this.name;
8248         }
8249         if (this.size) {
8250             input.cls += ' input-' + this.size;
8251         }
8252         var settings=this;
8253         ['xs','sm','md','lg'].map(function(size){
8254             if (settings[size]) {
8255                 cfg.cls += ' col-' + size + '-' + settings[size];
8256             }
8257         });
8258         
8259         var inputblock = input;
8260         
8261         var feedback = {
8262             tag: 'span',
8263             cls: 'glyphicon form-control-feedback'
8264         };
8265             
8266         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8267             
8268             inputblock = {
8269                 cls : 'has-feedback',
8270                 cn :  [
8271                     input,
8272                     feedback
8273                 ] 
8274             };  
8275         }
8276         
8277         if (this.before || this.after) {
8278             
8279             inputblock = {
8280                 cls : 'input-group',
8281                 cn :  [] 
8282             };
8283             
8284             if (this.before && typeof(this.before) == 'string') {
8285                 
8286                 inputblock.cn.push({
8287                     tag :'span',
8288                     cls : 'roo-input-before input-group-addon',
8289                     html : this.before
8290                 });
8291             }
8292             if (this.before && typeof(this.before) == 'object') {
8293                 this.before = Roo.factory(this.before);
8294                 
8295                 inputblock.cn.push({
8296                     tag :'span',
8297                     cls : 'roo-input-before input-group-' +
8298                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8299                 });
8300             }
8301             
8302             inputblock.cn.push(input);
8303             
8304             if (this.after && typeof(this.after) == 'string') {
8305                 inputblock.cn.push({
8306                     tag :'span',
8307                     cls : 'roo-input-after input-group-addon',
8308                     html : this.after
8309                 });
8310             }
8311             if (this.after && typeof(this.after) == 'object') {
8312                 this.after = Roo.factory(this.after);
8313                 
8314                 inputblock.cn.push({
8315                     tag :'span',
8316                     cls : 'roo-input-after input-group-' +
8317                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8318                 });
8319             }
8320             
8321             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8322                 inputblock.cls += ' has-feedback';
8323                 inputblock.cn.push(feedback);
8324             }
8325         };
8326         
8327         if (align ==='left' && this.fieldLabel.length) {
8328                 
8329                 cfg.cn = [
8330                     
8331                     {
8332                         tag: 'label',
8333                         'for' :  id,
8334                         cls : 'control-label col-sm-' + this.labelWidth,
8335                         html : this.fieldLabel
8336                         
8337                     },
8338                     {
8339                         cls : "col-sm-" + (12 - this.labelWidth), 
8340                         cn: [
8341                             inputblock
8342                         ]
8343                     }
8344                     
8345                 ];
8346         } else if ( this.fieldLabel.length) {
8347                 
8348                  cfg.cn = [
8349                    
8350                     {
8351                         tag: 'label',
8352                         //cls : 'input-group-addon',
8353                         html : this.fieldLabel
8354                         
8355                     },
8356                     
8357                     inputblock
8358                     
8359                 ];
8360
8361         } else {
8362             
8363                 cfg.cn = [
8364                     
8365                         inputblock
8366                     
8367                 ];
8368                 
8369                 
8370         };
8371         
8372         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8373            cfg.cls += ' navbar-form';
8374         }
8375         if (this.parentType === 'NavGroup') {
8376            cfg.cls += ' navbar-form';
8377            cfg.tag = 'li';
8378         }
8379         return cfg;
8380         
8381     },
8382     /**
8383      * return the real input element.
8384      */
8385     inputEl: function ()
8386     {
8387         return this.el.select('input.form-control',true).first();
8388     },
8389     
8390     tooltipEl : function()
8391     {
8392         return this.inputEl();
8393     },
8394     
8395     setDisabled : function(v)
8396     {
8397         var i  = this.inputEl().dom;
8398         if (!v) {
8399             i.removeAttribute('disabled');
8400             return;
8401             
8402         }
8403         i.setAttribute('disabled','true');
8404     },
8405     initEvents : function()
8406     {
8407           
8408         this.inputEl().on("keydown" , this.fireKey,  this);
8409         this.inputEl().on("focus", this.onFocus,  this);
8410         this.inputEl().on("blur", this.onBlur,  this);
8411         
8412         this.inputEl().relayEvent('keyup', this);
8413  
8414         // reference to original value for reset
8415         this.originalValue = this.getValue();
8416         //Roo.form.TextField.superclass.initEvents.call(this);
8417         if(this.validationEvent == 'keyup'){
8418             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8419             this.inputEl().on('keyup', this.filterValidation, this);
8420         }
8421         else if(this.validationEvent !== false){
8422             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8423         }
8424         
8425         if(this.selectOnFocus){
8426             this.on("focus", this.preFocus, this);
8427             
8428         }
8429         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8430             this.inputEl().on("keypress", this.filterKeys, this);
8431         }
8432        /* if(this.grow){
8433             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8434             this.el.on("click", this.autoSize,  this);
8435         }
8436         */
8437         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8438             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8439         }
8440         
8441         if (typeof(this.before) == 'object') {
8442             this.before.render(this.el.select('.roo-input-before',true).first());
8443         }
8444         if (typeof(this.after) == 'object') {
8445             this.after.render(this.el.select('.roo-input-after',true).first());
8446         }
8447         
8448         
8449     },
8450     filterValidation : function(e){
8451         if(!e.isNavKeyPress()){
8452             this.validationTask.delay(this.validationDelay);
8453         }
8454     },
8455      /**
8456      * Validates the field value
8457      * @return {Boolean} True if the value is valid, else false
8458      */
8459     validate : function(){
8460         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8461         if(this.disabled || this.validateValue(this.getRawValue())){
8462             this.markValid();
8463             return true;
8464         }
8465         
8466         this.markInvalid();
8467         return false;
8468     },
8469     
8470     
8471     /**
8472      * Validates a value according to the field's validation rules and marks the field as invalid
8473      * if the validation fails
8474      * @param {Mixed} value The value to validate
8475      * @return {Boolean} True if the value is valid, else false
8476      */
8477     validateValue : function(value){
8478         if(value.length < 1)  { // if it's blank
8479             if(this.allowBlank){
8480                 return true;
8481             }
8482             return false;
8483         }
8484         
8485         if(value.length < this.minLength){
8486             return false;
8487         }
8488         if(value.length > this.maxLength){
8489             return false;
8490         }
8491         if(this.vtype){
8492             var vt = Roo.form.VTypes;
8493             if(!vt[this.vtype](value, this)){
8494                 return false;
8495             }
8496         }
8497         if(typeof this.validator == "function"){
8498             var msg = this.validator(value);
8499             if(msg !== true){
8500                 return false;
8501             }
8502         }
8503         
8504         if(this.regex && !this.regex.test(value)){
8505             return false;
8506         }
8507         
8508         return true;
8509     },
8510
8511     
8512     
8513      // private
8514     fireKey : function(e){
8515         //Roo.log('field ' + e.getKey());
8516         if(e.isNavKeyPress()){
8517             this.fireEvent("specialkey", this, e);
8518         }
8519     },
8520     focus : function (selectText){
8521         if(this.rendered){
8522             this.inputEl().focus();
8523             if(selectText === true){
8524                 this.inputEl().dom.select();
8525             }
8526         }
8527         return this;
8528     } ,
8529     
8530     onFocus : function(){
8531         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8532            // this.el.addClass(this.focusClass);
8533         }
8534         if(!this.hasFocus){
8535             this.hasFocus = true;
8536             this.startValue = this.getValue();
8537             this.fireEvent("focus", this);
8538         }
8539     },
8540     
8541     beforeBlur : Roo.emptyFn,
8542
8543     
8544     // private
8545     onBlur : function(){
8546         this.beforeBlur();
8547         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8548             //this.el.removeClass(this.focusClass);
8549         }
8550         this.hasFocus = false;
8551         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8552             this.validate();
8553         }
8554         var v = this.getValue();
8555         if(String(v) !== String(this.startValue)){
8556             this.fireEvent('change', this, v, this.startValue);
8557         }
8558         this.fireEvent("blur", this);
8559     },
8560     
8561     /**
8562      * Resets the current field value to the originally loaded value and clears any validation messages
8563      */
8564     reset : function(){
8565         this.setValue(this.originalValue);
8566         this.validate();
8567     },
8568      /**
8569      * Returns the name of the field
8570      * @return {Mixed} name The name field
8571      */
8572     getName: function(){
8573         return this.name;
8574     },
8575      /**
8576      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8577      * @return {Mixed} value The field value
8578      */
8579     getValue : function(){
8580         
8581         var v = this.inputEl().getValue();
8582         
8583         return v;
8584     },
8585     /**
8586      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8587      * @return {Mixed} value The field value
8588      */
8589     getRawValue : function(){
8590         var v = this.inputEl().getValue();
8591         
8592         return v;
8593     },
8594     
8595     /**
8596      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8597      * @param {Mixed} value The value to set
8598      */
8599     setRawValue : function(v){
8600         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8601     },
8602     
8603     selectText : function(start, end){
8604         var v = this.getRawValue();
8605         if(v.length > 0){
8606             start = start === undefined ? 0 : start;
8607             end = end === undefined ? v.length : end;
8608             var d = this.inputEl().dom;
8609             if(d.setSelectionRange){
8610                 d.setSelectionRange(start, end);
8611             }else if(d.createTextRange){
8612                 var range = d.createTextRange();
8613                 range.moveStart("character", start);
8614                 range.moveEnd("character", v.length-end);
8615                 range.select();
8616             }
8617         }
8618     },
8619     
8620     /**
8621      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8622      * @param {Mixed} value The value to set
8623      */
8624     setValue : function(v){
8625         this.value = v;
8626         if(this.rendered){
8627             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8628             this.validate();
8629         }
8630     },
8631     
8632     /*
8633     processValue : function(value){
8634         if(this.stripCharsRe){
8635             var newValue = value.replace(this.stripCharsRe, '');
8636             if(newValue !== value){
8637                 this.setRawValue(newValue);
8638                 return newValue;
8639             }
8640         }
8641         return value;
8642     },
8643   */
8644     preFocus : function(){
8645         
8646         if(this.selectOnFocus){
8647             this.inputEl().dom.select();
8648         }
8649     },
8650     filterKeys : function(e){
8651         var k = e.getKey();
8652         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8653             return;
8654         }
8655         var c = e.getCharCode(), cc = String.fromCharCode(c);
8656         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8657             return;
8658         }
8659         if(!this.maskRe.test(cc)){
8660             e.stopEvent();
8661         }
8662     },
8663      /**
8664      * Clear any invalid styles/messages for this field
8665      */
8666     clearInvalid : function(){
8667         
8668         if(!this.el || this.preventMark){ // not rendered
8669             return;
8670         }
8671         
8672         var label = this.el.select('label', true).first();
8673         var icon = this.el.select('i.fa-star', true).first();
8674         
8675         if(label && icon){
8676             icon.remove();
8677         }
8678         
8679         this.el.removeClass(this.invalidClass);
8680         
8681         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8682             
8683             var feedback = this.el.select('.form-control-feedback', true).first();
8684             
8685             if(feedback){
8686                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8687             }
8688             
8689         }
8690         
8691         this.fireEvent('valid', this);
8692     },
8693     
8694      /**
8695      * Mark this field as valid
8696      */
8697     markValid : function()
8698     {
8699         if(!this.el  || this.preventMark){ // not rendered
8700             return;
8701         }
8702         
8703         this.el.removeClass([this.invalidClass, this.validClass]);
8704         
8705         var feedback = this.el.select('.form-control-feedback', true).first();
8706             
8707         if(feedback){
8708             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8709         }
8710
8711         if(this.disabled || this.allowBlank){
8712             return;
8713         }
8714         
8715         var formGroup = this.el.findParent('.form-group', false, true);
8716         
8717         if(formGroup){
8718             
8719             var label = formGroup.select('label', true).first();
8720             var icon = formGroup.select('i.fa-star', true).first();
8721             
8722             if(label && icon){
8723                 icon.remove();
8724             }
8725         }
8726         
8727         this.el.addClass(this.validClass);
8728         
8729         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8730             
8731             var feedback = this.el.select('.form-control-feedback', true).first();
8732             
8733             if(feedback){
8734                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8735                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8736             }
8737             
8738         }
8739         
8740         this.fireEvent('valid', this);
8741     },
8742     
8743      /**
8744      * Mark this field as invalid
8745      * @param {String} msg The validation message
8746      */
8747     markInvalid : function(msg)
8748     {
8749         if(!this.el  || this.preventMark){ // not rendered
8750             return;
8751         }
8752         
8753         this.el.removeClass([this.invalidClass, this.validClass]);
8754         
8755         var feedback = this.el.select('.form-control-feedback', true).first();
8756             
8757         if(feedback){
8758             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8759         }
8760
8761         if(this.disabled || this.allowBlank){
8762             return;
8763         }
8764         
8765         var formGroup = this.el.findParent('.form-group', false, true);
8766         
8767         if(formGroup){
8768             var label = formGroup.select('label', true).first();
8769             var icon = formGroup.select('i.fa-star', true).first();
8770
8771             if(!this.getValue().length && label && !icon){
8772                 this.el.findParent('.form-group', false, true).createChild({
8773                     tag : 'i',
8774                     cls : 'text-danger fa fa-lg fa-star',
8775                     tooltip : 'This field is required',
8776                     style : 'margin-right:5px;'
8777                 }, label, true);
8778             }
8779         }
8780         
8781         
8782         this.el.addClass(this.invalidClass);
8783         
8784         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8785             
8786             var feedback = this.el.select('.form-control-feedback', true).first();
8787             
8788             if(feedback){
8789                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8790                 
8791                 if(this.getValue().length || this.forceFeedback){
8792                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8793                 }
8794                 
8795             }
8796             
8797         }
8798         
8799         this.fireEvent('invalid', this, msg);
8800     },
8801     // private
8802     SafariOnKeyDown : function(event)
8803     {
8804         // this is a workaround for a password hang bug on chrome/ webkit.
8805         
8806         var isSelectAll = false;
8807         
8808         if(this.inputEl().dom.selectionEnd > 0){
8809             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8810         }
8811         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8812             event.preventDefault();
8813             this.setValue('');
8814             return;
8815         }
8816         
8817         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8818             
8819             event.preventDefault();
8820             // this is very hacky as keydown always get's upper case.
8821             //
8822             var cc = String.fromCharCode(event.getCharCode());
8823             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8824             
8825         }
8826     },
8827     adjustWidth : function(tag, w){
8828         tag = tag.toLowerCase();
8829         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8830             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8831                 if(tag == 'input'){
8832                     return w + 2;
8833                 }
8834                 if(tag == 'textarea'){
8835                     return w-2;
8836                 }
8837             }else if(Roo.isOpera){
8838                 if(tag == 'input'){
8839                     return w + 2;
8840                 }
8841                 if(tag == 'textarea'){
8842                     return w-2;
8843                 }
8844             }
8845         }
8846         return w;
8847     }
8848     
8849 });
8850
8851  
8852 /*
8853  * - LGPL
8854  *
8855  * Input
8856  * 
8857  */
8858
8859 /**
8860  * @class Roo.bootstrap.TextArea
8861  * @extends Roo.bootstrap.Input
8862  * Bootstrap TextArea class
8863  * @cfg {Number} cols Specifies the visible width of a text area
8864  * @cfg {Number} rows Specifies the visible number of lines in a text area
8865  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8866  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8867  * @cfg {string} html text
8868  * 
8869  * @constructor
8870  * Create a new TextArea
8871  * @param {Object} config The config object
8872  */
8873
8874 Roo.bootstrap.TextArea = function(config){
8875     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8876    
8877 };
8878
8879 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8880      
8881     cols : false,
8882     rows : 5,
8883     readOnly : false,
8884     warp : 'soft',
8885     resize : false,
8886     value: false,
8887     html: false,
8888     
8889     getAutoCreate : function(){
8890         
8891         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8892         
8893         var id = Roo.id();
8894         
8895         var cfg = {};
8896         
8897         var input =  {
8898             tag: 'textarea',
8899             id : id,
8900             warp : this.warp,
8901             rows : this.rows,
8902             value : this.value || '',
8903             html: this.html || '',
8904             cls : 'form-control',
8905             placeholder : this.placeholder || '' 
8906             
8907         };
8908         
8909         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8910             input.maxLength = this.maxLength;
8911         }
8912         
8913         if(this.resize){
8914             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8915         }
8916         
8917         if(this.cols){
8918             input.cols = this.cols;
8919         }
8920         
8921         if (this.readOnly) {
8922             input.readonly = true;
8923         }
8924         
8925         if (this.name) {
8926             input.name = this.name;
8927         }
8928         
8929         if (this.size) {
8930             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8931         }
8932         
8933         var settings=this;
8934         ['xs','sm','md','lg'].map(function(size){
8935             if (settings[size]) {
8936                 cfg.cls += ' col-' + size + '-' + settings[size];
8937             }
8938         });
8939         
8940         var inputblock = input;
8941         
8942         if(this.hasFeedback && !this.allowBlank){
8943             
8944             var feedback = {
8945                 tag: 'span',
8946                 cls: 'glyphicon form-control-feedback'
8947             };
8948
8949             inputblock = {
8950                 cls : 'has-feedback',
8951                 cn :  [
8952                     input,
8953                     feedback
8954                 ] 
8955             };  
8956         }
8957         
8958         
8959         if (this.before || this.after) {
8960             
8961             inputblock = {
8962                 cls : 'input-group',
8963                 cn :  [] 
8964             };
8965             if (this.before) {
8966                 inputblock.cn.push({
8967                     tag :'span',
8968                     cls : 'input-group-addon',
8969                     html : this.before
8970                 });
8971             }
8972             
8973             inputblock.cn.push(input);
8974             
8975             if(this.hasFeedback && !this.allowBlank){
8976                 inputblock.cls += ' has-feedback';
8977                 inputblock.cn.push(feedback);
8978             }
8979             
8980             if (this.after) {
8981                 inputblock.cn.push({
8982                     tag :'span',
8983                     cls : 'input-group-addon',
8984                     html : this.after
8985                 });
8986             }
8987             
8988         }
8989         
8990         if (align ==='left' && this.fieldLabel.length) {
8991 //                Roo.log("left and has label");
8992                 cfg.cn = [
8993                     
8994                     {
8995                         tag: 'label',
8996                         'for' :  id,
8997                         cls : 'control-label col-sm-' + this.labelWidth,
8998                         html : this.fieldLabel
8999                         
9000                     },
9001                     {
9002                         cls : "col-sm-" + (12 - this.labelWidth), 
9003                         cn: [
9004                             inputblock
9005                         ]
9006                     }
9007                     
9008                 ];
9009         } else if ( this.fieldLabel.length) {
9010 //                Roo.log(" label");
9011                  cfg.cn = [
9012                    
9013                     {
9014                         tag: 'label',
9015                         //cls : 'input-group-addon',
9016                         html : this.fieldLabel
9017                         
9018                     },
9019                     
9020                     inputblock
9021                     
9022                 ];
9023
9024         } else {
9025             
9026 //                   Roo.log(" no label && no align");
9027                 cfg.cn = [
9028                     
9029                         inputblock
9030                     
9031                 ];
9032                 
9033                 
9034         }
9035         
9036         if (this.disabled) {
9037             input.disabled=true;
9038         }
9039         
9040         return cfg;
9041         
9042     },
9043     /**
9044      * return the real textarea element.
9045      */
9046     inputEl: function ()
9047     {
9048         return this.el.select('textarea.form-control',true).first();
9049     },
9050     
9051     /**
9052      * Clear any invalid styles/messages for this field
9053      */
9054     clearInvalid : function()
9055     {
9056         
9057         if(!this.el || this.preventMark){ // not rendered
9058             return;
9059         }
9060         
9061         var label = this.el.select('label', true).first();
9062         var icon = this.el.select('i.fa-star', true).first();
9063         
9064         if(label && icon){
9065             icon.remove();
9066         }
9067         
9068         this.el.removeClass(this.invalidClass);
9069         
9070         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9071             
9072             var feedback = this.el.select('.form-control-feedback', true).first();
9073             
9074             if(feedback){
9075                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9076             }
9077             
9078         }
9079         
9080         this.fireEvent('valid', this);
9081     },
9082     
9083      /**
9084      * Mark this field as valid
9085      */
9086     markValid : function()
9087     {
9088         if(!this.el  || this.preventMark){ // not rendered
9089             return;
9090         }
9091         
9092         this.el.removeClass([this.invalidClass, this.validClass]);
9093         
9094         var feedback = this.el.select('.form-control-feedback', true).first();
9095             
9096         if(feedback){
9097             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9098         }
9099
9100         if(this.disabled || this.allowBlank){
9101             return;
9102         }
9103         
9104         var label = this.el.select('label', true).first();
9105         var icon = this.el.select('i.fa-star', true).first();
9106         
9107         if(label && icon){
9108             icon.remove();
9109         }
9110         
9111         this.el.addClass(this.validClass);
9112         
9113         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9114             
9115             var feedback = this.el.select('.form-control-feedback', true).first();
9116             
9117             if(feedback){
9118                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9119                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9120             }
9121             
9122         }
9123         
9124         this.fireEvent('valid', this);
9125     },
9126     
9127      /**
9128      * Mark this field as invalid
9129      * @param {String} msg The validation message
9130      */
9131     markInvalid : function(msg)
9132     {
9133         if(!this.el  || this.preventMark){ // not rendered
9134             return;
9135         }
9136         
9137         this.el.removeClass([this.invalidClass, this.validClass]);
9138         
9139         var feedback = this.el.select('.form-control-feedback', true).first();
9140             
9141         if(feedback){
9142             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9143         }
9144
9145         if(this.disabled || this.allowBlank){
9146             return;
9147         }
9148         
9149         var label = this.el.select('label', true).first();
9150         var icon = this.el.select('i.fa-star', true).first();
9151         
9152         if(!this.getValue().length && label && !icon){
9153             this.el.createChild({
9154                 tag : 'i',
9155                 cls : 'text-danger fa fa-lg fa-star',
9156                 tooltip : 'This field is required',
9157                 style : 'margin-right:5px;'
9158             }, label, true);
9159         }
9160
9161         this.el.addClass(this.invalidClass);
9162         
9163         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9164             
9165             var feedback = this.el.select('.form-control-feedback', true).first();
9166             
9167             if(feedback){
9168                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9169                 
9170                 if(this.getValue().length || this.forceFeedback){
9171                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9172                 }
9173                 
9174             }
9175             
9176         }
9177         
9178         this.fireEvent('invalid', this, msg);
9179     }
9180 });
9181
9182  
9183 /*
9184  * - LGPL
9185  *
9186  * trigger field - base class for combo..
9187  * 
9188  */
9189  
9190 /**
9191  * @class Roo.bootstrap.TriggerField
9192  * @extends Roo.bootstrap.Input
9193  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9194  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9195  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9196  * for which you can provide a custom implementation.  For example:
9197  * <pre><code>
9198 var trigger = new Roo.bootstrap.TriggerField();
9199 trigger.onTriggerClick = myTriggerFn;
9200 trigger.applyTo('my-field');
9201 </code></pre>
9202  *
9203  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9204  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9205  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9206  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9207  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9208
9209  * @constructor
9210  * Create a new TriggerField.
9211  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9212  * to the base TextField)
9213  */
9214 Roo.bootstrap.TriggerField = function(config){
9215     this.mimicing = false;
9216     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9217 };
9218
9219 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9220     /**
9221      * @cfg {String} triggerClass A CSS class to apply to the trigger
9222      */
9223      /**
9224      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9225      */
9226     hideTrigger:false,
9227
9228     /**
9229      * @cfg {Boolean} removable (true|false) special filter default false
9230      */
9231     removable : false,
9232     
9233     /** @cfg {Boolean} grow @hide */
9234     /** @cfg {Number} growMin @hide */
9235     /** @cfg {Number} growMax @hide */
9236
9237     /**
9238      * @hide 
9239      * @method
9240      */
9241     autoSize: Roo.emptyFn,
9242     // private
9243     monitorTab : true,
9244     // private
9245     deferHeight : true,
9246
9247     
9248     actionMode : 'wrap',
9249     
9250     caret : false,
9251     
9252     
9253     getAutoCreate : function(){
9254        
9255         var align = this.labelAlign || this.parentLabelAlign();
9256         
9257         var id = Roo.id();
9258         
9259         var cfg = {
9260             cls: 'form-group' //input-group
9261         };
9262         
9263         
9264         var input =  {
9265             tag: 'input',
9266             id : id,
9267             type : this.inputType,
9268             cls : 'form-control',
9269             autocomplete: 'new-password',
9270             placeholder : this.placeholder || '' 
9271             
9272         };
9273         if (this.name) {
9274             input.name = this.name;
9275         }
9276         if (this.size) {
9277             input.cls += ' input-' + this.size;
9278         }
9279         
9280         if (this.disabled) {
9281             input.disabled=true;
9282         }
9283         
9284         var inputblock = input;
9285         
9286         if(this.hasFeedback && !this.allowBlank){
9287             
9288             var feedback = {
9289                 tag: 'span',
9290                 cls: 'glyphicon form-control-feedback'
9291             };
9292             
9293             if(this.removable && !this.editable && !this.tickable){
9294                 inputblock = {
9295                     cls : 'has-feedback',
9296                     cn :  [
9297                         inputblock,
9298                         {
9299                             tag: 'button',
9300                             html : 'x',
9301                             cls : 'roo-combo-removable-btn close'
9302                         },
9303                         feedback
9304                     ] 
9305                 };
9306             } else {
9307                 inputblock = {
9308                     cls : 'has-feedback',
9309                     cn :  [
9310                         inputblock,
9311                         feedback
9312                     ] 
9313                 };
9314             }
9315
9316         } else {
9317             if(this.removable && !this.editable && !this.tickable){
9318                 inputblock = {
9319                     cls : 'roo-removable',
9320                     cn :  [
9321                         inputblock,
9322                         {
9323                             tag: 'button',
9324                             html : 'x',
9325                             cls : 'roo-combo-removable-btn close'
9326                         }
9327                     ] 
9328                 };
9329             }
9330         }
9331         
9332         if (this.before || this.after) {
9333             
9334             inputblock = {
9335                 cls : 'input-group',
9336                 cn :  [] 
9337             };
9338             if (this.before) {
9339                 inputblock.cn.push({
9340                     tag :'span',
9341                     cls : 'input-group-addon',
9342                     html : this.before
9343                 });
9344             }
9345             
9346             inputblock.cn.push(input);
9347             
9348             if(this.hasFeedback && !this.allowBlank){
9349                 inputblock.cls += ' has-feedback';
9350                 inputblock.cn.push(feedback);
9351             }
9352             
9353             if (this.after) {
9354                 inputblock.cn.push({
9355                     tag :'span',
9356                     cls : 'input-group-addon',
9357                     html : this.after
9358                 });
9359             }
9360             
9361         };
9362         
9363         var box = {
9364             tag: 'div',
9365             cn: [
9366                 {
9367                     tag: 'input',
9368                     type : 'hidden',
9369                     cls: 'form-hidden-field'
9370                 },
9371                 inputblock
9372             ]
9373             
9374         };
9375         
9376         if(this.multiple){
9377             box = {
9378                 tag: 'div',
9379                 cn: [
9380                     {
9381                         tag: 'input',
9382                         type : 'hidden',
9383                         cls: 'form-hidden-field'
9384                     },
9385                     {
9386                         tag: 'ul',
9387                         cls: 'roo-select2-choices',
9388                         cn:[
9389                             {
9390                                 tag: 'li',
9391                                 cls: 'roo-select2-search-field',
9392                                 cn: [
9393
9394                                     inputblock
9395                                 ]
9396                             }
9397                         ]
9398                     }
9399                 ]
9400             }
9401         };
9402         
9403         var combobox = {
9404             cls: 'roo-select2-container input-group',
9405             cn: [
9406                 box
9407 //                {
9408 //                    tag: 'ul',
9409 //                    cls: 'typeahead typeahead-long dropdown-menu',
9410 //                    style: 'display:none'
9411 //                }
9412             ]
9413         };
9414         
9415         if(!this.multiple && this.showToggleBtn){
9416             
9417             var caret = {
9418                         tag: 'span',
9419                         cls: 'caret'
9420              };
9421             if (this.caret != false) {
9422                 caret = {
9423                      tag: 'i',
9424                      cls: 'fa fa-' + this.caret
9425                 };
9426                 
9427             }
9428             
9429             combobox.cn.push({
9430                 tag :'span',
9431                 cls : 'input-group-addon btn dropdown-toggle',
9432                 cn : [
9433                     caret,
9434                     {
9435                         tag: 'span',
9436                         cls: 'combobox-clear',
9437                         cn  : [
9438                             {
9439                                 tag : 'i',
9440                                 cls: 'icon-remove'
9441                             }
9442                         ]
9443                     }
9444                 ]
9445
9446             })
9447         }
9448         
9449         if(this.multiple){
9450             combobox.cls += ' roo-select2-container-multi';
9451         }
9452         
9453         if (align ==='left' && this.fieldLabel.length) {
9454             
9455 //                Roo.log("left and has label");
9456                 cfg.cn = [
9457                     
9458                     {
9459                         tag: 'label',
9460                         'for' :  id,
9461                         cls : 'control-label col-sm-' + this.labelWidth,
9462                         html : this.fieldLabel
9463                         
9464                     },
9465                     {
9466                         cls : "col-sm-" + (12 - this.labelWidth), 
9467                         cn: [
9468                             combobox
9469                         ]
9470                     }
9471                     
9472                 ];
9473         } else if ( this.fieldLabel.length) {
9474 //                Roo.log(" label");
9475                  cfg.cn = [
9476                    
9477                     {
9478                         tag: 'label',
9479                         //cls : 'input-group-addon',
9480                         html : this.fieldLabel
9481                         
9482                     },
9483                     
9484                     combobox
9485                     
9486                 ];
9487
9488         } else {
9489             
9490 //                Roo.log(" no label && no align");
9491                 cfg = combobox
9492                      
9493                 
9494         }
9495          
9496         var settings=this;
9497         ['xs','sm','md','lg'].map(function(size){
9498             if (settings[size]) {
9499                 cfg.cls += ' col-' + size + '-' + settings[size];
9500             }
9501         });
9502         
9503         return cfg;
9504         
9505     },
9506     
9507     
9508     
9509     // private
9510     onResize : function(w, h){
9511 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9512 //        if(typeof w == 'number'){
9513 //            var x = w - this.trigger.getWidth();
9514 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9515 //            this.trigger.setStyle('left', x+'px');
9516 //        }
9517     },
9518
9519     // private
9520     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9521
9522     // private
9523     getResizeEl : function(){
9524         return this.inputEl();
9525     },
9526
9527     // private
9528     getPositionEl : function(){
9529         return this.inputEl();
9530     },
9531
9532     // private
9533     alignErrorIcon : function(){
9534         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9535     },
9536
9537     // private
9538     initEvents : function(){
9539         
9540         this.createList();
9541         
9542         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9543         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9544         if(!this.multiple && this.showToggleBtn){
9545             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9546             if(this.hideTrigger){
9547                 this.trigger.setDisplayed(false);
9548             }
9549             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9550         }
9551         
9552         if(this.multiple){
9553             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9554         }
9555         
9556         if(this.removable && !this.editable && !this.tickable){
9557             var close = this.closeTriggerEl();
9558             
9559             if(close){
9560                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9561                 close.on('click', this.removeBtnClick, this, close);
9562             }
9563         }
9564         
9565         //this.trigger.addClassOnOver('x-form-trigger-over');
9566         //this.trigger.addClassOnClick('x-form-trigger-click');
9567         
9568         //if(!this.width){
9569         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9570         //}
9571     },
9572     
9573     closeTriggerEl : function()
9574     {
9575         var close = this.el.select('.roo-combo-removable-btn', true).first();
9576         return close ? close : false;
9577     },
9578     
9579     removeBtnClick : function(e, h, el)
9580     {
9581         e.preventDefault();
9582         
9583         if(this.fireEvent("remove", this) !== false){
9584             this.reset();
9585             this.fireEvent("afterremove", this)
9586         }
9587     },
9588     
9589     createList : function()
9590     {
9591         this.list = Roo.get(document.body).createChild({
9592             tag: 'ul',
9593             cls: 'typeahead typeahead-long dropdown-menu',
9594             style: 'display:none'
9595         });
9596         
9597         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9598         
9599     },
9600
9601     // private
9602     initTrigger : function(){
9603        
9604     },
9605
9606     // private
9607     onDestroy : function(){
9608         if(this.trigger){
9609             this.trigger.removeAllListeners();
9610           //  this.trigger.remove();
9611         }
9612         //if(this.wrap){
9613         //    this.wrap.remove();
9614         //}
9615         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9616     },
9617
9618     // private
9619     onFocus : function(){
9620         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9621         /*
9622         if(!this.mimicing){
9623             this.wrap.addClass('x-trigger-wrap-focus');
9624             this.mimicing = true;
9625             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9626             if(this.monitorTab){
9627                 this.el.on("keydown", this.checkTab, this);
9628             }
9629         }
9630         */
9631     },
9632
9633     // private
9634     checkTab : function(e){
9635         if(e.getKey() == e.TAB){
9636             this.triggerBlur();
9637         }
9638     },
9639
9640     // private
9641     onBlur : function(){
9642         // do nothing
9643     },
9644
9645     // private
9646     mimicBlur : function(e, t){
9647         /*
9648         if(!this.wrap.contains(t) && this.validateBlur()){
9649             this.triggerBlur();
9650         }
9651         */
9652     },
9653
9654     // private
9655     triggerBlur : function(){
9656         this.mimicing = false;
9657         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9658         if(this.monitorTab){
9659             this.el.un("keydown", this.checkTab, this);
9660         }
9661         //this.wrap.removeClass('x-trigger-wrap-focus');
9662         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9663     },
9664
9665     // private
9666     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9667     validateBlur : function(e, t){
9668         return true;
9669     },
9670
9671     // private
9672     onDisable : function(){
9673         this.inputEl().dom.disabled = true;
9674         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9675         //if(this.wrap){
9676         //    this.wrap.addClass('x-item-disabled');
9677         //}
9678     },
9679
9680     // private
9681     onEnable : function(){
9682         this.inputEl().dom.disabled = false;
9683         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9684         //if(this.wrap){
9685         //    this.el.removeClass('x-item-disabled');
9686         //}
9687     },
9688
9689     // private
9690     onShow : function(){
9691         var ae = this.getActionEl();
9692         
9693         if(ae){
9694             ae.dom.style.display = '';
9695             ae.dom.style.visibility = 'visible';
9696         }
9697     },
9698
9699     // private
9700     
9701     onHide : function(){
9702         var ae = this.getActionEl();
9703         ae.dom.style.display = 'none';
9704     },
9705
9706     /**
9707      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9708      * by an implementing function.
9709      * @method
9710      * @param {EventObject} e
9711      */
9712     onTriggerClick : Roo.emptyFn
9713 });
9714  /*
9715  * Based on:
9716  * Ext JS Library 1.1.1
9717  * Copyright(c) 2006-2007, Ext JS, LLC.
9718  *
9719  * Originally Released Under LGPL - original licence link has changed is not relivant.
9720  *
9721  * Fork - LGPL
9722  * <script type="text/javascript">
9723  */
9724
9725
9726 /**
9727  * @class Roo.data.SortTypes
9728  * @singleton
9729  * Defines the default sorting (casting?) comparison functions used when sorting data.
9730  */
9731 Roo.data.SortTypes = {
9732     /**
9733      * Default sort that does nothing
9734      * @param {Mixed} s The value being converted
9735      * @return {Mixed} The comparison value
9736      */
9737     none : function(s){
9738         return s;
9739     },
9740     
9741     /**
9742      * The regular expression used to strip tags
9743      * @type {RegExp}
9744      * @property
9745      */
9746     stripTagsRE : /<\/?[^>]+>/gi,
9747     
9748     /**
9749      * Strips all HTML tags to sort on text only
9750      * @param {Mixed} s The value being converted
9751      * @return {String} The comparison value
9752      */
9753     asText : function(s){
9754         return String(s).replace(this.stripTagsRE, "");
9755     },
9756     
9757     /**
9758      * Strips all HTML tags to sort on text only - Case insensitive
9759      * @param {Mixed} s The value being converted
9760      * @return {String} The comparison value
9761      */
9762     asUCText : function(s){
9763         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9764     },
9765     
9766     /**
9767      * Case insensitive string
9768      * @param {Mixed} s The value being converted
9769      * @return {String} The comparison value
9770      */
9771     asUCString : function(s) {
9772         return String(s).toUpperCase();
9773     },
9774     
9775     /**
9776      * Date sorting
9777      * @param {Mixed} s The value being converted
9778      * @return {Number} The comparison value
9779      */
9780     asDate : function(s) {
9781         if(!s){
9782             return 0;
9783         }
9784         if(s instanceof Date){
9785             return s.getTime();
9786         }
9787         return Date.parse(String(s));
9788     },
9789     
9790     /**
9791      * Float sorting
9792      * @param {Mixed} s The value being converted
9793      * @return {Float} The comparison value
9794      */
9795     asFloat : function(s) {
9796         var val = parseFloat(String(s).replace(/,/g, ""));
9797         if(isNaN(val)) {
9798             val = 0;
9799         }
9800         return val;
9801     },
9802     
9803     /**
9804      * Integer sorting
9805      * @param {Mixed} s The value being converted
9806      * @return {Number} The comparison value
9807      */
9808     asInt : function(s) {
9809         var val = parseInt(String(s).replace(/,/g, ""));
9810         if(isNaN(val)) {
9811             val = 0;
9812         }
9813         return val;
9814     }
9815 };/*
9816  * Based on:
9817  * Ext JS Library 1.1.1
9818  * Copyright(c) 2006-2007, Ext JS, LLC.
9819  *
9820  * Originally Released Under LGPL - original licence link has changed is not relivant.
9821  *
9822  * Fork - LGPL
9823  * <script type="text/javascript">
9824  */
9825
9826 /**
9827 * @class Roo.data.Record
9828  * Instances of this class encapsulate both record <em>definition</em> information, and record
9829  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9830  * to access Records cached in an {@link Roo.data.Store} object.<br>
9831  * <p>
9832  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9833  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9834  * objects.<br>
9835  * <p>
9836  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9837  * @constructor
9838  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9839  * {@link #create}. The parameters are the same.
9840  * @param {Array} data An associative Array of data values keyed by the field name.
9841  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9842  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9843  * not specified an integer id is generated.
9844  */
9845 Roo.data.Record = function(data, id){
9846     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9847     this.data = data;
9848 };
9849
9850 /**
9851  * Generate a constructor for a specific record layout.
9852  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9853  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9854  * Each field definition object may contain the following properties: <ul>
9855  * <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,
9856  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9857  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9858  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9859  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9860  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9861  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9862  * this may be omitted.</p></li>
9863  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9864  * <ul><li>auto (Default, implies no conversion)</li>
9865  * <li>string</li>
9866  * <li>int</li>
9867  * <li>float</li>
9868  * <li>boolean</li>
9869  * <li>date</li></ul></p></li>
9870  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9871  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9872  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9873  * by the Reader into an object that will be stored in the Record. It is passed the
9874  * following parameters:<ul>
9875  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9876  * </ul></p></li>
9877  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9878  * </ul>
9879  * <br>usage:<br><pre><code>
9880 var TopicRecord = Roo.data.Record.create(
9881     {name: 'title', mapping: 'topic_title'},
9882     {name: 'author', mapping: 'username'},
9883     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9884     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9885     {name: 'lastPoster', mapping: 'user2'},
9886     {name: 'excerpt', mapping: 'post_text'}
9887 );
9888
9889 var myNewRecord = new TopicRecord({
9890     title: 'Do my job please',
9891     author: 'noobie',
9892     totalPosts: 1,
9893     lastPost: new Date(),
9894     lastPoster: 'Animal',
9895     excerpt: 'No way dude!'
9896 });
9897 myStore.add(myNewRecord);
9898 </code></pre>
9899  * @method create
9900  * @static
9901  */
9902 Roo.data.Record.create = function(o){
9903     var f = function(){
9904         f.superclass.constructor.apply(this, arguments);
9905     };
9906     Roo.extend(f, Roo.data.Record);
9907     var p = f.prototype;
9908     p.fields = new Roo.util.MixedCollection(false, function(field){
9909         return field.name;
9910     });
9911     for(var i = 0, len = o.length; i < len; i++){
9912         p.fields.add(new Roo.data.Field(o[i]));
9913     }
9914     f.getField = function(name){
9915         return p.fields.get(name);  
9916     };
9917     return f;
9918 };
9919
9920 Roo.data.Record.AUTO_ID = 1000;
9921 Roo.data.Record.EDIT = 'edit';
9922 Roo.data.Record.REJECT = 'reject';
9923 Roo.data.Record.COMMIT = 'commit';
9924
9925 Roo.data.Record.prototype = {
9926     /**
9927      * Readonly flag - true if this record has been modified.
9928      * @type Boolean
9929      */
9930     dirty : false,
9931     editing : false,
9932     error: null,
9933     modified: null,
9934
9935     // private
9936     join : function(store){
9937         this.store = store;
9938     },
9939
9940     /**
9941      * Set the named field to the specified value.
9942      * @param {String} name The name of the field to set.
9943      * @param {Object} value The value to set the field to.
9944      */
9945     set : function(name, value){
9946         if(this.data[name] == value){
9947             return;
9948         }
9949         this.dirty = true;
9950         if(!this.modified){
9951             this.modified = {};
9952         }
9953         if(typeof this.modified[name] == 'undefined'){
9954             this.modified[name] = this.data[name];
9955         }
9956         this.data[name] = value;
9957         if(!this.editing && this.store){
9958             this.store.afterEdit(this);
9959         }       
9960     },
9961
9962     /**
9963      * Get the value of the named field.
9964      * @param {String} name The name of the field to get the value of.
9965      * @return {Object} The value of the field.
9966      */
9967     get : function(name){
9968         return this.data[name]; 
9969     },
9970
9971     // private
9972     beginEdit : function(){
9973         this.editing = true;
9974         this.modified = {}; 
9975     },
9976
9977     // private
9978     cancelEdit : function(){
9979         this.editing = false;
9980         delete this.modified;
9981     },
9982
9983     // private
9984     endEdit : function(){
9985         this.editing = false;
9986         if(this.dirty && this.store){
9987             this.store.afterEdit(this);
9988         }
9989     },
9990
9991     /**
9992      * Usually called by the {@link Roo.data.Store} which owns the Record.
9993      * Rejects all changes made to the Record since either creation, or the last commit operation.
9994      * Modified fields are reverted to their original values.
9995      * <p>
9996      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9997      * of reject operations.
9998      */
9999     reject : function(){
10000         var m = this.modified;
10001         for(var n in m){
10002             if(typeof m[n] != "function"){
10003                 this.data[n] = m[n];
10004             }
10005         }
10006         this.dirty = false;
10007         delete this.modified;
10008         this.editing = false;
10009         if(this.store){
10010             this.store.afterReject(this);
10011         }
10012     },
10013
10014     /**
10015      * Usually called by the {@link Roo.data.Store} which owns the Record.
10016      * Commits all changes made to the Record since either creation, or the last commit operation.
10017      * <p>
10018      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10019      * of commit operations.
10020      */
10021     commit : function(){
10022         this.dirty = false;
10023         delete this.modified;
10024         this.editing = false;
10025         if(this.store){
10026             this.store.afterCommit(this);
10027         }
10028     },
10029
10030     // private
10031     hasError : function(){
10032         return this.error != null;
10033     },
10034
10035     // private
10036     clearError : function(){
10037         this.error = null;
10038     },
10039
10040     /**
10041      * Creates a copy of this record.
10042      * @param {String} id (optional) A new record id if you don't want to use this record's id
10043      * @return {Record}
10044      */
10045     copy : function(newId) {
10046         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10047     }
10048 };/*
10049  * Based on:
10050  * Ext JS Library 1.1.1
10051  * Copyright(c) 2006-2007, Ext JS, LLC.
10052  *
10053  * Originally Released Under LGPL - original licence link has changed is not relivant.
10054  *
10055  * Fork - LGPL
10056  * <script type="text/javascript">
10057  */
10058
10059
10060
10061 /**
10062  * @class Roo.data.Store
10063  * @extends Roo.util.Observable
10064  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10065  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10066  * <p>
10067  * 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
10068  * has no knowledge of the format of the data returned by the Proxy.<br>
10069  * <p>
10070  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10071  * instances from the data object. These records are cached and made available through accessor functions.
10072  * @constructor
10073  * Creates a new Store.
10074  * @param {Object} config A config object containing the objects needed for the Store to access data,
10075  * and read the data into Records.
10076  */
10077 Roo.data.Store = function(config){
10078     this.data = new Roo.util.MixedCollection(false);
10079     this.data.getKey = function(o){
10080         return o.id;
10081     };
10082     this.baseParams = {};
10083     // private
10084     this.paramNames = {
10085         "start" : "start",
10086         "limit" : "limit",
10087         "sort" : "sort",
10088         "dir" : "dir",
10089         "multisort" : "_multisort"
10090     };
10091
10092     if(config && config.data){
10093         this.inlineData = config.data;
10094         delete config.data;
10095     }
10096
10097     Roo.apply(this, config);
10098     
10099     if(this.reader){ // reader passed
10100         this.reader = Roo.factory(this.reader, Roo.data);
10101         this.reader.xmodule = this.xmodule || false;
10102         if(!this.recordType){
10103             this.recordType = this.reader.recordType;
10104         }
10105         if(this.reader.onMetaChange){
10106             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10107         }
10108     }
10109
10110     if(this.recordType){
10111         this.fields = this.recordType.prototype.fields;
10112     }
10113     this.modified = [];
10114
10115     this.addEvents({
10116         /**
10117          * @event datachanged
10118          * Fires when the data cache has changed, and a widget which is using this Store
10119          * as a Record cache should refresh its view.
10120          * @param {Store} this
10121          */
10122         datachanged : true,
10123         /**
10124          * @event metachange
10125          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10126          * @param {Store} this
10127          * @param {Object} meta The JSON metadata
10128          */
10129         metachange : true,
10130         /**
10131          * @event add
10132          * Fires when Records have been added to the Store
10133          * @param {Store} this
10134          * @param {Roo.data.Record[]} records The array of Records added
10135          * @param {Number} index The index at which the record(s) were added
10136          */
10137         add : true,
10138         /**
10139          * @event remove
10140          * Fires when a Record has been removed from the Store
10141          * @param {Store} this
10142          * @param {Roo.data.Record} record The Record that was removed
10143          * @param {Number} index The index at which the record was removed
10144          */
10145         remove : true,
10146         /**
10147          * @event update
10148          * Fires when a Record has been updated
10149          * @param {Store} this
10150          * @param {Roo.data.Record} record The Record that was updated
10151          * @param {String} operation The update operation being performed.  Value may be one of:
10152          * <pre><code>
10153  Roo.data.Record.EDIT
10154  Roo.data.Record.REJECT
10155  Roo.data.Record.COMMIT
10156          * </code></pre>
10157          */
10158         update : true,
10159         /**
10160          * @event clear
10161          * Fires when the data cache has been cleared.
10162          * @param {Store} this
10163          */
10164         clear : true,
10165         /**
10166          * @event beforeload
10167          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10168          * the load action will be canceled.
10169          * @param {Store} this
10170          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10171          */
10172         beforeload : true,
10173         /**
10174          * @event beforeloadadd
10175          * Fires after a new set of Records has been loaded.
10176          * @param {Store} this
10177          * @param {Roo.data.Record[]} records The Records that were loaded
10178          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10179          */
10180         beforeloadadd : true,
10181         /**
10182          * @event load
10183          * Fires after a new set of Records has been loaded, before they are added to the store.
10184          * @param {Store} this
10185          * @param {Roo.data.Record[]} records The Records that were loaded
10186          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10187          * @params {Object} return from reader
10188          */
10189         load : true,
10190         /**
10191          * @event loadexception
10192          * Fires if an exception occurs in the Proxy during loading.
10193          * Called with the signature of the Proxy's "loadexception" event.
10194          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10195          * 
10196          * @param {Proxy} 
10197          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10198          * @param {Object} load options 
10199          * @param {Object} jsonData from your request (normally this contains the Exception)
10200          */
10201         loadexception : true
10202     });
10203     
10204     if(this.proxy){
10205         this.proxy = Roo.factory(this.proxy, Roo.data);
10206         this.proxy.xmodule = this.xmodule || false;
10207         this.relayEvents(this.proxy,  ["loadexception"]);
10208     }
10209     this.sortToggle = {};
10210     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10211
10212     Roo.data.Store.superclass.constructor.call(this);
10213
10214     if(this.inlineData){
10215         this.loadData(this.inlineData);
10216         delete this.inlineData;
10217     }
10218 };
10219
10220 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10221      /**
10222     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10223     * without a remote query - used by combo/forms at present.
10224     */
10225     
10226     /**
10227     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10228     */
10229     /**
10230     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10231     */
10232     /**
10233     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10234     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10235     */
10236     /**
10237     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10238     * on any HTTP request
10239     */
10240     /**
10241     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10242     */
10243     /**
10244     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10245     */
10246     multiSort: false,
10247     /**
10248     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10249     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10250     */
10251     remoteSort : false,
10252
10253     /**
10254     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10255      * loaded or when a record is removed. (defaults to false).
10256     */
10257     pruneModifiedRecords : false,
10258
10259     // private
10260     lastOptions : null,
10261
10262     /**
10263      * Add Records to the Store and fires the add event.
10264      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10265      */
10266     add : function(records){
10267         records = [].concat(records);
10268         for(var i = 0, len = records.length; i < len; i++){
10269             records[i].join(this);
10270         }
10271         var index = this.data.length;
10272         this.data.addAll(records);
10273         this.fireEvent("add", this, records, index);
10274     },
10275
10276     /**
10277      * Remove a Record from the Store and fires the remove event.
10278      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10279      */
10280     remove : function(record){
10281         var index = this.data.indexOf(record);
10282         this.data.removeAt(index);
10283         if(this.pruneModifiedRecords){
10284             this.modified.remove(record);
10285         }
10286         this.fireEvent("remove", this, record, index);
10287     },
10288
10289     /**
10290      * Remove all Records from the Store and fires the clear event.
10291      */
10292     removeAll : function(){
10293         this.data.clear();
10294         if(this.pruneModifiedRecords){
10295             this.modified = [];
10296         }
10297         this.fireEvent("clear", this);
10298     },
10299
10300     /**
10301      * Inserts Records to the Store at the given index and fires the add event.
10302      * @param {Number} index The start index at which to insert the passed Records.
10303      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10304      */
10305     insert : function(index, records){
10306         records = [].concat(records);
10307         for(var i = 0, len = records.length; i < len; i++){
10308             this.data.insert(index, records[i]);
10309             records[i].join(this);
10310         }
10311         this.fireEvent("add", this, records, index);
10312     },
10313
10314     /**
10315      * Get the index within the cache of the passed Record.
10316      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10317      * @return {Number} The index of the passed Record. Returns -1 if not found.
10318      */
10319     indexOf : function(record){
10320         return this.data.indexOf(record);
10321     },
10322
10323     /**
10324      * Get the index within the cache of the Record with the passed id.
10325      * @param {String} id The id of the Record to find.
10326      * @return {Number} The index of the Record. Returns -1 if not found.
10327      */
10328     indexOfId : function(id){
10329         return this.data.indexOfKey(id);
10330     },
10331
10332     /**
10333      * Get the Record with the specified id.
10334      * @param {String} id The id of the Record to find.
10335      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10336      */
10337     getById : function(id){
10338         return this.data.key(id);
10339     },
10340
10341     /**
10342      * Get the Record at the specified index.
10343      * @param {Number} index The index of the Record to find.
10344      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10345      */
10346     getAt : function(index){
10347         return this.data.itemAt(index);
10348     },
10349
10350     /**
10351      * Returns a range of Records between specified indices.
10352      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10353      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10354      * @return {Roo.data.Record[]} An array of Records
10355      */
10356     getRange : function(start, end){
10357         return this.data.getRange(start, end);
10358     },
10359
10360     // private
10361     storeOptions : function(o){
10362         o = Roo.apply({}, o);
10363         delete o.callback;
10364         delete o.scope;
10365         this.lastOptions = o;
10366     },
10367
10368     /**
10369      * Loads the Record cache from the configured Proxy using the configured Reader.
10370      * <p>
10371      * If using remote paging, then the first load call must specify the <em>start</em>
10372      * and <em>limit</em> properties in the options.params property to establish the initial
10373      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10374      * <p>
10375      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10376      * and this call will return before the new data has been loaded. Perform any post-processing
10377      * in a callback function, or in a "load" event handler.</strong>
10378      * <p>
10379      * @param {Object} options An object containing properties which control loading options:<ul>
10380      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10381      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10382      * passed the following arguments:<ul>
10383      * <li>r : Roo.data.Record[]</li>
10384      * <li>options: Options object from the load call</li>
10385      * <li>success: Boolean success indicator</li></ul></li>
10386      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10387      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10388      * </ul>
10389      */
10390     load : function(options){
10391         options = options || {};
10392         if(this.fireEvent("beforeload", this, options) !== false){
10393             this.storeOptions(options);
10394             var p = Roo.apply(options.params || {}, this.baseParams);
10395             // if meta was not loaded from remote source.. try requesting it.
10396             if (!this.reader.metaFromRemote) {
10397                 p._requestMeta = 1;
10398             }
10399             if(this.sortInfo && this.remoteSort){
10400                 var pn = this.paramNames;
10401                 p[pn["sort"]] = this.sortInfo.field;
10402                 p[pn["dir"]] = this.sortInfo.direction;
10403             }
10404             if (this.multiSort) {
10405                 var pn = this.paramNames;
10406                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10407             }
10408             
10409             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10410         }
10411     },
10412
10413     /**
10414      * Reloads the Record cache from the configured Proxy using the configured Reader and
10415      * the options from the last load operation performed.
10416      * @param {Object} options (optional) An object containing properties which may override the options
10417      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10418      * the most recently used options are reused).
10419      */
10420     reload : function(options){
10421         this.load(Roo.applyIf(options||{}, this.lastOptions));
10422     },
10423
10424     // private
10425     // Called as a callback by the Reader during a load operation.
10426     loadRecords : function(o, options, success){
10427         if(!o || success === false){
10428             if(success !== false){
10429                 this.fireEvent("load", this, [], options, o);
10430             }
10431             if(options.callback){
10432                 options.callback.call(options.scope || this, [], options, false);
10433             }
10434             return;
10435         }
10436         // if data returned failure - throw an exception.
10437         if (o.success === false) {
10438             // show a message if no listener is registered.
10439             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10440                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10441             }
10442             // loadmask wil be hooked into this..
10443             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10444             return;
10445         }
10446         var r = o.records, t = o.totalRecords || r.length;
10447         
10448         this.fireEvent("beforeloadadd", this, r, options, o);
10449         
10450         if(!options || options.add !== true){
10451             if(this.pruneModifiedRecords){
10452                 this.modified = [];
10453             }
10454             for(var i = 0, len = r.length; i < len; i++){
10455                 r[i].join(this);
10456             }
10457             if(this.snapshot){
10458                 this.data = this.snapshot;
10459                 delete this.snapshot;
10460             }
10461             this.data.clear();
10462             this.data.addAll(r);
10463             this.totalLength = t;
10464             this.applySort();
10465             this.fireEvent("datachanged", this);
10466         }else{
10467             this.totalLength = Math.max(t, this.data.length+r.length);
10468             this.add(r);
10469         }
10470         this.fireEvent("load", this, r, options, o);
10471         if(options.callback){
10472             options.callback.call(options.scope || this, r, options, true);
10473         }
10474     },
10475
10476
10477     /**
10478      * Loads data from a passed data block. A Reader which understands the format of the data
10479      * must have been configured in the constructor.
10480      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10481      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10482      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10483      */
10484     loadData : function(o, append){
10485         var r = this.reader.readRecords(o);
10486         this.loadRecords(r, {add: append}, true);
10487     },
10488
10489     /**
10490      * Gets the number of cached records.
10491      * <p>
10492      * <em>If using paging, this may not be the total size of the dataset. If the data object
10493      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10494      * the data set size</em>
10495      */
10496     getCount : function(){
10497         return this.data.length || 0;
10498     },
10499
10500     /**
10501      * Gets the total number of records in the dataset as returned by the server.
10502      * <p>
10503      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10504      * the dataset size</em>
10505      */
10506     getTotalCount : function(){
10507         return this.totalLength || 0;
10508     },
10509
10510     /**
10511      * Returns the sort state of the Store as an object with two properties:
10512      * <pre><code>
10513  field {String} The name of the field by which the Records are sorted
10514  direction {String} The sort order, "ASC" or "DESC"
10515      * </code></pre>
10516      */
10517     getSortState : function(){
10518         return this.sortInfo;
10519     },
10520
10521     // private
10522     applySort : function(){
10523         if(this.sortInfo && !this.remoteSort){
10524             var s = this.sortInfo, f = s.field;
10525             var st = this.fields.get(f).sortType;
10526             var fn = function(r1, r2){
10527                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10528                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10529             };
10530             this.data.sort(s.direction, fn);
10531             if(this.snapshot && this.snapshot != this.data){
10532                 this.snapshot.sort(s.direction, fn);
10533             }
10534         }
10535     },
10536
10537     /**
10538      * Sets the default sort column and order to be used by the next load operation.
10539      * @param {String} fieldName The name of the field to sort by.
10540      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10541      */
10542     setDefaultSort : function(field, dir){
10543         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10544     },
10545
10546     /**
10547      * Sort the Records.
10548      * If remote sorting is used, the sort is performed on the server, and the cache is
10549      * reloaded. If local sorting is used, the cache is sorted internally.
10550      * @param {String} fieldName The name of the field to sort by.
10551      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10552      */
10553     sort : function(fieldName, dir){
10554         var f = this.fields.get(fieldName);
10555         if(!dir){
10556             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10557             
10558             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10559                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10560             }else{
10561                 dir = f.sortDir;
10562             }
10563         }
10564         this.sortToggle[f.name] = dir;
10565         this.sortInfo = {field: f.name, direction: dir};
10566         if(!this.remoteSort){
10567             this.applySort();
10568             this.fireEvent("datachanged", this);
10569         }else{
10570             this.load(this.lastOptions);
10571         }
10572     },
10573
10574     /**
10575      * Calls the specified function for each of the Records in the cache.
10576      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10577      * Returning <em>false</em> aborts and exits the iteration.
10578      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10579      */
10580     each : function(fn, scope){
10581         this.data.each(fn, scope);
10582     },
10583
10584     /**
10585      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10586      * (e.g., during paging).
10587      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10588      */
10589     getModifiedRecords : function(){
10590         return this.modified;
10591     },
10592
10593     // private
10594     createFilterFn : function(property, value, anyMatch){
10595         if(!value.exec){ // not a regex
10596             value = String(value);
10597             if(value.length == 0){
10598                 return false;
10599             }
10600             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10601         }
10602         return function(r){
10603             return value.test(r.data[property]);
10604         };
10605     },
10606
10607     /**
10608      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10609      * @param {String} property A field on your records
10610      * @param {Number} start The record index to start at (defaults to 0)
10611      * @param {Number} end The last record index to include (defaults to length - 1)
10612      * @return {Number} The sum
10613      */
10614     sum : function(property, start, end){
10615         var rs = this.data.items, v = 0;
10616         start = start || 0;
10617         end = (end || end === 0) ? end : rs.length-1;
10618
10619         for(var i = start; i <= end; i++){
10620             v += (rs[i].data[property] || 0);
10621         }
10622         return v;
10623     },
10624
10625     /**
10626      * Filter the records by a specified property.
10627      * @param {String} field A field on your records
10628      * @param {String/RegExp} value Either a string that the field
10629      * should start with or a RegExp to test against the field
10630      * @param {Boolean} anyMatch True to match any part not just the beginning
10631      */
10632     filter : function(property, value, anyMatch){
10633         var fn = this.createFilterFn(property, value, anyMatch);
10634         return fn ? this.filterBy(fn) : this.clearFilter();
10635     },
10636
10637     /**
10638      * Filter by a function. The specified function will be called with each
10639      * record in this data source. If the function returns true the record is included,
10640      * otherwise it is filtered.
10641      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10642      * @param {Object} scope (optional) The scope of the function (defaults to this)
10643      */
10644     filterBy : function(fn, scope){
10645         this.snapshot = this.snapshot || this.data;
10646         this.data = this.queryBy(fn, scope||this);
10647         this.fireEvent("datachanged", this);
10648     },
10649
10650     /**
10651      * Query the records by a specified property.
10652      * @param {String} field A field on your records
10653      * @param {String/RegExp} value Either a string that the field
10654      * should start with or a RegExp to test against the field
10655      * @param {Boolean} anyMatch True to match any part not just the beginning
10656      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10657      */
10658     query : function(property, value, anyMatch){
10659         var fn = this.createFilterFn(property, value, anyMatch);
10660         return fn ? this.queryBy(fn) : this.data.clone();
10661     },
10662
10663     /**
10664      * Query by a function. The specified function will be called with each
10665      * record in this data source. If the function returns true the record is included
10666      * in the results.
10667      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10668      * @param {Object} scope (optional) The scope of the function (defaults to this)
10669       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10670      **/
10671     queryBy : function(fn, scope){
10672         var data = this.snapshot || this.data;
10673         return data.filterBy(fn, scope||this);
10674     },
10675
10676     /**
10677      * Collects unique values for a particular dataIndex from this store.
10678      * @param {String} dataIndex The property to collect
10679      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10680      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10681      * @return {Array} An array of the unique values
10682      **/
10683     collect : function(dataIndex, allowNull, bypassFilter){
10684         var d = (bypassFilter === true && this.snapshot) ?
10685                 this.snapshot.items : this.data.items;
10686         var v, sv, r = [], l = {};
10687         for(var i = 0, len = d.length; i < len; i++){
10688             v = d[i].data[dataIndex];
10689             sv = String(v);
10690             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10691                 l[sv] = true;
10692                 r[r.length] = v;
10693             }
10694         }
10695         return r;
10696     },
10697
10698     /**
10699      * Revert to a view of the Record cache with no filtering applied.
10700      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10701      */
10702     clearFilter : function(suppressEvent){
10703         if(this.snapshot && this.snapshot != this.data){
10704             this.data = this.snapshot;
10705             delete this.snapshot;
10706             if(suppressEvent !== true){
10707                 this.fireEvent("datachanged", this);
10708             }
10709         }
10710     },
10711
10712     // private
10713     afterEdit : function(record){
10714         if(this.modified.indexOf(record) == -1){
10715             this.modified.push(record);
10716         }
10717         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10718     },
10719     
10720     // private
10721     afterReject : function(record){
10722         this.modified.remove(record);
10723         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10724     },
10725
10726     // private
10727     afterCommit : function(record){
10728         this.modified.remove(record);
10729         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10730     },
10731
10732     /**
10733      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10734      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10735      */
10736     commitChanges : function(){
10737         var m = this.modified.slice(0);
10738         this.modified = [];
10739         for(var i = 0, len = m.length; i < len; i++){
10740             m[i].commit();
10741         }
10742     },
10743
10744     /**
10745      * Cancel outstanding changes on all changed records.
10746      */
10747     rejectChanges : function(){
10748         var m = this.modified.slice(0);
10749         this.modified = [];
10750         for(var i = 0, len = m.length; i < len; i++){
10751             m[i].reject();
10752         }
10753     },
10754
10755     onMetaChange : function(meta, rtype, o){
10756         this.recordType = rtype;
10757         this.fields = rtype.prototype.fields;
10758         delete this.snapshot;
10759         this.sortInfo = meta.sortInfo || this.sortInfo;
10760         this.modified = [];
10761         this.fireEvent('metachange', this, this.reader.meta);
10762     },
10763     
10764     moveIndex : function(data, type)
10765     {
10766         var index = this.indexOf(data);
10767         
10768         var newIndex = index + type;
10769         
10770         this.remove(data);
10771         
10772         this.insert(newIndex, data);
10773         
10774     }
10775 });/*
10776  * Based on:
10777  * Ext JS Library 1.1.1
10778  * Copyright(c) 2006-2007, Ext JS, LLC.
10779  *
10780  * Originally Released Under LGPL - original licence link has changed is not relivant.
10781  *
10782  * Fork - LGPL
10783  * <script type="text/javascript">
10784  */
10785
10786 /**
10787  * @class Roo.data.SimpleStore
10788  * @extends Roo.data.Store
10789  * Small helper class to make creating Stores from Array data easier.
10790  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10791  * @cfg {Array} fields An array of field definition objects, or field name strings.
10792  * @cfg {Array} data The multi-dimensional array of data
10793  * @constructor
10794  * @param {Object} config
10795  */
10796 Roo.data.SimpleStore = function(config){
10797     Roo.data.SimpleStore.superclass.constructor.call(this, {
10798         isLocal : true,
10799         reader: new Roo.data.ArrayReader({
10800                 id: config.id
10801             },
10802             Roo.data.Record.create(config.fields)
10803         ),
10804         proxy : new Roo.data.MemoryProxy(config.data)
10805     });
10806     this.load();
10807 };
10808 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10809  * Based on:
10810  * Ext JS Library 1.1.1
10811  * Copyright(c) 2006-2007, Ext JS, LLC.
10812  *
10813  * Originally Released Under LGPL - original licence link has changed is not relivant.
10814  *
10815  * Fork - LGPL
10816  * <script type="text/javascript">
10817  */
10818
10819 /**
10820 /**
10821  * @extends Roo.data.Store
10822  * @class Roo.data.JsonStore
10823  * Small helper class to make creating Stores for JSON data easier. <br/>
10824 <pre><code>
10825 var store = new Roo.data.JsonStore({
10826     url: 'get-images.php',
10827     root: 'images',
10828     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10829 });
10830 </code></pre>
10831  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10832  * JsonReader and HttpProxy (unless inline data is provided).</b>
10833  * @cfg {Array} fields An array of field definition objects, or field name strings.
10834  * @constructor
10835  * @param {Object} config
10836  */
10837 Roo.data.JsonStore = function(c){
10838     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10839         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10840         reader: new Roo.data.JsonReader(c, c.fields)
10841     }));
10842 };
10843 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10844  * Based on:
10845  * Ext JS Library 1.1.1
10846  * Copyright(c) 2006-2007, Ext JS, LLC.
10847  *
10848  * Originally Released Under LGPL - original licence link has changed is not relivant.
10849  *
10850  * Fork - LGPL
10851  * <script type="text/javascript">
10852  */
10853
10854  
10855 Roo.data.Field = function(config){
10856     if(typeof config == "string"){
10857         config = {name: config};
10858     }
10859     Roo.apply(this, config);
10860     
10861     if(!this.type){
10862         this.type = "auto";
10863     }
10864     
10865     var st = Roo.data.SortTypes;
10866     // named sortTypes are supported, here we look them up
10867     if(typeof this.sortType == "string"){
10868         this.sortType = st[this.sortType];
10869     }
10870     
10871     // set default sortType for strings and dates
10872     if(!this.sortType){
10873         switch(this.type){
10874             case "string":
10875                 this.sortType = st.asUCString;
10876                 break;
10877             case "date":
10878                 this.sortType = st.asDate;
10879                 break;
10880             default:
10881                 this.sortType = st.none;
10882         }
10883     }
10884
10885     // define once
10886     var stripRe = /[\$,%]/g;
10887
10888     // prebuilt conversion function for this field, instead of
10889     // switching every time we're reading a value
10890     if(!this.convert){
10891         var cv, dateFormat = this.dateFormat;
10892         switch(this.type){
10893             case "":
10894             case "auto":
10895             case undefined:
10896                 cv = function(v){ return v; };
10897                 break;
10898             case "string":
10899                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10900                 break;
10901             case "int":
10902                 cv = function(v){
10903                     return v !== undefined && v !== null && v !== '' ?
10904                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10905                     };
10906                 break;
10907             case "float":
10908                 cv = function(v){
10909                     return v !== undefined && v !== null && v !== '' ?
10910                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10911                     };
10912                 break;
10913             case "bool":
10914             case "boolean":
10915                 cv = function(v){ return v === true || v === "true" || v == 1; };
10916                 break;
10917             case "date":
10918                 cv = function(v){
10919                     if(!v){
10920                         return '';
10921                     }
10922                     if(v instanceof Date){
10923                         return v;
10924                     }
10925                     if(dateFormat){
10926                         if(dateFormat == "timestamp"){
10927                             return new Date(v*1000);
10928                         }
10929                         return Date.parseDate(v, dateFormat);
10930                     }
10931                     var parsed = Date.parse(v);
10932                     return parsed ? new Date(parsed) : null;
10933                 };
10934              break;
10935             
10936         }
10937         this.convert = cv;
10938     }
10939 };
10940
10941 Roo.data.Field.prototype = {
10942     dateFormat: null,
10943     defaultValue: "",
10944     mapping: null,
10945     sortType : null,
10946     sortDir : "ASC"
10947 };/*
10948  * Based on:
10949  * Ext JS Library 1.1.1
10950  * Copyright(c) 2006-2007, Ext JS, LLC.
10951  *
10952  * Originally Released Under LGPL - original licence link has changed is not relivant.
10953  *
10954  * Fork - LGPL
10955  * <script type="text/javascript">
10956  */
10957  
10958 // Base class for reading structured data from a data source.  This class is intended to be
10959 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10960
10961 /**
10962  * @class Roo.data.DataReader
10963  * Base class for reading structured data from a data source.  This class is intended to be
10964  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10965  */
10966
10967 Roo.data.DataReader = function(meta, recordType){
10968     
10969     this.meta = meta;
10970     
10971     this.recordType = recordType instanceof Array ? 
10972         Roo.data.Record.create(recordType) : recordType;
10973 };
10974
10975 Roo.data.DataReader.prototype = {
10976      /**
10977      * Create an empty record
10978      * @param {Object} data (optional) - overlay some values
10979      * @return {Roo.data.Record} record created.
10980      */
10981     newRow :  function(d) {
10982         var da =  {};
10983         this.recordType.prototype.fields.each(function(c) {
10984             switch( c.type) {
10985                 case 'int' : da[c.name] = 0; break;
10986                 case 'date' : da[c.name] = new Date(); break;
10987                 case 'float' : da[c.name] = 0.0; break;
10988                 case 'boolean' : da[c.name] = false; break;
10989                 default : da[c.name] = ""; break;
10990             }
10991             
10992         });
10993         return new this.recordType(Roo.apply(da, d));
10994     }
10995     
10996 };/*
10997  * Based on:
10998  * Ext JS Library 1.1.1
10999  * Copyright(c) 2006-2007, Ext JS, LLC.
11000  *
11001  * Originally Released Under LGPL - original licence link has changed is not relivant.
11002  *
11003  * Fork - LGPL
11004  * <script type="text/javascript">
11005  */
11006
11007 /**
11008  * @class Roo.data.DataProxy
11009  * @extends Roo.data.Observable
11010  * This class is an abstract base class for implementations which provide retrieval of
11011  * unformatted data objects.<br>
11012  * <p>
11013  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11014  * (of the appropriate type which knows how to parse the data object) to provide a block of
11015  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11016  * <p>
11017  * Custom implementations must implement the load method as described in
11018  * {@link Roo.data.HttpProxy#load}.
11019  */
11020 Roo.data.DataProxy = function(){
11021     this.addEvents({
11022         /**
11023          * @event beforeload
11024          * Fires before a network request is made to retrieve a data object.
11025          * @param {Object} This DataProxy object.
11026          * @param {Object} params The params parameter to the load function.
11027          */
11028         beforeload : true,
11029         /**
11030          * @event load
11031          * Fires before the load method's callback is called.
11032          * @param {Object} This DataProxy object.
11033          * @param {Object} o The data object.
11034          * @param {Object} arg The callback argument object passed to the load function.
11035          */
11036         load : true,
11037         /**
11038          * @event loadexception
11039          * Fires if an Exception occurs during data retrieval.
11040          * @param {Object} This DataProxy object.
11041          * @param {Object} o The data object.
11042          * @param {Object} arg The callback argument object passed to the load function.
11043          * @param {Object} e The Exception.
11044          */
11045         loadexception : true
11046     });
11047     Roo.data.DataProxy.superclass.constructor.call(this);
11048 };
11049
11050 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11051
11052     /**
11053      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11054      */
11055 /*
11056  * Based on:
11057  * Ext JS Library 1.1.1
11058  * Copyright(c) 2006-2007, Ext JS, LLC.
11059  *
11060  * Originally Released Under LGPL - original licence link has changed is not relivant.
11061  *
11062  * Fork - LGPL
11063  * <script type="text/javascript">
11064  */
11065 /**
11066  * @class Roo.data.MemoryProxy
11067  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11068  * to the Reader when its load method is called.
11069  * @constructor
11070  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11071  */
11072 Roo.data.MemoryProxy = function(data){
11073     if (data.data) {
11074         data = data.data;
11075     }
11076     Roo.data.MemoryProxy.superclass.constructor.call(this);
11077     this.data = data;
11078 };
11079
11080 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11081     
11082     /**
11083      * Load data from the requested source (in this case an in-memory
11084      * data object passed to the constructor), read the data object into
11085      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11086      * process that block using the passed callback.
11087      * @param {Object} params This parameter is not used by the MemoryProxy class.
11088      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11089      * object into a block of Roo.data.Records.
11090      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11091      * The function must be passed <ul>
11092      * <li>The Record block object</li>
11093      * <li>The "arg" argument from the load function</li>
11094      * <li>A boolean success indicator</li>
11095      * </ul>
11096      * @param {Object} scope The scope in which to call the callback
11097      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11098      */
11099     load : function(params, reader, callback, scope, arg){
11100         params = params || {};
11101         var result;
11102         try {
11103             result = reader.readRecords(this.data);
11104         }catch(e){
11105             this.fireEvent("loadexception", this, arg, null, e);
11106             callback.call(scope, null, arg, false);
11107             return;
11108         }
11109         callback.call(scope, result, arg, true);
11110     },
11111     
11112     // private
11113     update : function(params, records){
11114         
11115     }
11116 });/*
11117  * Based on:
11118  * Ext JS Library 1.1.1
11119  * Copyright(c) 2006-2007, Ext JS, LLC.
11120  *
11121  * Originally Released Under LGPL - original licence link has changed is not relivant.
11122  *
11123  * Fork - LGPL
11124  * <script type="text/javascript">
11125  */
11126 /**
11127  * @class Roo.data.HttpProxy
11128  * @extends Roo.data.DataProxy
11129  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11130  * configured to reference a certain URL.<br><br>
11131  * <p>
11132  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11133  * from which the running page was served.<br><br>
11134  * <p>
11135  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11136  * <p>
11137  * Be aware that to enable the browser to parse an XML document, the server must set
11138  * the Content-Type header in the HTTP response to "text/xml".
11139  * @constructor
11140  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11141  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11142  * will be used to make the request.
11143  */
11144 Roo.data.HttpProxy = function(conn){
11145     Roo.data.HttpProxy.superclass.constructor.call(this);
11146     // is conn a conn config or a real conn?
11147     this.conn = conn;
11148     this.useAjax = !conn || !conn.events;
11149   
11150 };
11151
11152 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11153     // thse are take from connection...
11154     
11155     /**
11156      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11157      */
11158     /**
11159      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11160      * extra parameters to each request made by this object. (defaults to undefined)
11161      */
11162     /**
11163      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11164      *  to each request made by this object. (defaults to undefined)
11165      */
11166     /**
11167      * @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)
11168      */
11169     /**
11170      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11171      */
11172      /**
11173      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11174      * @type Boolean
11175      */
11176   
11177
11178     /**
11179      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11180      * @type Boolean
11181      */
11182     /**
11183      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11184      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11185      * a finer-grained basis than the DataProxy events.
11186      */
11187     getConnection : function(){
11188         return this.useAjax ? Roo.Ajax : this.conn;
11189     },
11190
11191     /**
11192      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11193      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11194      * process that block using the passed callback.
11195      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11196      * for the request to the remote server.
11197      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11198      * object into a block of Roo.data.Records.
11199      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11200      * The function must be passed <ul>
11201      * <li>The Record block object</li>
11202      * <li>The "arg" argument from the load function</li>
11203      * <li>A boolean success indicator</li>
11204      * </ul>
11205      * @param {Object} scope The scope in which to call the callback
11206      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11207      */
11208     load : function(params, reader, callback, scope, arg){
11209         if(this.fireEvent("beforeload", this, params) !== false){
11210             var  o = {
11211                 params : params || {},
11212                 request: {
11213                     callback : callback,
11214                     scope : scope,
11215                     arg : arg
11216                 },
11217                 reader: reader,
11218                 callback : this.loadResponse,
11219                 scope: this
11220             };
11221             if(this.useAjax){
11222                 Roo.applyIf(o, this.conn);
11223                 if(this.activeRequest){
11224                     Roo.Ajax.abort(this.activeRequest);
11225                 }
11226                 this.activeRequest = Roo.Ajax.request(o);
11227             }else{
11228                 this.conn.request(o);
11229             }
11230         }else{
11231             callback.call(scope||this, null, arg, false);
11232         }
11233     },
11234
11235     // private
11236     loadResponse : function(o, success, response){
11237         delete this.activeRequest;
11238         if(!success){
11239             this.fireEvent("loadexception", this, o, response);
11240             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11241             return;
11242         }
11243         var result;
11244         try {
11245             result = o.reader.read(response);
11246         }catch(e){
11247             this.fireEvent("loadexception", this, o, response, e);
11248             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11249             return;
11250         }
11251         
11252         this.fireEvent("load", this, o, o.request.arg);
11253         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11254     },
11255
11256     // private
11257     update : function(dataSet){
11258
11259     },
11260
11261     // private
11262     updateResponse : function(dataSet){
11263
11264     }
11265 });/*
11266  * Based on:
11267  * Ext JS Library 1.1.1
11268  * Copyright(c) 2006-2007, Ext JS, LLC.
11269  *
11270  * Originally Released Under LGPL - original licence link has changed is not relivant.
11271  *
11272  * Fork - LGPL
11273  * <script type="text/javascript">
11274  */
11275
11276 /**
11277  * @class Roo.data.ScriptTagProxy
11278  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11279  * other than the originating domain of the running page.<br><br>
11280  * <p>
11281  * <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
11282  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11283  * <p>
11284  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11285  * source code that is used as the source inside a &lt;script> tag.<br><br>
11286  * <p>
11287  * In order for the browser to process the returned data, the server must wrap the data object
11288  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11289  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11290  * depending on whether the callback name was passed:
11291  * <p>
11292  * <pre><code>
11293 boolean scriptTag = false;
11294 String cb = request.getParameter("callback");
11295 if (cb != null) {
11296     scriptTag = true;
11297     response.setContentType("text/javascript");
11298 } else {
11299     response.setContentType("application/x-json");
11300 }
11301 Writer out = response.getWriter();
11302 if (scriptTag) {
11303     out.write(cb + "(");
11304 }
11305 out.print(dataBlock.toJsonString());
11306 if (scriptTag) {
11307     out.write(");");
11308 }
11309 </pre></code>
11310  *
11311  * @constructor
11312  * @param {Object} config A configuration object.
11313  */
11314 Roo.data.ScriptTagProxy = function(config){
11315     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11316     Roo.apply(this, config);
11317     this.head = document.getElementsByTagName("head")[0];
11318 };
11319
11320 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11321
11322 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11323     /**
11324      * @cfg {String} url The URL from which to request the data object.
11325      */
11326     /**
11327      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11328      */
11329     timeout : 30000,
11330     /**
11331      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11332      * the server the name of the callback function set up by the load call to process the returned data object.
11333      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11334      * javascript output which calls this named function passing the data object as its only parameter.
11335      */
11336     callbackParam : "callback",
11337     /**
11338      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11339      * name to the request.
11340      */
11341     nocache : true,
11342
11343     /**
11344      * Load data from the configured URL, read the data object into
11345      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11346      * process that block using the passed callback.
11347      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11348      * for the request to the remote server.
11349      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11350      * object into a block of Roo.data.Records.
11351      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11352      * The function must be passed <ul>
11353      * <li>The Record block object</li>
11354      * <li>The "arg" argument from the load function</li>
11355      * <li>A boolean success indicator</li>
11356      * </ul>
11357      * @param {Object} scope The scope in which to call the callback
11358      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11359      */
11360     load : function(params, reader, callback, scope, arg){
11361         if(this.fireEvent("beforeload", this, params) !== false){
11362
11363             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11364
11365             var url = this.url;
11366             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11367             if(this.nocache){
11368                 url += "&_dc=" + (new Date().getTime());
11369             }
11370             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11371             var trans = {
11372                 id : transId,
11373                 cb : "stcCallback"+transId,
11374                 scriptId : "stcScript"+transId,
11375                 params : params,
11376                 arg : arg,
11377                 url : url,
11378                 callback : callback,
11379                 scope : scope,
11380                 reader : reader
11381             };
11382             var conn = this;
11383
11384             window[trans.cb] = function(o){
11385                 conn.handleResponse(o, trans);
11386             };
11387
11388             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11389
11390             if(this.autoAbort !== false){
11391                 this.abort();
11392             }
11393
11394             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11395
11396             var script = document.createElement("script");
11397             script.setAttribute("src", url);
11398             script.setAttribute("type", "text/javascript");
11399             script.setAttribute("id", trans.scriptId);
11400             this.head.appendChild(script);
11401
11402             this.trans = trans;
11403         }else{
11404             callback.call(scope||this, null, arg, false);
11405         }
11406     },
11407
11408     // private
11409     isLoading : function(){
11410         return this.trans ? true : false;
11411     },
11412
11413     /**
11414      * Abort the current server request.
11415      */
11416     abort : function(){
11417         if(this.isLoading()){
11418             this.destroyTrans(this.trans);
11419         }
11420     },
11421
11422     // private
11423     destroyTrans : function(trans, isLoaded){
11424         this.head.removeChild(document.getElementById(trans.scriptId));
11425         clearTimeout(trans.timeoutId);
11426         if(isLoaded){
11427             window[trans.cb] = undefined;
11428             try{
11429                 delete window[trans.cb];
11430             }catch(e){}
11431         }else{
11432             // if hasn't been loaded, wait for load to remove it to prevent script error
11433             window[trans.cb] = function(){
11434                 window[trans.cb] = undefined;
11435                 try{
11436                     delete window[trans.cb];
11437                 }catch(e){}
11438             };
11439         }
11440     },
11441
11442     // private
11443     handleResponse : function(o, trans){
11444         this.trans = false;
11445         this.destroyTrans(trans, true);
11446         var result;
11447         try {
11448             result = trans.reader.readRecords(o);
11449         }catch(e){
11450             this.fireEvent("loadexception", this, o, trans.arg, e);
11451             trans.callback.call(trans.scope||window, null, trans.arg, false);
11452             return;
11453         }
11454         this.fireEvent("load", this, o, trans.arg);
11455         trans.callback.call(trans.scope||window, result, trans.arg, true);
11456     },
11457
11458     // private
11459     handleFailure : function(trans){
11460         this.trans = false;
11461         this.destroyTrans(trans, false);
11462         this.fireEvent("loadexception", this, null, trans.arg);
11463         trans.callback.call(trans.scope||window, null, trans.arg, false);
11464     }
11465 });/*
11466  * Based on:
11467  * Ext JS Library 1.1.1
11468  * Copyright(c) 2006-2007, Ext JS, LLC.
11469  *
11470  * Originally Released Under LGPL - original licence link has changed is not relivant.
11471  *
11472  * Fork - LGPL
11473  * <script type="text/javascript">
11474  */
11475
11476 /**
11477  * @class Roo.data.JsonReader
11478  * @extends Roo.data.DataReader
11479  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11480  * based on mappings in a provided Roo.data.Record constructor.
11481  * 
11482  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11483  * in the reply previously. 
11484  * 
11485  * <p>
11486  * Example code:
11487  * <pre><code>
11488 var RecordDef = Roo.data.Record.create([
11489     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11490     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11491 ]);
11492 var myReader = new Roo.data.JsonReader({
11493     totalProperty: "results",    // The property which contains the total dataset size (optional)
11494     root: "rows",                // The property which contains an Array of row objects
11495     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11496 }, RecordDef);
11497 </code></pre>
11498  * <p>
11499  * This would consume a JSON file like this:
11500  * <pre><code>
11501 { 'results': 2, 'rows': [
11502     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11503     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11504 }
11505 </code></pre>
11506  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11507  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11508  * paged from the remote server.
11509  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11510  * @cfg {String} root name of the property which contains the Array of row objects.
11511  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11512  * @cfg {Array} fields Array of field definition objects
11513  * @constructor
11514  * Create a new JsonReader
11515  * @param {Object} meta Metadata configuration options
11516  * @param {Object} recordType Either an Array of field definition objects,
11517  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11518  */
11519 Roo.data.JsonReader = function(meta, recordType){
11520     
11521     meta = meta || {};
11522     // set some defaults:
11523     Roo.applyIf(meta, {
11524         totalProperty: 'total',
11525         successProperty : 'success',
11526         root : 'data',
11527         id : 'id'
11528     });
11529     
11530     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11531 };
11532 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11533     
11534     /**
11535      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11536      * Used by Store query builder to append _requestMeta to params.
11537      * 
11538      */
11539     metaFromRemote : false,
11540     /**
11541      * This method is only used by a DataProxy which has retrieved data from a remote server.
11542      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11543      * @return {Object} data A data block which is used by an Roo.data.Store object as
11544      * a cache of Roo.data.Records.
11545      */
11546     read : function(response){
11547         var json = response.responseText;
11548        
11549         var o = /* eval:var:o */ eval("("+json+")");
11550         if(!o) {
11551             throw {message: "JsonReader.read: Json object not found"};
11552         }
11553         
11554         if(o.metaData){
11555             
11556             delete this.ef;
11557             this.metaFromRemote = true;
11558             this.meta = o.metaData;
11559             this.recordType = Roo.data.Record.create(o.metaData.fields);
11560             this.onMetaChange(this.meta, this.recordType, o);
11561         }
11562         return this.readRecords(o);
11563     },
11564
11565     // private function a store will implement
11566     onMetaChange : function(meta, recordType, o){
11567
11568     },
11569
11570     /**
11571          * @ignore
11572          */
11573     simpleAccess: function(obj, subsc) {
11574         return obj[subsc];
11575     },
11576
11577         /**
11578          * @ignore
11579          */
11580     getJsonAccessor: function(){
11581         var re = /[\[\.]/;
11582         return function(expr) {
11583             try {
11584                 return(re.test(expr))
11585                     ? new Function("obj", "return obj." + expr)
11586                     : function(obj){
11587                         return obj[expr];
11588                     };
11589             } catch(e){}
11590             return Roo.emptyFn;
11591         };
11592     }(),
11593
11594     /**
11595      * Create a data block containing Roo.data.Records from an XML document.
11596      * @param {Object} o An object which contains an Array of row objects in the property specified
11597      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11598      * which contains the total size of the dataset.
11599      * @return {Object} data A data block which is used by an Roo.data.Store object as
11600      * a cache of Roo.data.Records.
11601      */
11602     readRecords : function(o){
11603         /**
11604          * After any data loads, the raw JSON data is available for further custom processing.
11605          * @type Object
11606          */
11607         this.o = o;
11608         var s = this.meta, Record = this.recordType,
11609             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11610
11611 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11612         if (!this.ef) {
11613             if(s.totalProperty) {
11614                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11615                 }
11616                 if(s.successProperty) {
11617                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11618                 }
11619                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11620                 if (s.id) {
11621                         var g = this.getJsonAccessor(s.id);
11622                         this.getId = function(rec) {
11623                                 var r = g(rec);  
11624                                 return (r === undefined || r === "") ? null : r;
11625                         };
11626                 } else {
11627                         this.getId = function(){return null;};
11628                 }
11629             this.ef = [];
11630             for(var jj = 0; jj < fl; jj++){
11631                 f = fi[jj];
11632                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11633                 this.ef[jj] = this.getJsonAccessor(map);
11634             }
11635         }
11636
11637         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11638         if(s.totalProperty){
11639             var vt = parseInt(this.getTotal(o), 10);
11640             if(!isNaN(vt)){
11641                 totalRecords = vt;
11642             }
11643         }
11644         if(s.successProperty){
11645             var vs = this.getSuccess(o);
11646             if(vs === false || vs === 'false'){
11647                 success = false;
11648             }
11649         }
11650         var records = [];
11651         for(var i = 0; i < c; i++){
11652                 var n = root[i];
11653             var values = {};
11654             var id = this.getId(n);
11655             for(var j = 0; j < fl; j++){
11656                 f = fi[j];
11657             var v = this.ef[j](n);
11658             if (!f.convert) {
11659                 Roo.log('missing convert for ' + f.name);
11660                 Roo.log(f);
11661                 continue;
11662             }
11663             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11664             }
11665             var record = new Record(values, id);
11666             record.json = n;
11667             records[i] = record;
11668         }
11669         return {
11670             raw : o,
11671             success : success,
11672             records : records,
11673             totalRecords : totalRecords
11674         };
11675     }
11676 });/*
11677  * Based on:
11678  * Ext JS Library 1.1.1
11679  * Copyright(c) 2006-2007, Ext JS, LLC.
11680  *
11681  * Originally Released Under LGPL - original licence link has changed is not relivant.
11682  *
11683  * Fork - LGPL
11684  * <script type="text/javascript">
11685  */
11686
11687 /**
11688  * @class Roo.data.ArrayReader
11689  * @extends Roo.data.DataReader
11690  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11691  * Each element of that Array represents a row of data fields. The
11692  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11693  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11694  * <p>
11695  * Example code:.
11696  * <pre><code>
11697 var RecordDef = Roo.data.Record.create([
11698     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11699     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11700 ]);
11701 var myReader = new Roo.data.ArrayReader({
11702     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11703 }, RecordDef);
11704 </code></pre>
11705  * <p>
11706  * This would consume an Array like this:
11707  * <pre><code>
11708 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11709   </code></pre>
11710  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11711  * @constructor
11712  * Create a new JsonReader
11713  * @param {Object} meta Metadata configuration options.
11714  * @param {Object} recordType Either an Array of field definition objects
11715  * as specified to {@link Roo.data.Record#create},
11716  * or an {@link Roo.data.Record} object
11717  * created using {@link Roo.data.Record#create}.
11718  */
11719 Roo.data.ArrayReader = function(meta, recordType){
11720     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11721 };
11722
11723 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11724     /**
11725      * Create a data block containing Roo.data.Records from an XML document.
11726      * @param {Object} o An Array of row objects which represents the dataset.
11727      * @return {Object} data A data block which is used by an Roo.data.Store object as
11728      * a cache of Roo.data.Records.
11729      */
11730     readRecords : function(o){
11731         var sid = this.meta ? this.meta.id : null;
11732         var recordType = this.recordType, fields = recordType.prototype.fields;
11733         var records = [];
11734         var root = o;
11735             for(var i = 0; i < root.length; i++){
11736                     var n = root[i];
11737                 var values = {};
11738                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11739                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11740                 var f = fields.items[j];
11741                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11742                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11743                 v = f.convert(v);
11744                 values[f.name] = v;
11745             }
11746                 var record = new recordType(values, id);
11747                 record.json = n;
11748                 records[records.length] = record;
11749             }
11750             return {
11751                 records : records,
11752                 totalRecords : records.length
11753             };
11754     }
11755 });/*
11756  * - LGPL
11757  * * 
11758  */
11759
11760 /**
11761  * @class Roo.bootstrap.ComboBox
11762  * @extends Roo.bootstrap.TriggerField
11763  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11764  * @cfg {Boolean} append (true|false) default false
11765  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11766  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11767  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11768  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11769  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11770  * @cfg {Boolean} animate default true
11771  * @cfg {Boolean} emptyResultText only for touch device
11772  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11773  * @constructor
11774  * Create a new ComboBox.
11775  * @param {Object} config Configuration options
11776  */
11777 Roo.bootstrap.ComboBox = function(config){
11778     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11779     this.addEvents({
11780         /**
11781          * @event expand
11782          * Fires when the dropdown list is expanded
11783              * @param {Roo.bootstrap.ComboBox} combo This combo box
11784              */
11785         'expand' : true,
11786         /**
11787          * @event collapse
11788          * Fires when the dropdown list is collapsed
11789              * @param {Roo.bootstrap.ComboBox} combo This combo box
11790              */
11791         'collapse' : true,
11792         /**
11793          * @event beforeselect
11794          * Fires before a list item is selected. Return false to cancel the selection.
11795              * @param {Roo.bootstrap.ComboBox} combo This combo box
11796              * @param {Roo.data.Record} record The data record returned from the underlying store
11797              * @param {Number} index The index of the selected item in the dropdown list
11798              */
11799         'beforeselect' : true,
11800         /**
11801          * @event select
11802          * Fires when a list item is selected
11803              * @param {Roo.bootstrap.ComboBox} combo This combo box
11804              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11805              * @param {Number} index The index of the selected item in the dropdown list
11806              */
11807         'select' : true,
11808         /**
11809          * @event beforequery
11810          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11811          * The event object passed has these properties:
11812              * @param {Roo.bootstrap.ComboBox} combo This combo box
11813              * @param {String} query The query
11814              * @param {Boolean} forceAll true to force "all" query
11815              * @param {Boolean} cancel true to cancel the query
11816              * @param {Object} e The query event object
11817              */
11818         'beforequery': true,
11819          /**
11820          * @event add
11821          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11822              * @param {Roo.bootstrap.ComboBox} combo This combo box
11823              */
11824         'add' : true,
11825         /**
11826          * @event edit
11827          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11828              * @param {Roo.bootstrap.ComboBox} combo This combo box
11829              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11830              */
11831         'edit' : true,
11832         /**
11833          * @event remove
11834          * Fires when the remove value from the combobox array
11835              * @param {Roo.bootstrap.ComboBox} combo This combo box
11836              */
11837         'remove' : true,
11838         /**
11839          * @event afterremove
11840          * Fires when the remove value from the combobox array
11841              * @param {Roo.bootstrap.ComboBox} combo This combo box
11842              */
11843         'afterremove' : true,
11844         /**
11845          * @event specialfilter
11846          * Fires when specialfilter
11847             * @param {Roo.bootstrap.ComboBox} combo This combo box
11848             */
11849         'specialfilter' : true,
11850         /**
11851          * @event tick
11852          * Fires when tick the element
11853             * @param {Roo.bootstrap.ComboBox} combo This combo box
11854             */
11855         'tick' : true,
11856         /**
11857          * @event touchviewdisplay
11858          * Fires when touch view require special display (default is using displayField)
11859             * @param {Roo.bootstrap.ComboBox} combo This combo box
11860             * @param {Object} cfg set html .
11861             */
11862         'touchviewdisplay' : true
11863         
11864     });
11865     
11866     this.item = [];
11867     this.tickItems = [];
11868     
11869     this.selectedIndex = -1;
11870     if(this.mode == 'local'){
11871         if(config.queryDelay === undefined){
11872             this.queryDelay = 10;
11873         }
11874         if(config.minChars === undefined){
11875             this.minChars = 0;
11876         }
11877     }
11878 };
11879
11880 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11881      
11882     /**
11883      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11884      * rendering into an Roo.Editor, defaults to false)
11885      */
11886     /**
11887      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11888      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11889      */
11890     /**
11891      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11892      */
11893     /**
11894      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11895      * the dropdown list (defaults to undefined, with no header element)
11896      */
11897
11898      /**
11899      * @cfg {String/Roo.Template} tpl The template to use to render the output
11900      */
11901      
11902      /**
11903      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11904      */
11905     listWidth: undefined,
11906     /**
11907      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11908      * mode = 'remote' or 'text' if mode = 'local')
11909      */
11910     displayField: undefined,
11911     
11912     /**
11913      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11914      * mode = 'remote' or 'value' if mode = 'local'). 
11915      * Note: use of a valueField requires the user make a selection
11916      * in order for a value to be mapped.
11917      */
11918     valueField: undefined,
11919     
11920     
11921     /**
11922      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11923      * field's data value (defaults to the underlying DOM element's name)
11924      */
11925     hiddenName: undefined,
11926     /**
11927      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11928      */
11929     listClass: '',
11930     /**
11931      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11932      */
11933     selectedClass: 'active',
11934     
11935     /**
11936      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11937      */
11938     shadow:'sides',
11939     /**
11940      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11941      * anchor positions (defaults to 'tl-bl')
11942      */
11943     listAlign: 'tl-bl?',
11944     /**
11945      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11946      */
11947     maxHeight: 300,
11948     /**
11949      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11950      * query specified by the allQuery config option (defaults to 'query')
11951      */
11952     triggerAction: 'query',
11953     /**
11954      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11955      * (defaults to 4, does not apply if editable = false)
11956      */
11957     minChars : 4,
11958     /**
11959      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11960      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11961      */
11962     typeAhead: false,
11963     /**
11964      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11965      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11966      */
11967     queryDelay: 500,
11968     /**
11969      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11970      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11971      */
11972     pageSize: 0,
11973     /**
11974      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11975      * when editable = true (defaults to false)
11976      */
11977     selectOnFocus:false,
11978     /**
11979      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11980      */
11981     queryParam: 'query',
11982     /**
11983      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11984      * when mode = 'remote' (defaults to 'Loading...')
11985      */
11986     loadingText: 'Loading...',
11987     /**
11988      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11989      */
11990     resizable: false,
11991     /**
11992      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11993      */
11994     handleHeight : 8,
11995     /**
11996      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11997      * traditional select (defaults to true)
11998      */
11999     editable: true,
12000     /**
12001      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12002      */
12003     allQuery: '',
12004     /**
12005      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12006      */
12007     mode: 'remote',
12008     /**
12009      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12010      * listWidth has a higher value)
12011      */
12012     minListWidth : 70,
12013     /**
12014      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12015      * allow the user to set arbitrary text into the field (defaults to false)
12016      */
12017     forceSelection:false,
12018     /**
12019      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12020      * if typeAhead = true (defaults to 250)
12021      */
12022     typeAheadDelay : 250,
12023     /**
12024      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12025      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12026      */
12027     valueNotFoundText : undefined,
12028     /**
12029      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12030      */
12031     blockFocus : false,
12032     
12033     /**
12034      * @cfg {Boolean} disableClear Disable showing of clear button.
12035      */
12036     disableClear : false,
12037     /**
12038      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12039      */
12040     alwaysQuery : false,
12041     
12042     /**
12043      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12044      */
12045     multiple : false,
12046     
12047     /**
12048      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12049      */
12050     invalidClass : "has-warning",
12051     
12052     /**
12053      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12054      */
12055     validClass : "has-success",
12056     
12057     /**
12058      * @cfg {Boolean} specialFilter (true|false) special filter default false
12059      */
12060     specialFilter : false,
12061     
12062     /**
12063      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12064      */
12065     mobileTouchView : true,
12066     
12067     //private
12068     addicon : false,
12069     editicon: false,
12070     
12071     page: 0,
12072     hasQuery: false,
12073     append: false,
12074     loadNext: false,
12075     autoFocus : true,
12076     tickable : false,
12077     btnPosition : 'right',
12078     triggerList : true,
12079     showToggleBtn : true,
12080     animate : true,
12081     emptyResultText: 'Empty',
12082     triggerText : 'Select',
12083     
12084     // element that contains real text value.. (when hidden is used..)
12085     
12086     getAutoCreate : function()
12087     {
12088         var cfg = false;
12089         
12090         /*
12091          * Touch Devices
12092          */
12093         
12094         if(Roo.isTouch && this.mobileTouchView){
12095             cfg = this.getAutoCreateTouchView();
12096             return cfg;;
12097         }
12098         
12099         /*
12100          *  Normal ComboBox
12101          */
12102         if(!this.tickable){
12103             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12104             return cfg;
12105         }
12106         
12107         /*
12108          *  ComboBox with tickable selections
12109          */
12110              
12111         var align = this.labelAlign || this.parentLabelAlign();
12112         
12113         cfg = {
12114             cls : 'form-group roo-combobox-tickable' //input-group
12115         };
12116         
12117         var buttons = {
12118             tag : 'div',
12119             cls : 'tickable-buttons',
12120             cn : [
12121                 {
12122                     tag : 'button',
12123                     type : 'button',
12124                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12125                     html : this.triggerText
12126                 },
12127                 {
12128                     tag : 'button',
12129                     type : 'button',
12130                     name : 'ok',
12131                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12132                     html : 'Done'
12133                 },
12134                 {
12135                     tag : 'button',
12136                     type : 'button',
12137                     name : 'cancel',
12138                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12139                     html : 'Cancel'
12140                 }
12141             ]
12142         };
12143         
12144         if(this.editable){
12145             buttons.cn.unshift({
12146                 tag: 'input',
12147                 cls: 'roo-select2-search-field-input'
12148             });
12149         }
12150         
12151         var _this = this;
12152         
12153         Roo.each(buttons.cn, function(c){
12154             if (_this.size) {
12155                 c.cls += ' btn-' + _this.size;
12156             }
12157
12158             if (_this.disabled) {
12159                 c.disabled = true;
12160             }
12161         });
12162         
12163         var box = {
12164             tag: 'div',
12165             cn: [
12166                 {
12167                     tag: 'input',
12168                     type : 'hidden',
12169                     cls: 'form-hidden-field'
12170                 },
12171                 {
12172                     tag: 'ul',
12173                     cls: 'roo-select2-choices',
12174                     cn:[
12175                         {
12176                             tag: 'li',
12177                             cls: 'roo-select2-search-field',
12178                             cn: [
12179
12180                                 buttons
12181                             ]
12182                         }
12183                     ]
12184                 }
12185             ]
12186         };
12187         
12188         var combobox = {
12189             cls: 'roo-select2-container input-group roo-select2-container-multi',
12190             cn: [
12191                 box
12192 //                {
12193 //                    tag: 'ul',
12194 //                    cls: 'typeahead typeahead-long dropdown-menu',
12195 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12196 //                }
12197             ]
12198         };
12199         
12200         if(this.hasFeedback && !this.allowBlank){
12201             
12202             var feedback = {
12203                 tag: 'span',
12204                 cls: 'glyphicon form-control-feedback'
12205             };
12206
12207             combobox.cn.push(feedback);
12208         }
12209         
12210         if (align ==='left' && this.fieldLabel.length) {
12211             
12212 //                Roo.log("left and has label");
12213                 cfg.cn = [
12214                     
12215                     {
12216                         tag: 'label',
12217                         'for' :  id,
12218                         cls : 'control-label col-sm-' + this.labelWidth,
12219                         html : this.fieldLabel
12220                         
12221                     },
12222                     {
12223                         cls : "col-sm-" + (12 - this.labelWidth), 
12224                         cn: [
12225                             combobox
12226                         ]
12227                     }
12228                     
12229                 ];
12230         } else if ( this.fieldLabel.length) {
12231 //                Roo.log(" label");
12232                  cfg.cn = [
12233                    
12234                     {
12235                         tag: 'label',
12236                         //cls : 'input-group-addon',
12237                         html : this.fieldLabel
12238                         
12239                     },
12240                     
12241                     combobox
12242                     
12243                 ];
12244
12245         } else {
12246             
12247 //                Roo.log(" no label && no align");
12248                 cfg = combobox
12249                      
12250                 
12251         }
12252          
12253         var settings=this;
12254         ['xs','sm','md','lg'].map(function(size){
12255             if (settings[size]) {
12256                 cfg.cls += ' col-' + size + '-' + settings[size];
12257             }
12258         });
12259         
12260         return cfg;
12261         
12262     },
12263     
12264     _initEventsCalled : false,
12265     
12266     // private
12267     initEvents: function()
12268     {
12269         
12270         if (this._initEventsCalled) { // as we call render... prevent looping...
12271             return;
12272         }
12273         this._initEventsCalled = true;
12274         
12275         if (!this.store) {
12276             throw "can not find store for combo";
12277         }
12278         
12279         this.store = Roo.factory(this.store, Roo.data);
12280         
12281         // if we are building from html. then this element is so complex, that we can not really
12282         // use the rendered HTML.
12283         // so we have to trash and replace the previous code.
12284         if (Roo.XComponent.build_from_html) {
12285             
12286             // remove this element....
12287             var e = this.el.dom, k=0;
12288             while (e ) { e = e.previousSibling;  ++k;}
12289
12290             this.el.remove();
12291             
12292             this.el=false;
12293             this.rendered = false;
12294             
12295             this.render(this.parent().getChildContainer(true), k);
12296             
12297             
12298             
12299         }
12300         
12301         
12302         /*
12303          * Touch Devices
12304          */
12305         
12306         if(Roo.isTouch && this.mobileTouchView){
12307             this.initTouchView();
12308             return;
12309         }
12310         
12311         if(this.tickable){
12312             this.initTickableEvents();
12313             return;
12314         }
12315         
12316         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12317         
12318         if(this.hiddenName){
12319             
12320             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12321             
12322             this.hiddenField.dom.value =
12323                 this.hiddenValue !== undefined ? this.hiddenValue :
12324                 this.value !== undefined ? this.value : '';
12325
12326             // prevent input submission
12327             this.el.dom.removeAttribute('name');
12328             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12329              
12330              
12331         }
12332         //if(Roo.isGecko){
12333         //    this.el.dom.setAttribute('autocomplete', 'off');
12334         //}
12335         
12336         var cls = 'x-combo-list';
12337         
12338         //this.list = new Roo.Layer({
12339         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12340         //});
12341         
12342         var _this = this;
12343         
12344         (function(){
12345             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12346             _this.list.setWidth(lw);
12347         }).defer(100);
12348         
12349         this.list.on('mouseover', this.onViewOver, this);
12350         this.list.on('mousemove', this.onViewMove, this);
12351         
12352         this.list.on('scroll', this.onViewScroll, this);
12353         
12354         /*
12355         this.list.swallowEvent('mousewheel');
12356         this.assetHeight = 0;
12357
12358         if(this.title){
12359             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12360             this.assetHeight += this.header.getHeight();
12361         }
12362
12363         this.innerList = this.list.createChild({cls:cls+'-inner'});
12364         this.innerList.on('mouseover', this.onViewOver, this);
12365         this.innerList.on('mousemove', this.onViewMove, this);
12366         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12367         
12368         if(this.allowBlank && !this.pageSize && !this.disableClear){
12369             this.footer = this.list.createChild({cls:cls+'-ft'});
12370             this.pageTb = new Roo.Toolbar(this.footer);
12371            
12372         }
12373         if(this.pageSize){
12374             this.footer = this.list.createChild({cls:cls+'-ft'});
12375             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12376                     {pageSize: this.pageSize});
12377             
12378         }
12379         
12380         if (this.pageTb && this.allowBlank && !this.disableClear) {
12381             var _this = this;
12382             this.pageTb.add(new Roo.Toolbar.Fill(), {
12383                 cls: 'x-btn-icon x-btn-clear',
12384                 text: '&#160;',
12385                 handler: function()
12386                 {
12387                     _this.collapse();
12388                     _this.clearValue();
12389                     _this.onSelect(false, -1);
12390                 }
12391             });
12392         }
12393         if (this.footer) {
12394             this.assetHeight += this.footer.getHeight();
12395         }
12396         */
12397             
12398         if(!this.tpl){
12399             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12400         }
12401
12402         this.view = new Roo.View(this.list, this.tpl, {
12403             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12404         });
12405         //this.view.wrapEl.setDisplayed(false);
12406         this.view.on('click', this.onViewClick, this);
12407         
12408         
12409         
12410         this.store.on('beforeload', this.onBeforeLoad, this);
12411         this.store.on('load', this.onLoad, this);
12412         this.store.on('loadexception', this.onLoadException, this);
12413         /*
12414         if(this.resizable){
12415             this.resizer = new Roo.Resizable(this.list,  {
12416                pinned:true, handles:'se'
12417             });
12418             this.resizer.on('resize', function(r, w, h){
12419                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12420                 this.listWidth = w;
12421                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12422                 this.restrictHeight();
12423             }, this);
12424             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12425         }
12426         */
12427         if(!this.editable){
12428             this.editable = true;
12429             this.setEditable(false);
12430         }
12431         
12432         /*
12433         
12434         if (typeof(this.events.add.listeners) != 'undefined') {
12435             
12436             this.addicon = this.wrap.createChild(
12437                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12438        
12439             this.addicon.on('click', function(e) {
12440                 this.fireEvent('add', this);
12441             }, this);
12442         }
12443         if (typeof(this.events.edit.listeners) != 'undefined') {
12444             
12445             this.editicon = this.wrap.createChild(
12446                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12447             if (this.addicon) {
12448                 this.editicon.setStyle('margin-left', '40px');
12449             }
12450             this.editicon.on('click', function(e) {
12451                 
12452                 // we fire even  if inothing is selected..
12453                 this.fireEvent('edit', this, this.lastData );
12454                 
12455             }, this);
12456         }
12457         */
12458         
12459         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12460             "up" : function(e){
12461                 this.inKeyMode = true;
12462                 this.selectPrev();
12463             },
12464
12465             "down" : function(e){
12466                 if(!this.isExpanded()){
12467                     this.onTriggerClick();
12468                 }else{
12469                     this.inKeyMode = true;
12470                     this.selectNext();
12471                 }
12472             },
12473
12474             "enter" : function(e){
12475 //                this.onViewClick();
12476                 //return true;
12477                 this.collapse();
12478                 
12479                 if(this.fireEvent("specialkey", this, e)){
12480                     this.onViewClick(false);
12481                 }
12482                 
12483                 return true;
12484             },
12485
12486             "esc" : function(e){
12487                 this.collapse();
12488             },
12489
12490             "tab" : function(e){
12491                 this.collapse();
12492                 
12493                 if(this.fireEvent("specialkey", this, e)){
12494                     this.onViewClick(false);
12495                 }
12496                 
12497                 return true;
12498             },
12499
12500             scope : this,
12501
12502             doRelay : function(foo, bar, hname){
12503                 if(hname == 'down' || this.scope.isExpanded()){
12504                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12505                 }
12506                 return true;
12507             },
12508
12509             forceKeyDown: true
12510         });
12511         
12512         
12513         this.queryDelay = Math.max(this.queryDelay || 10,
12514                 this.mode == 'local' ? 10 : 250);
12515         
12516         
12517         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12518         
12519         if(this.typeAhead){
12520             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12521         }
12522         if(this.editable !== false){
12523             this.inputEl().on("keyup", this.onKeyUp, this);
12524         }
12525         if(this.forceSelection){
12526             this.inputEl().on('blur', this.doForce, this);
12527         }
12528         
12529         if(this.multiple){
12530             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12531             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12532         }
12533     },
12534     
12535     initTickableEvents: function()
12536     {   
12537         this.createList();
12538         
12539         if(this.hiddenName){
12540             
12541             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12542             
12543             this.hiddenField.dom.value =
12544                 this.hiddenValue !== undefined ? this.hiddenValue :
12545                 this.value !== undefined ? this.value : '';
12546
12547             // prevent input submission
12548             this.el.dom.removeAttribute('name');
12549             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12550              
12551              
12552         }
12553         
12554 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12555         
12556         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12557         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12558         if(this.triggerList){
12559             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12560         }
12561          
12562         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12563         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12564         
12565         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12566         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12567         
12568         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12569         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12570         
12571         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12572         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12573         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12574         
12575         this.okBtn.hide();
12576         this.cancelBtn.hide();
12577         
12578         var _this = this;
12579         
12580         (function(){
12581             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12582             _this.list.setWidth(lw);
12583         }).defer(100);
12584         
12585         this.list.on('mouseover', this.onViewOver, this);
12586         this.list.on('mousemove', this.onViewMove, this);
12587         
12588         this.list.on('scroll', this.onViewScroll, this);
12589         
12590         if(!this.tpl){
12591             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>';
12592         }
12593
12594         this.view = new Roo.View(this.list, this.tpl, {
12595             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12596         });
12597         
12598         //this.view.wrapEl.setDisplayed(false);
12599         this.view.on('click', this.onViewClick, this);
12600         
12601         
12602         
12603         this.store.on('beforeload', this.onBeforeLoad, this);
12604         this.store.on('load', this.onLoad, this);
12605         this.store.on('loadexception', this.onLoadException, this);
12606         
12607         if(this.editable){
12608             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12609                 "up" : function(e){
12610                     this.inKeyMode = true;
12611                     this.selectPrev();
12612                 },
12613
12614                 "down" : function(e){
12615                     this.inKeyMode = true;
12616                     this.selectNext();
12617                 },
12618
12619                 "enter" : function(e){
12620                     if(this.fireEvent("specialkey", this, e)){
12621                         this.onViewClick(false);
12622                     }
12623                     
12624                     return true;
12625                 },
12626
12627                 "esc" : function(e){
12628                     this.onTickableFooterButtonClick(e, false, false);
12629                 },
12630
12631                 "tab" : function(e){
12632                     this.fireEvent("specialkey", this, e);
12633                     
12634                     this.onTickableFooterButtonClick(e, false, false);
12635                     
12636                     return true;
12637                 },
12638
12639                 scope : this,
12640
12641                 doRelay : function(e, fn, key){
12642                     if(this.scope.isExpanded()){
12643                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12644                     }
12645                     return true;
12646                 },
12647
12648                 forceKeyDown: true
12649             });
12650         }
12651         
12652         this.queryDelay = Math.max(this.queryDelay || 10,
12653                 this.mode == 'local' ? 10 : 250);
12654         
12655         
12656         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12657         
12658         if(this.typeAhead){
12659             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12660         }
12661         
12662         if(this.editable !== false){
12663             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12664         }
12665         
12666     },
12667
12668     onDestroy : function(){
12669         if(this.view){
12670             this.view.setStore(null);
12671             this.view.el.removeAllListeners();
12672             this.view.el.remove();
12673             this.view.purgeListeners();
12674         }
12675         if(this.list){
12676             this.list.dom.innerHTML  = '';
12677         }
12678         
12679         if(this.store){
12680             this.store.un('beforeload', this.onBeforeLoad, this);
12681             this.store.un('load', this.onLoad, this);
12682             this.store.un('loadexception', this.onLoadException, this);
12683         }
12684         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12685     },
12686
12687     // private
12688     fireKey : function(e){
12689         if(e.isNavKeyPress() && !this.list.isVisible()){
12690             this.fireEvent("specialkey", this, e);
12691         }
12692     },
12693
12694     // private
12695     onResize: function(w, h){
12696 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12697 //        
12698 //        if(typeof w != 'number'){
12699 //            // we do not handle it!?!?
12700 //            return;
12701 //        }
12702 //        var tw = this.trigger.getWidth();
12703 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12704 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12705 //        var x = w - tw;
12706 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12707 //            
12708 //        //this.trigger.setStyle('left', x+'px');
12709 //        
12710 //        if(this.list && this.listWidth === undefined){
12711 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12712 //            this.list.setWidth(lw);
12713 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12714 //        }
12715         
12716     
12717         
12718     },
12719
12720     /**
12721      * Allow or prevent the user from directly editing the field text.  If false is passed,
12722      * the user will only be able to select from the items defined in the dropdown list.  This method
12723      * is the runtime equivalent of setting the 'editable' config option at config time.
12724      * @param {Boolean} value True to allow the user to directly edit the field text
12725      */
12726     setEditable : function(value){
12727         if(value == this.editable){
12728             return;
12729         }
12730         this.editable = value;
12731         if(!value){
12732             this.inputEl().dom.setAttribute('readOnly', true);
12733             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12734             this.inputEl().addClass('x-combo-noedit');
12735         }else{
12736             this.inputEl().dom.setAttribute('readOnly', false);
12737             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12738             this.inputEl().removeClass('x-combo-noedit');
12739         }
12740     },
12741
12742     // private
12743     
12744     onBeforeLoad : function(combo,opts){
12745         if(!this.hasFocus){
12746             return;
12747         }
12748          if (!opts.add) {
12749             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12750          }
12751         this.restrictHeight();
12752         this.selectedIndex = -1;
12753     },
12754
12755     // private
12756     onLoad : function(){
12757         
12758         this.hasQuery = false;
12759         
12760         if(!this.hasFocus){
12761             return;
12762         }
12763         
12764         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12765             this.loading.hide();
12766         }
12767              
12768         if(this.store.getCount() > 0){
12769             this.expand();
12770             this.restrictHeight();
12771             if(this.lastQuery == this.allQuery){
12772                 if(this.editable && !this.tickable){
12773                     this.inputEl().dom.select();
12774                 }
12775                 
12776                 if(
12777                     !this.selectByValue(this.value, true) &&
12778                     this.autoFocus && 
12779                     (
12780                         !this.store.lastOptions ||
12781                         typeof(this.store.lastOptions.add) == 'undefined' || 
12782                         this.store.lastOptions.add != true
12783                     )
12784                 ){
12785                     this.select(0, true);
12786                 }
12787             }else{
12788                 if(this.autoFocus){
12789                     this.selectNext();
12790                 }
12791                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12792                     this.taTask.delay(this.typeAheadDelay);
12793                 }
12794             }
12795         }else{
12796             this.onEmptyResults();
12797         }
12798         
12799         //this.el.focus();
12800     },
12801     // private
12802     onLoadException : function()
12803     {
12804         this.hasQuery = false;
12805         
12806         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12807             this.loading.hide();
12808         }
12809         
12810         if(this.tickable && this.editable){
12811             return;
12812         }
12813         
12814         this.collapse();
12815         // only causes errors at present
12816         //Roo.log(this.store.reader.jsonData);
12817         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12818             // fixme
12819             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12820         //}
12821         
12822         
12823     },
12824     // private
12825     onTypeAhead : function(){
12826         if(this.store.getCount() > 0){
12827             var r = this.store.getAt(0);
12828             var newValue = r.data[this.displayField];
12829             var len = newValue.length;
12830             var selStart = this.getRawValue().length;
12831             
12832             if(selStart != len){
12833                 this.setRawValue(newValue);
12834                 this.selectText(selStart, newValue.length);
12835             }
12836         }
12837     },
12838
12839     // private
12840     onSelect : function(record, index){
12841         
12842         if(this.fireEvent('beforeselect', this, record, index) !== false){
12843         
12844             this.setFromData(index > -1 ? record.data : false);
12845             
12846             this.collapse();
12847             this.fireEvent('select', this, record, index);
12848         }
12849     },
12850
12851     /**
12852      * Returns the currently selected field value or empty string if no value is set.
12853      * @return {String} value The selected value
12854      */
12855     getValue : function(){
12856         
12857         if(this.multiple){
12858             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12859         }
12860         
12861         if(this.valueField){
12862             return typeof this.value != 'undefined' ? this.value : '';
12863         }else{
12864             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12865         }
12866     },
12867
12868     /**
12869      * Clears any text/value currently set in the field
12870      */
12871     clearValue : function(){
12872         if(this.hiddenField){
12873             this.hiddenField.dom.value = '';
12874         }
12875         this.value = '';
12876         this.setRawValue('');
12877         this.lastSelectionText = '';
12878         this.lastData = false;
12879         
12880         var close = this.closeTriggerEl();
12881         
12882         if(close){
12883             close.hide();
12884         }
12885         
12886     },
12887
12888     /**
12889      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12890      * will be displayed in the field.  If the value does not match the data value of an existing item,
12891      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12892      * Otherwise the field will be blank (although the value will still be set).
12893      * @param {String} value The value to match
12894      */
12895     setValue : function(v){
12896         if(this.multiple){
12897             this.syncValue();
12898             return;
12899         }
12900         
12901         var text = v;
12902         if(this.valueField){
12903             var r = this.findRecord(this.valueField, v);
12904             if(r){
12905                 text = r.data[this.displayField];
12906             }else if(this.valueNotFoundText !== undefined){
12907                 text = this.valueNotFoundText;
12908             }
12909         }
12910         this.lastSelectionText = text;
12911         if(this.hiddenField){
12912             this.hiddenField.dom.value = v;
12913         }
12914         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12915         this.value = v;
12916         
12917         var close = this.closeTriggerEl();
12918         
12919         if(close){
12920             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12921         }
12922     },
12923     /**
12924      * @property {Object} the last set data for the element
12925      */
12926     
12927     lastData : false,
12928     /**
12929      * Sets the value of the field based on a object which is related to the record format for the store.
12930      * @param {Object} value the value to set as. or false on reset?
12931      */
12932     setFromData : function(o){
12933         
12934         if(this.multiple){
12935             this.addItem(o);
12936             return;
12937         }
12938             
12939         var dv = ''; // display value
12940         var vv = ''; // value value..
12941         this.lastData = o;
12942         if (this.displayField) {
12943             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12944         } else {
12945             // this is an error condition!!!
12946             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12947         }
12948         
12949         if(this.valueField){
12950             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12951         }
12952         
12953         var close = this.closeTriggerEl();
12954         
12955         if(close){
12956             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12957         }
12958         
12959         if(this.hiddenField){
12960             this.hiddenField.dom.value = vv;
12961             
12962             this.lastSelectionText = dv;
12963             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12964             this.value = vv;
12965             return;
12966         }
12967         // no hidden field.. - we store the value in 'value', but still display
12968         // display field!!!!
12969         this.lastSelectionText = dv;
12970         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12971         this.value = vv;
12972         
12973         
12974         
12975     },
12976     // private
12977     reset : function(){
12978         // overridden so that last data is reset..
12979         
12980         if(this.multiple){
12981             this.clearItem();
12982             return;
12983         }
12984         
12985         this.setValue(this.originalValue);
12986         this.clearInvalid();
12987         this.lastData = false;
12988         if (this.view) {
12989             this.view.clearSelections();
12990         }
12991     },
12992     // private
12993     findRecord : function(prop, value){
12994         var record;
12995         if(this.store.getCount() > 0){
12996             this.store.each(function(r){
12997                 if(r.data[prop] == value){
12998                     record = r;
12999                     return false;
13000                 }
13001                 return true;
13002             });
13003         }
13004         return record;
13005     },
13006     
13007     getName: function()
13008     {
13009         // returns hidden if it's set..
13010         if (!this.rendered) {return ''};
13011         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13012         
13013     },
13014     // private
13015     onViewMove : function(e, t){
13016         this.inKeyMode = false;
13017     },
13018
13019     // private
13020     onViewOver : function(e, t){
13021         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13022             return;
13023         }
13024         var item = this.view.findItemFromChild(t);
13025         
13026         if(item){
13027             var index = this.view.indexOf(item);
13028             this.select(index, false);
13029         }
13030     },
13031
13032     // private
13033     onViewClick : function(view, doFocus, el, e)
13034     {
13035         var index = this.view.getSelectedIndexes()[0];
13036         
13037         var r = this.store.getAt(index);
13038         
13039         if(this.tickable){
13040             
13041             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13042                 return;
13043             }
13044             
13045             var rm = false;
13046             var _this = this;
13047             
13048             Roo.each(this.tickItems, function(v,k){
13049                 
13050                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13051                     Roo.log(v);
13052                     _this.tickItems.splice(k, 1);
13053                     
13054                     if(typeof(e) == 'undefined' && view == false){
13055                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13056                     }
13057                     
13058                     rm = true;
13059                     return;
13060                 }
13061             });
13062             
13063             if(rm){
13064                 return;
13065             }
13066             
13067             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13068                 this.tickItems.push(r.data);
13069             }
13070             
13071             if(typeof(e) == 'undefined' && view == false){
13072                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13073             }
13074                     
13075             return;
13076         }
13077         
13078         if(r){
13079             this.onSelect(r, index);
13080         }
13081         if(doFocus !== false && !this.blockFocus){
13082             this.inputEl().focus();
13083         }
13084     },
13085
13086     // private
13087     restrictHeight : function(){
13088         //this.innerList.dom.style.height = '';
13089         //var inner = this.innerList.dom;
13090         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13091         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13092         //this.list.beginUpdate();
13093         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13094         this.list.alignTo(this.inputEl(), this.listAlign);
13095         this.list.alignTo(this.inputEl(), this.listAlign);
13096         //this.list.endUpdate();
13097     },
13098
13099     // private
13100     onEmptyResults : function(){
13101         
13102         if(this.tickable && this.editable){
13103             this.restrictHeight();
13104             return;
13105         }
13106         
13107         this.collapse();
13108     },
13109
13110     /**
13111      * Returns true if the dropdown list is expanded, else false.
13112      */
13113     isExpanded : function(){
13114         return this.list.isVisible();
13115     },
13116
13117     /**
13118      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13119      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13120      * @param {String} value The data value of the item to select
13121      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13122      * selected item if it is not currently in view (defaults to true)
13123      * @return {Boolean} True if the value matched an item in the list, else false
13124      */
13125     selectByValue : function(v, scrollIntoView){
13126         if(v !== undefined && v !== null){
13127             var r = this.findRecord(this.valueField || this.displayField, v);
13128             if(r){
13129                 this.select(this.store.indexOf(r), scrollIntoView);
13130                 return true;
13131             }
13132         }
13133         return false;
13134     },
13135
13136     /**
13137      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13138      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13139      * @param {Number} index The zero-based index of the list item to select
13140      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13141      * selected item if it is not currently in view (defaults to true)
13142      */
13143     select : function(index, scrollIntoView){
13144         this.selectedIndex = index;
13145         this.view.select(index);
13146         if(scrollIntoView !== false){
13147             var el = this.view.getNode(index);
13148             /*
13149              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13150              */
13151             if(el){
13152                 this.list.scrollChildIntoView(el, false);
13153             }
13154         }
13155     },
13156
13157     // private
13158     selectNext : function(){
13159         var ct = this.store.getCount();
13160         if(ct > 0){
13161             if(this.selectedIndex == -1){
13162                 this.select(0);
13163             }else if(this.selectedIndex < ct-1){
13164                 this.select(this.selectedIndex+1);
13165             }
13166         }
13167     },
13168
13169     // private
13170     selectPrev : function(){
13171         var ct = this.store.getCount();
13172         if(ct > 0){
13173             if(this.selectedIndex == -1){
13174                 this.select(0);
13175             }else if(this.selectedIndex != 0){
13176                 this.select(this.selectedIndex-1);
13177             }
13178         }
13179     },
13180
13181     // private
13182     onKeyUp : function(e){
13183         if(this.editable !== false && !e.isSpecialKey()){
13184             this.lastKey = e.getKey();
13185             this.dqTask.delay(this.queryDelay);
13186         }
13187     },
13188
13189     // private
13190     validateBlur : function(){
13191         return !this.list || !this.list.isVisible();   
13192     },
13193
13194     // private
13195     initQuery : function(){
13196         
13197         var v = this.getRawValue();
13198         
13199         if(this.tickable && this.editable){
13200             v = this.tickableInputEl().getValue();
13201         }
13202         
13203         this.doQuery(v);
13204     },
13205
13206     // private
13207     doForce : function(){
13208         if(this.inputEl().dom.value.length > 0){
13209             this.inputEl().dom.value =
13210                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13211              
13212         }
13213     },
13214
13215     /**
13216      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13217      * query allowing the query action to be canceled if needed.
13218      * @param {String} query The SQL query to execute
13219      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13220      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13221      * saved in the current store (defaults to false)
13222      */
13223     doQuery : function(q, forceAll){
13224         
13225         if(q === undefined || q === null){
13226             q = '';
13227         }
13228         var qe = {
13229             query: q,
13230             forceAll: forceAll,
13231             combo: this,
13232             cancel:false
13233         };
13234         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13235             return false;
13236         }
13237         q = qe.query;
13238         
13239         forceAll = qe.forceAll;
13240         if(forceAll === true || (q.length >= this.minChars)){
13241             
13242             this.hasQuery = true;
13243             
13244             if(this.lastQuery != q || this.alwaysQuery){
13245                 this.lastQuery = q;
13246                 if(this.mode == 'local'){
13247                     this.selectedIndex = -1;
13248                     if(forceAll){
13249                         this.store.clearFilter();
13250                     }else{
13251                         
13252                         if(this.specialFilter){
13253                             this.fireEvent('specialfilter', this);
13254                             this.onLoad();
13255                             return;
13256                         }
13257                         
13258                         this.store.filter(this.displayField, q);
13259                     }
13260                     
13261                     this.store.fireEvent("datachanged", this.store);
13262                     
13263                     this.onLoad();
13264                     
13265                     
13266                 }else{
13267                     
13268                     this.store.baseParams[this.queryParam] = q;
13269                     
13270                     var options = {params : this.getParams(q)};
13271                     
13272                     if(this.loadNext){
13273                         options.add = true;
13274                         options.params.start = this.page * this.pageSize;
13275                     }
13276                     
13277                     this.store.load(options);
13278                     
13279                     /*
13280                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13281                      *  we should expand the list on onLoad
13282                      *  so command out it
13283                      */
13284 //                    this.expand();
13285                 }
13286             }else{
13287                 this.selectedIndex = -1;
13288                 this.onLoad();   
13289             }
13290         }
13291         
13292         this.loadNext = false;
13293     },
13294     
13295     // private
13296     getParams : function(q){
13297         var p = {};
13298         //p[this.queryParam] = q;
13299         
13300         if(this.pageSize){
13301             p.start = 0;
13302             p.limit = this.pageSize;
13303         }
13304         return p;
13305     },
13306
13307     /**
13308      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13309      */
13310     collapse : function(){
13311         if(!this.isExpanded()){
13312             return;
13313         }
13314         
13315         this.list.hide();
13316         
13317         if(this.tickable){
13318             this.hasFocus = false;
13319             this.okBtn.hide();
13320             this.cancelBtn.hide();
13321             this.trigger.show();
13322             
13323             if(this.editable){
13324                 this.tickableInputEl().dom.value = '';
13325                 this.tickableInputEl().blur();
13326             }
13327             
13328         }
13329         
13330         Roo.get(document).un('mousedown', this.collapseIf, this);
13331         Roo.get(document).un('mousewheel', this.collapseIf, this);
13332         if (!this.editable) {
13333             Roo.get(document).un('keydown', this.listKeyPress, this);
13334         }
13335         this.fireEvent('collapse', this);
13336     },
13337
13338     // private
13339     collapseIf : function(e){
13340         var in_combo  = e.within(this.el);
13341         var in_list =  e.within(this.list);
13342         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13343         
13344         if (in_combo || in_list || is_list) {
13345             //e.stopPropagation();
13346             return;
13347         }
13348         
13349         if(this.tickable){
13350             this.onTickableFooterButtonClick(e, false, false);
13351         }
13352
13353         this.collapse();
13354         
13355     },
13356
13357     /**
13358      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13359      */
13360     expand : function(){
13361        
13362         if(this.isExpanded() || !this.hasFocus){
13363             return;
13364         }
13365         
13366         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13367         this.list.setWidth(lw);
13368         
13369         
13370          Roo.log('expand');
13371         
13372         this.list.show();
13373         
13374         this.restrictHeight();
13375         
13376         if(this.tickable){
13377             
13378             this.tickItems = Roo.apply([], this.item);
13379             
13380             this.okBtn.show();
13381             this.cancelBtn.show();
13382             this.trigger.hide();
13383             
13384             if(this.editable){
13385                 this.tickableInputEl().focus();
13386             }
13387             
13388         }
13389         
13390         Roo.get(document).on('mousedown', this.collapseIf, this);
13391         Roo.get(document).on('mousewheel', this.collapseIf, this);
13392         if (!this.editable) {
13393             Roo.get(document).on('keydown', this.listKeyPress, this);
13394         }
13395         
13396         this.fireEvent('expand', this);
13397     },
13398
13399     // private
13400     // Implements the default empty TriggerField.onTriggerClick function
13401     onTriggerClick : function(e)
13402     {
13403         Roo.log('trigger click');
13404         
13405         if(this.disabled || !this.triggerList){
13406             return;
13407         }
13408         
13409         this.page = 0;
13410         this.loadNext = false;
13411         
13412         if(this.isExpanded()){
13413             this.collapse();
13414             if (!this.blockFocus) {
13415                 this.inputEl().focus();
13416             }
13417             
13418         }else {
13419             this.hasFocus = true;
13420             if(this.triggerAction == 'all') {
13421                 this.doQuery(this.allQuery, true);
13422             } else {
13423                 this.doQuery(this.getRawValue());
13424             }
13425             if (!this.blockFocus) {
13426                 this.inputEl().focus();
13427             }
13428         }
13429     },
13430     
13431     onTickableTriggerClick : function(e)
13432     {
13433         if(this.disabled){
13434             return;
13435         }
13436         
13437         this.page = 0;
13438         this.loadNext = false;
13439         this.hasFocus = true;
13440         
13441         if(this.triggerAction == 'all') {
13442             this.doQuery(this.allQuery, true);
13443         } else {
13444             this.doQuery(this.getRawValue());
13445         }
13446     },
13447     
13448     onSearchFieldClick : function(e)
13449     {
13450         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13451             this.onTickableFooterButtonClick(e, false, false);
13452             return;
13453         }
13454         
13455         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13456             return;
13457         }
13458         
13459         this.page = 0;
13460         this.loadNext = false;
13461         this.hasFocus = true;
13462         
13463         if(this.triggerAction == 'all') {
13464             this.doQuery(this.allQuery, true);
13465         } else {
13466             this.doQuery(this.getRawValue());
13467         }
13468     },
13469     
13470     listKeyPress : function(e)
13471     {
13472         //Roo.log('listkeypress');
13473         // scroll to first matching element based on key pres..
13474         if (e.isSpecialKey()) {
13475             return false;
13476         }
13477         var k = String.fromCharCode(e.getKey()).toUpperCase();
13478         //Roo.log(k);
13479         var match  = false;
13480         var csel = this.view.getSelectedNodes();
13481         var cselitem = false;
13482         if (csel.length) {
13483             var ix = this.view.indexOf(csel[0]);
13484             cselitem  = this.store.getAt(ix);
13485             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13486                 cselitem = false;
13487             }
13488             
13489         }
13490         
13491         this.store.each(function(v) { 
13492             if (cselitem) {
13493                 // start at existing selection.
13494                 if (cselitem.id == v.id) {
13495                     cselitem = false;
13496                 }
13497                 return true;
13498             }
13499                 
13500             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13501                 match = this.store.indexOf(v);
13502                 return false;
13503             }
13504             return true;
13505         }, this);
13506         
13507         if (match === false) {
13508             return true; // no more action?
13509         }
13510         // scroll to?
13511         this.view.select(match);
13512         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13513         sn.scrollIntoView(sn.dom.parentNode, false);
13514     },
13515     
13516     onViewScroll : function(e, t){
13517         
13518         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){
13519             return;
13520         }
13521         
13522         this.hasQuery = true;
13523         
13524         this.loading = this.list.select('.loading', true).first();
13525         
13526         if(this.loading === null){
13527             this.list.createChild({
13528                 tag: 'div',
13529                 cls: 'loading roo-select2-more-results roo-select2-active',
13530                 html: 'Loading more results...'
13531             });
13532             
13533             this.loading = this.list.select('.loading', true).first();
13534             
13535             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13536             
13537             this.loading.hide();
13538         }
13539         
13540         this.loading.show();
13541         
13542         var _combo = this;
13543         
13544         this.page++;
13545         this.loadNext = true;
13546         
13547         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13548         
13549         return;
13550     },
13551     
13552     addItem : function(o)
13553     {   
13554         var dv = ''; // display value
13555         
13556         if (this.displayField) {
13557             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13558         } else {
13559             // this is an error condition!!!
13560             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13561         }
13562         
13563         if(!dv.length){
13564             return;
13565         }
13566         
13567         var choice = this.choices.createChild({
13568             tag: 'li',
13569             cls: 'roo-select2-search-choice',
13570             cn: [
13571                 {
13572                     tag: 'div',
13573                     html: dv
13574                 },
13575                 {
13576                     tag: 'a',
13577                     href: '#',
13578                     cls: 'roo-select2-search-choice-close',
13579                     tabindex: '-1'
13580                 }
13581             ]
13582             
13583         }, this.searchField);
13584         
13585         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13586         
13587         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13588         
13589         this.item.push(o);
13590         
13591         this.lastData = o;
13592         
13593         this.syncValue();
13594         
13595         this.inputEl().dom.value = '';
13596         
13597         this.validate();
13598     },
13599     
13600     onRemoveItem : function(e, _self, o)
13601     {
13602         e.preventDefault();
13603         
13604         this.lastItem = Roo.apply([], this.item);
13605         
13606         var index = this.item.indexOf(o.data) * 1;
13607         
13608         if( index < 0){
13609             Roo.log('not this item?!');
13610             return;
13611         }
13612         
13613         this.item.splice(index, 1);
13614         o.item.remove();
13615         
13616         this.syncValue();
13617         
13618         this.fireEvent('remove', this, e);
13619         
13620         this.validate();
13621         
13622     },
13623     
13624     syncValue : function()
13625     {
13626         if(!this.item.length){
13627             this.clearValue();
13628             return;
13629         }
13630             
13631         var value = [];
13632         var _this = this;
13633         Roo.each(this.item, function(i){
13634             if(_this.valueField){
13635                 value.push(i[_this.valueField]);
13636                 return;
13637             }
13638
13639             value.push(i);
13640         });
13641
13642         this.value = value.join(',');
13643
13644         if(this.hiddenField){
13645             this.hiddenField.dom.value = this.value;
13646         }
13647         
13648         this.store.fireEvent("datachanged", this.store);
13649     },
13650     
13651     clearItem : function()
13652     {
13653         if(!this.multiple){
13654             return;
13655         }
13656         
13657         this.item = [];
13658         
13659         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13660            c.remove();
13661         });
13662         
13663         this.syncValue();
13664         
13665         this.validate();
13666         
13667         if(this.tickable && !Roo.isTouch){
13668             this.view.refresh();
13669         }
13670     },
13671     
13672     inputEl: function ()
13673     {
13674         if(Roo.isTouch && this.mobileTouchView){
13675             return this.el.select('input.form-control',true).first();
13676         }
13677         
13678         if(this.tickable){
13679             return this.searchField;
13680         }
13681         
13682         return this.el.select('input.form-control',true).first();
13683     },
13684     
13685     
13686     onTickableFooterButtonClick : function(e, btn, el)
13687     {
13688         e.preventDefault();
13689         
13690         this.lastItem = Roo.apply([], this.item);
13691         
13692         if(btn && btn.name == 'cancel'){
13693             this.tickItems = Roo.apply([], this.item);
13694             this.collapse();
13695             return;
13696         }
13697         
13698         this.clearItem();
13699         
13700         var _this = this;
13701         
13702         Roo.each(this.tickItems, function(o){
13703             _this.addItem(o);
13704         });
13705         
13706         this.collapse();
13707         
13708     },
13709     
13710     validate : function()
13711     {
13712         var v = this.getRawValue();
13713         
13714         if(this.multiple){
13715             v = this.getValue();
13716         }
13717         
13718         if(this.disabled || this.allowBlank || v.length){
13719             this.markValid();
13720             return true;
13721         }
13722         
13723         this.markInvalid();
13724         return false;
13725     },
13726     
13727     tickableInputEl : function()
13728     {
13729         if(!this.tickable || !this.editable){
13730             return this.inputEl();
13731         }
13732         
13733         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13734     },
13735     
13736     
13737     getAutoCreateTouchView : function()
13738     {
13739         var id = Roo.id();
13740         
13741         var cfg = {
13742             cls: 'form-group' //input-group
13743         };
13744         
13745         var input =  {
13746             tag: 'input',
13747             id : id,
13748             type : this.inputType,
13749             cls : 'form-control x-combo-noedit',
13750             autocomplete: 'new-password',
13751             placeholder : this.placeholder || '',
13752             readonly : true
13753         };
13754         
13755         if (this.name) {
13756             input.name = this.name;
13757         }
13758         
13759         if (this.size) {
13760             input.cls += ' input-' + this.size;
13761         }
13762         
13763         if (this.disabled) {
13764             input.disabled = true;
13765         }
13766         
13767         var inputblock = {
13768             cls : '',
13769             cn : [
13770                 input
13771             ]
13772         };
13773         
13774         if(this.before){
13775             inputblock.cls += ' input-group';
13776             
13777             inputblock.cn.unshift({
13778                 tag :'span',
13779                 cls : 'input-group-addon',
13780                 html : this.before
13781             });
13782         }
13783         
13784         if(this.removable && !this.multiple){
13785             inputblock.cls += ' roo-removable';
13786             
13787             inputblock.cn.push({
13788                 tag: 'button',
13789                 html : 'x',
13790                 cls : 'roo-combo-removable-btn close'
13791             });
13792         }
13793
13794         if(this.hasFeedback && !this.allowBlank){
13795             
13796             inputblock.cls += ' has-feedback';
13797             
13798             inputblock.cn.push({
13799                 tag: 'span',
13800                 cls: 'glyphicon form-control-feedback'
13801             });
13802             
13803         }
13804         
13805         if (this.after) {
13806             
13807             inputblock.cls += (this.before) ? '' : ' input-group';
13808             
13809             inputblock.cn.push({
13810                 tag :'span',
13811                 cls : 'input-group-addon',
13812                 html : this.after
13813             });
13814         }
13815
13816         var box = {
13817             tag: 'div',
13818             cn: [
13819                 {
13820                     tag: 'input',
13821                     type : 'hidden',
13822                     cls: 'form-hidden-field'
13823                 },
13824                 inputblock
13825             ]
13826             
13827         };
13828         
13829         if(this.multiple){
13830             box = {
13831                 tag: 'div',
13832                 cn: [
13833                     {
13834                         tag: 'input',
13835                         type : 'hidden',
13836                         cls: 'form-hidden-field'
13837                     },
13838                     {
13839                         tag: 'ul',
13840                         cls: 'roo-select2-choices',
13841                         cn:[
13842                             {
13843                                 tag: 'li',
13844                                 cls: 'roo-select2-search-field',
13845                                 cn: [
13846
13847                                     inputblock
13848                                 ]
13849                             }
13850                         ]
13851                     }
13852                 ]
13853             }
13854         };
13855         
13856         var combobox = {
13857             cls: 'roo-select2-container input-group',
13858             cn: [
13859                 box
13860             ]
13861         };
13862         
13863         if(this.multiple){
13864             combobox.cls += ' roo-select2-container-multi';
13865         }
13866         
13867         var align = this.labelAlign || this.parentLabelAlign();
13868         
13869         cfg.cn = combobox;
13870         
13871         if(this.fieldLabel.length){
13872             
13873             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13874             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13875             
13876             cfg.cn = [
13877                 {
13878                     tag: 'label',
13879                     cls : 'control-label ' + lw,
13880                     html : this.fieldLabel
13881
13882                 },
13883                 {
13884                     cls : cw, 
13885                     cn: [
13886                         combobox
13887                     ]
13888                 }
13889             ];
13890         }
13891         
13892         var settings = this;
13893         
13894         ['xs','sm','md','lg'].map(function(size){
13895             if (settings[size]) {
13896                 cfg.cls += ' col-' + size + '-' + settings[size];
13897             }
13898         });
13899         
13900         return cfg;
13901     },
13902     
13903     initTouchView : function()
13904     {
13905         this.renderTouchView();
13906         
13907         this.touchViewEl.on('scroll', function(){
13908             this.el.dom.scrollTop = 0;
13909         }, this);
13910         
13911         this.originalValue = this.getValue();
13912         
13913         this.inputEl().on("click", this.showTouchView, this);
13914         
13915         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13916         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13917         
13918         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13919         
13920         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13921         this.store.on('load', this.onTouchViewLoad, this);
13922         this.store.on('loadexception', this.onTouchViewLoadException, this);
13923         
13924         if(this.hiddenName){
13925             
13926             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13927             
13928             this.hiddenField.dom.value =
13929                 this.hiddenValue !== undefined ? this.hiddenValue :
13930                 this.value !== undefined ? this.value : '';
13931         
13932             this.el.dom.removeAttribute('name');
13933             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13934         }
13935         
13936         if(this.multiple){
13937             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13938             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13939         }
13940         
13941         if(this.removable && !this.multiple){
13942             var close = this.closeTriggerEl();
13943             if(close){
13944                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13945                 close.on('click', this.removeBtnClick, this, close);
13946             }
13947         }
13948         /*
13949          * fix the bug in Safari iOS8
13950          */
13951         this.inputEl().on("focus", function(e){
13952             document.activeElement.blur();
13953         }, this);
13954         
13955         return;
13956         
13957         
13958     },
13959     
13960     renderTouchView : function()
13961     {
13962         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13963         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13964         
13965         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13966         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13967         
13968         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13969         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13970         this.touchViewBodyEl.setStyle('overflow', 'auto');
13971         
13972         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13973         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13974         
13975         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13976         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13977         
13978     },
13979     
13980     showTouchView : function()
13981     {
13982         if(this.disabled){
13983             return;
13984         }
13985         
13986         this.touchViewHeaderEl.hide();
13987
13988         if(this.fieldLabel.length){
13989             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13990             this.touchViewHeaderEl.show();
13991         }
13992
13993         this.touchViewEl.show();
13994
13995         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13996         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13997
13998         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13999
14000         if(this.fieldLabel.length){
14001             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14002         }
14003         
14004         this.touchViewBodyEl.setHeight(bodyHeight);
14005
14006         if(this.animate){
14007             var _this = this;
14008             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14009         }else{
14010             this.touchViewEl.addClass('in');
14011         }
14012
14013         this.doTouchViewQuery();
14014         
14015     },
14016     
14017     hideTouchView : function()
14018     {
14019         this.touchViewEl.removeClass('in');
14020
14021         if(this.animate){
14022             var _this = this;
14023             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14024         }else{
14025             this.touchViewEl.setStyle('display', 'none');
14026         }
14027         
14028     },
14029     
14030     setTouchViewValue : function()
14031     {
14032         if(this.multiple){
14033             this.clearItem();
14034         
14035             var _this = this;
14036
14037             Roo.each(this.tickItems, function(o){
14038                 this.addItem(o);
14039             }, this);
14040         }
14041         
14042         this.hideTouchView();
14043     },
14044     
14045     doTouchViewQuery : function()
14046     {
14047         var qe = {
14048             query: '',
14049             forceAll: true,
14050             combo: this,
14051             cancel:false
14052         };
14053         
14054         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14055             return false;
14056         }
14057         
14058         if(!this.alwaysQuery || this.mode == 'local'){
14059             this.onTouchViewLoad();
14060             return;
14061         }
14062         
14063         this.store.load();
14064     },
14065     
14066     onTouchViewBeforeLoad : function(combo,opts)
14067     {
14068         return;
14069     },
14070
14071     // private
14072     onTouchViewLoad : function()
14073     {
14074         if(this.store.getCount() < 1){
14075             this.onTouchViewEmptyResults();
14076             return;
14077         }
14078         
14079         this.clearTouchView();
14080         
14081         var rawValue = this.getRawValue();
14082         
14083         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14084         
14085         this.tickItems = [];
14086         
14087         this.store.data.each(function(d, rowIndex){
14088             var row = this.touchViewListGroup.createChild(template);
14089             
14090             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14091                 row.addClass(d.data.cls);
14092             }
14093             
14094             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14095                 var cfg = {
14096                     data : d.data,
14097                     html : d.data[this.displayField]
14098                 };
14099                 
14100                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14101                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14102                 }
14103             }
14104             
14105             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
14106                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14107             }
14108             
14109             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
14110                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14111                 this.tickItems.push(d.data);
14112             }
14113             
14114             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14115             
14116         }, this);
14117         
14118         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14119         
14120         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14121
14122         if(this.fieldLabel.length){
14123             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14124         }
14125
14126         var listHeight = this.touchViewListGroup.getHeight();
14127         
14128         var _this = this;
14129         
14130         if(firstChecked && listHeight > bodyHeight){
14131             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14132         }
14133         
14134     },
14135     
14136     onTouchViewLoadException : function()
14137     {
14138         this.hideTouchView();
14139     },
14140     
14141     onTouchViewEmptyResults : function()
14142     {
14143         this.clearTouchView();
14144         
14145         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14146         
14147         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14148         
14149     },
14150     
14151     clearTouchView : function()
14152     {
14153         this.touchViewListGroup.dom.innerHTML = '';
14154     },
14155     
14156     onTouchViewClick : function(e, el, o)
14157     {
14158         e.preventDefault();
14159         
14160         var row = o.row;
14161         var rowIndex = o.rowIndex;
14162         
14163         var r = this.store.getAt(rowIndex);
14164         
14165         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14166             
14167             if(!this.multiple){
14168                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14169                     c.dom.removeAttribute('checked');
14170                 }, this);
14171
14172                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14173
14174                 this.setFromData(r.data);
14175
14176                 var close = this.closeTriggerEl();
14177
14178                 if(close){
14179                     close.show();
14180                 }
14181
14182                 this.hideTouchView();
14183
14184                 this.fireEvent('select', this, r, rowIndex);
14185
14186                 return;
14187             }
14188
14189             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14190                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14191                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14192                 return;
14193             }
14194
14195             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14196             this.addItem(r.data);
14197             this.tickItems.push(r.data);
14198         }
14199     }
14200     
14201
14202     /** 
14203     * @cfg {Boolean} grow 
14204     * @hide 
14205     */
14206     /** 
14207     * @cfg {Number} growMin 
14208     * @hide 
14209     */
14210     /** 
14211     * @cfg {Number} growMax 
14212     * @hide 
14213     */
14214     /**
14215      * @hide
14216      * @method autoSize
14217      */
14218 });
14219
14220 Roo.apply(Roo.bootstrap.ComboBox,  {
14221     
14222     header : {
14223         tag: 'div',
14224         cls: 'modal-header',
14225         cn: [
14226             {
14227                 tag: 'h4',
14228                 cls: 'modal-title'
14229             }
14230         ]
14231     },
14232     
14233     body : {
14234         tag: 'div',
14235         cls: 'modal-body',
14236         cn: [
14237             {
14238                 tag: 'ul',
14239                 cls: 'list-group'
14240             }
14241         ]
14242     },
14243     
14244     listItemRadio : {
14245         tag: 'li',
14246         cls: 'list-group-item',
14247         cn: [
14248             {
14249                 tag: 'span',
14250                 cls: 'roo-combobox-list-group-item-value'
14251             },
14252             {
14253                 tag: 'div',
14254                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14255                 cn: [
14256                     {
14257                         tag: 'input',
14258                         type: 'radio'
14259                     },
14260                     {
14261                         tag: 'label'
14262                     }
14263                 ]
14264             }
14265         ]
14266     },
14267     
14268     listItemCheckbox : {
14269         tag: 'li',
14270         cls: 'list-group-item',
14271         cn: [
14272             {
14273                 tag: 'span',
14274                 cls: 'roo-combobox-list-group-item-value'
14275             },
14276             {
14277                 tag: 'div',
14278                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14279                 cn: [
14280                     {
14281                         tag: 'input',
14282                         type: 'checkbox'
14283                     },
14284                     {
14285                         tag: 'label'
14286                     }
14287                 ]
14288             }
14289         ]
14290     },
14291     
14292     emptyResult : {
14293         tag: 'div',
14294         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14295     },
14296     
14297     footer : {
14298         tag: 'div',
14299         cls: 'modal-footer',
14300         cn: [
14301             {
14302                 tag: 'div',
14303                 cls: 'row',
14304                 cn: [
14305                     {
14306                         tag: 'div',
14307                         cls: 'col-xs-6 text-left',
14308                         cn: {
14309                             tag: 'button',
14310                             cls: 'btn btn-danger roo-touch-view-cancel',
14311                             html: 'Cancel'
14312                         }
14313                     },
14314                     {
14315                         tag: 'div',
14316                         cls: 'col-xs-6 text-right',
14317                         cn: {
14318                             tag: 'button',
14319                             cls: 'btn btn-success roo-touch-view-ok',
14320                             html: 'OK'
14321                         }
14322                     }
14323                 ]
14324             }
14325         ]
14326         
14327     }
14328 });
14329
14330 Roo.apply(Roo.bootstrap.ComboBox,  {
14331     
14332     touchViewTemplate : {
14333         tag: 'div',
14334         cls: 'modal fade roo-combobox-touch-view',
14335         cn: [
14336             {
14337                 tag: 'div',
14338                 cls: 'modal-dialog',
14339                 style : 'position:fixed', // we have to fix position....
14340                 cn: [
14341                     {
14342                         tag: 'div',
14343                         cls: 'modal-content',
14344                         cn: [
14345                             Roo.bootstrap.ComboBox.header,
14346                             Roo.bootstrap.ComboBox.body,
14347                             Roo.bootstrap.ComboBox.footer
14348                         ]
14349                     }
14350                 ]
14351             }
14352         ]
14353     }
14354 });/*
14355  * Based on:
14356  * Ext JS Library 1.1.1
14357  * Copyright(c) 2006-2007, Ext JS, LLC.
14358  *
14359  * Originally Released Under LGPL - original licence link has changed is not relivant.
14360  *
14361  * Fork - LGPL
14362  * <script type="text/javascript">
14363  */
14364
14365 /**
14366  * @class Roo.View
14367  * @extends Roo.util.Observable
14368  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14369  * This class also supports single and multi selection modes. <br>
14370  * Create a data model bound view:
14371  <pre><code>
14372  var store = new Roo.data.Store(...);
14373
14374  var view = new Roo.View({
14375     el : "my-element",
14376     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14377  
14378     singleSelect: true,
14379     selectedClass: "ydataview-selected",
14380     store: store
14381  });
14382
14383  // listen for node click?
14384  view.on("click", function(vw, index, node, e){
14385  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14386  });
14387
14388  // load XML data
14389  dataModel.load("foobar.xml");
14390  </code></pre>
14391  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14392  * <br><br>
14393  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14394  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14395  * 
14396  * Note: old style constructor is still suported (container, template, config)
14397  * 
14398  * @constructor
14399  * Create a new View
14400  * @param {Object} config The config object
14401  * 
14402  */
14403 Roo.View = function(config, depreciated_tpl, depreciated_config){
14404     
14405     this.parent = false;
14406     
14407     if (typeof(depreciated_tpl) == 'undefined') {
14408         // new way.. - universal constructor.
14409         Roo.apply(this, config);
14410         this.el  = Roo.get(this.el);
14411     } else {
14412         // old format..
14413         this.el  = Roo.get(config);
14414         this.tpl = depreciated_tpl;
14415         Roo.apply(this, depreciated_config);
14416     }
14417     this.wrapEl  = this.el.wrap().wrap();
14418     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14419     
14420     
14421     if(typeof(this.tpl) == "string"){
14422         this.tpl = new Roo.Template(this.tpl);
14423     } else {
14424         // support xtype ctors..
14425         this.tpl = new Roo.factory(this.tpl, Roo);
14426     }
14427     
14428     
14429     this.tpl.compile();
14430     
14431     /** @private */
14432     this.addEvents({
14433         /**
14434          * @event beforeclick
14435          * Fires before a click is processed. Returns false to cancel the default action.
14436          * @param {Roo.View} this
14437          * @param {Number} index The index of the target node
14438          * @param {HTMLElement} node The target node
14439          * @param {Roo.EventObject} e The raw event object
14440          */
14441             "beforeclick" : true,
14442         /**
14443          * @event click
14444          * Fires when a template node is clicked.
14445          * @param {Roo.View} this
14446          * @param {Number} index The index of the target node
14447          * @param {HTMLElement} node The target node
14448          * @param {Roo.EventObject} e The raw event object
14449          */
14450             "click" : true,
14451         /**
14452          * @event dblclick
14453          * Fires when a template node is double clicked.
14454          * @param {Roo.View} this
14455          * @param {Number} index The index of the target node
14456          * @param {HTMLElement} node The target node
14457          * @param {Roo.EventObject} e The raw event object
14458          */
14459             "dblclick" : true,
14460         /**
14461          * @event contextmenu
14462          * Fires when a template node is right clicked.
14463          * @param {Roo.View} this
14464          * @param {Number} index The index of the target node
14465          * @param {HTMLElement} node The target node
14466          * @param {Roo.EventObject} e The raw event object
14467          */
14468             "contextmenu" : true,
14469         /**
14470          * @event selectionchange
14471          * Fires when the selected nodes change.
14472          * @param {Roo.View} this
14473          * @param {Array} selections Array of the selected nodes
14474          */
14475             "selectionchange" : true,
14476     
14477         /**
14478          * @event beforeselect
14479          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14480          * @param {Roo.View} this
14481          * @param {HTMLElement} node The node to be selected
14482          * @param {Array} selections Array of currently selected nodes
14483          */
14484             "beforeselect" : true,
14485         /**
14486          * @event preparedata
14487          * Fires on every row to render, to allow you to change the data.
14488          * @param {Roo.View} this
14489          * @param {Object} data to be rendered (change this)
14490          */
14491           "preparedata" : true
14492           
14493           
14494         });
14495
14496
14497
14498     this.el.on({
14499         "click": this.onClick,
14500         "dblclick": this.onDblClick,
14501         "contextmenu": this.onContextMenu,
14502         scope:this
14503     });
14504
14505     this.selections = [];
14506     this.nodes = [];
14507     this.cmp = new Roo.CompositeElementLite([]);
14508     if(this.store){
14509         this.store = Roo.factory(this.store, Roo.data);
14510         this.setStore(this.store, true);
14511     }
14512     
14513     if ( this.footer && this.footer.xtype) {
14514            
14515          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14516         
14517         this.footer.dataSource = this.store;
14518         this.footer.container = fctr;
14519         this.footer = Roo.factory(this.footer, Roo);
14520         fctr.insertFirst(this.el);
14521         
14522         // this is a bit insane - as the paging toolbar seems to detach the el..
14523 //        dom.parentNode.parentNode.parentNode
14524          // they get detached?
14525     }
14526     
14527     
14528     Roo.View.superclass.constructor.call(this);
14529     
14530     
14531 };
14532
14533 Roo.extend(Roo.View, Roo.util.Observable, {
14534     
14535      /**
14536      * @cfg {Roo.data.Store} store Data store to load data from.
14537      */
14538     store : false,
14539     
14540     /**
14541      * @cfg {String|Roo.Element} el The container element.
14542      */
14543     el : '',
14544     
14545     /**
14546      * @cfg {String|Roo.Template} tpl The template used by this View 
14547      */
14548     tpl : false,
14549     /**
14550      * @cfg {String} dataName the named area of the template to use as the data area
14551      *                          Works with domtemplates roo-name="name"
14552      */
14553     dataName: false,
14554     /**
14555      * @cfg {String} selectedClass The css class to add to selected nodes
14556      */
14557     selectedClass : "x-view-selected",
14558      /**
14559      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14560      */
14561     emptyText : "",
14562     
14563     /**
14564      * @cfg {String} text to display on mask (default Loading)
14565      */
14566     mask : false,
14567     /**
14568      * @cfg {Boolean} multiSelect Allow multiple selection
14569      */
14570     multiSelect : false,
14571     /**
14572      * @cfg {Boolean} singleSelect Allow single selection
14573      */
14574     singleSelect:  false,
14575     
14576     /**
14577      * @cfg {Boolean} toggleSelect - selecting 
14578      */
14579     toggleSelect : false,
14580     
14581     /**
14582      * @cfg {Boolean} tickable - selecting 
14583      */
14584     tickable : false,
14585     
14586     /**
14587      * Returns the element this view is bound to.
14588      * @return {Roo.Element}
14589      */
14590     getEl : function(){
14591         return this.wrapEl;
14592     },
14593     
14594     
14595
14596     /**
14597      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14598      */
14599     refresh : function(){
14600         //Roo.log('refresh');
14601         var t = this.tpl;
14602         
14603         // if we are using something like 'domtemplate', then
14604         // the what gets used is:
14605         // t.applySubtemplate(NAME, data, wrapping data..)
14606         // the outer template then get' applied with
14607         //     the store 'extra data'
14608         // and the body get's added to the
14609         //      roo-name="data" node?
14610         //      <span class='roo-tpl-{name}'></span> ?????
14611         
14612         
14613         
14614         this.clearSelections();
14615         this.el.update("");
14616         var html = [];
14617         var records = this.store.getRange();
14618         if(records.length < 1) {
14619             
14620             // is this valid??  = should it render a template??
14621             
14622             this.el.update(this.emptyText);
14623             return;
14624         }
14625         var el = this.el;
14626         if (this.dataName) {
14627             this.el.update(t.apply(this.store.meta)); //????
14628             el = this.el.child('.roo-tpl-' + this.dataName);
14629         }
14630         
14631         for(var i = 0, len = records.length; i < len; i++){
14632             var data = this.prepareData(records[i].data, i, records[i]);
14633             this.fireEvent("preparedata", this, data, i, records[i]);
14634             
14635             var d = Roo.apply({}, data);
14636             
14637             if(this.tickable){
14638                 Roo.apply(d, {'roo-id' : Roo.id()});
14639                 
14640                 var _this = this;
14641             
14642                 Roo.each(this.parent.item, function(item){
14643                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14644                         return;
14645                     }
14646                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14647                 });
14648             }
14649             
14650             html[html.length] = Roo.util.Format.trim(
14651                 this.dataName ?
14652                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14653                     t.apply(d)
14654             );
14655         }
14656         
14657         
14658         
14659         el.update(html.join(""));
14660         this.nodes = el.dom.childNodes;
14661         this.updateIndexes(0);
14662     },
14663     
14664
14665     /**
14666      * Function to override to reformat the data that is sent to
14667      * the template for each node.
14668      * DEPRICATED - use the preparedata event handler.
14669      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14670      * a JSON object for an UpdateManager bound view).
14671      */
14672     prepareData : function(data, index, record)
14673     {
14674         this.fireEvent("preparedata", this, data, index, record);
14675         return data;
14676     },
14677
14678     onUpdate : function(ds, record){
14679         // Roo.log('on update');   
14680         this.clearSelections();
14681         var index = this.store.indexOf(record);
14682         var n = this.nodes[index];
14683         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14684         n.parentNode.removeChild(n);
14685         this.updateIndexes(index, index);
14686     },
14687
14688     
14689     
14690 // --------- FIXME     
14691     onAdd : function(ds, records, index)
14692     {
14693         //Roo.log(['on Add', ds, records, index] );        
14694         this.clearSelections();
14695         if(this.nodes.length == 0){
14696             this.refresh();
14697             return;
14698         }
14699         var n = this.nodes[index];
14700         for(var i = 0, len = records.length; i < len; i++){
14701             var d = this.prepareData(records[i].data, i, records[i]);
14702             if(n){
14703                 this.tpl.insertBefore(n, d);
14704             }else{
14705                 
14706                 this.tpl.append(this.el, d);
14707             }
14708         }
14709         this.updateIndexes(index);
14710     },
14711
14712     onRemove : function(ds, record, index){
14713        // Roo.log('onRemove');
14714         this.clearSelections();
14715         var el = this.dataName  ?
14716             this.el.child('.roo-tpl-' + this.dataName) :
14717             this.el; 
14718         
14719         el.dom.removeChild(this.nodes[index]);
14720         this.updateIndexes(index);
14721     },
14722
14723     /**
14724      * Refresh an individual node.
14725      * @param {Number} index
14726      */
14727     refreshNode : function(index){
14728         this.onUpdate(this.store, this.store.getAt(index));
14729     },
14730
14731     updateIndexes : function(startIndex, endIndex){
14732         var ns = this.nodes;
14733         startIndex = startIndex || 0;
14734         endIndex = endIndex || ns.length - 1;
14735         for(var i = startIndex; i <= endIndex; i++){
14736             ns[i].nodeIndex = i;
14737         }
14738     },
14739
14740     /**
14741      * Changes the data store this view uses and refresh the view.
14742      * @param {Store} store
14743      */
14744     setStore : function(store, initial){
14745         if(!initial && this.store){
14746             this.store.un("datachanged", this.refresh);
14747             this.store.un("add", this.onAdd);
14748             this.store.un("remove", this.onRemove);
14749             this.store.un("update", this.onUpdate);
14750             this.store.un("clear", this.refresh);
14751             this.store.un("beforeload", this.onBeforeLoad);
14752             this.store.un("load", this.onLoad);
14753             this.store.un("loadexception", this.onLoad);
14754         }
14755         if(store){
14756           
14757             store.on("datachanged", this.refresh, this);
14758             store.on("add", this.onAdd, this);
14759             store.on("remove", this.onRemove, this);
14760             store.on("update", this.onUpdate, this);
14761             store.on("clear", this.refresh, this);
14762             store.on("beforeload", this.onBeforeLoad, this);
14763             store.on("load", this.onLoad, this);
14764             store.on("loadexception", this.onLoad, this);
14765         }
14766         
14767         if(store){
14768             this.refresh();
14769         }
14770     },
14771     /**
14772      * onbeforeLoad - masks the loading area.
14773      *
14774      */
14775     onBeforeLoad : function(store,opts)
14776     {
14777          //Roo.log('onBeforeLoad');   
14778         if (!opts.add) {
14779             this.el.update("");
14780         }
14781         this.el.mask(this.mask ? this.mask : "Loading" ); 
14782     },
14783     onLoad : function ()
14784     {
14785         this.el.unmask();
14786     },
14787     
14788
14789     /**
14790      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14791      * @param {HTMLElement} node
14792      * @return {HTMLElement} The template node
14793      */
14794     findItemFromChild : function(node){
14795         var el = this.dataName  ?
14796             this.el.child('.roo-tpl-' + this.dataName,true) :
14797             this.el.dom; 
14798         
14799         if(!node || node.parentNode == el){
14800                     return node;
14801             }
14802             var p = node.parentNode;
14803             while(p && p != el){
14804             if(p.parentNode == el){
14805                 return p;
14806             }
14807             p = p.parentNode;
14808         }
14809             return null;
14810     },
14811
14812     /** @ignore */
14813     onClick : function(e){
14814         var item = this.findItemFromChild(e.getTarget());
14815         if(item){
14816             var index = this.indexOf(item);
14817             if(this.onItemClick(item, index, e) !== false){
14818                 this.fireEvent("click", this, index, item, e);
14819             }
14820         }else{
14821             this.clearSelections();
14822         }
14823     },
14824
14825     /** @ignore */
14826     onContextMenu : function(e){
14827         var item = this.findItemFromChild(e.getTarget());
14828         if(item){
14829             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14830         }
14831     },
14832
14833     /** @ignore */
14834     onDblClick : function(e){
14835         var item = this.findItemFromChild(e.getTarget());
14836         if(item){
14837             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14838         }
14839     },
14840
14841     onItemClick : function(item, index, e)
14842     {
14843         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14844             return false;
14845         }
14846         if (this.toggleSelect) {
14847             var m = this.isSelected(item) ? 'unselect' : 'select';
14848             //Roo.log(m);
14849             var _t = this;
14850             _t[m](item, true, false);
14851             return true;
14852         }
14853         if(this.multiSelect || this.singleSelect){
14854             if(this.multiSelect && e.shiftKey && this.lastSelection){
14855                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14856             }else{
14857                 this.select(item, this.multiSelect && e.ctrlKey);
14858                 this.lastSelection = item;
14859             }
14860             
14861             if(!this.tickable){
14862                 e.preventDefault();
14863             }
14864             
14865         }
14866         return true;
14867     },
14868
14869     /**
14870      * Get the number of selected nodes.
14871      * @return {Number}
14872      */
14873     getSelectionCount : function(){
14874         return this.selections.length;
14875     },
14876
14877     /**
14878      * Get the currently selected nodes.
14879      * @return {Array} An array of HTMLElements
14880      */
14881     getSelectedNodes : function(){
14882         return this.selections;
14883     },
14884
14885     /**
14886      * Get the indexes of the selected nodes.
14887      * @return {Array}
14888      */
14889     getSelectedIndexes : function(){
14890         var indexes = [], s = this.selections;
14891         for(var i = 0, len = s.length; i < len; i++){
14892             indexes.push(s[i].nodeIndex);
14893         }
14894         return indexes;
14895     },
14896
14897     /**
14898      * Clear all selections
14899      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14900      */
14901     clearSelections : function(suppressEvent){
14902         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14903             this.cmp.elements = this.selections;
14904             this.cmp.removeClass(this.selectedClass);
14905             this.selections = [];
14906             if(!suppressEvent){
14907                 this.fireEvent("selectionchange", this, this.selections);
14908             }
14909         }
14910     },
14911
14912     /**
14913      * Returns true if the passed node is selected
14914      * @param {HTMLElement/Number} node The node or node index
14915      * @return {Boolean}
14916      */
14917     isSelected : function(node){
14918         var s = this.selections;
14919         if(s.length < 1){
14920             return false;
14921         }
14922         node = this.getNode(node);
14923         return s.indexOf(node) !== -1;
14924     },
14925
14926     /**
14927      * Selects nodes.
14928      * @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
14929      * @param {Boolean} keepExisting (optional) true to keep existing selections
14930      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14931      */
14932     select : function(nodeInfo, keepExisting, suppressEvent){
14933         if(nodeInfo instanceof Array){
14934             if(!keepExisting){
14935                 this.clearSelections(true);
14936             }
14937             for(var i = 0, len = nodeInfo.length; i < len; i++){
14938                 this.select(nodeInfo[i], true, true);
14939             }
14940             return;
14941         } 
14942         var node = this.getNode(nodeInfo);
14943         if(!node || this.isSelected(node)){
14944             return; // already selected.
14945         }
14946         if(!keepExisting){
14947             this.clearSelections(true);
14948         }
14949         
14950         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14951             Roo.fly(node).addClass(this.selectedClass);
14952             this.selections.push(node);
14953             if(!suppressEvent){
14954                 this.fireEvent("selectionchange", this, this.selections);
14955             }
14956         }
14957         
14958         
14959     },
14960       /**
14961      * Unselects nodes.
14962      * @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
14963      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14964      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14965      */
14966     unselect : function(nodeInfo, keepExisting, suppressEvent)
14967     {
14968         if(nodeInfo instanceof Array){
14969             Roo.each(this.selections, function(s) {
14970                 this.unselect(s, nodeInfo);
14971             }, this);
14972             return;
14973         }
14974         var node = this.getNode(nodeInfo);
14975         if(!node || !this.isSelected(node)){
14976             //Roo.log("not selected");
14977             return; // not selected.
14978         }
14979         // fireevent???
14980         var ns = [];
14981         Roo.each(this.selections, function(s) {
14982             if (s == node ) {
14983                 Roo.fly(node).removeClass(this.selectedClass);
14984
14985                 return;
14986             }
14987             ns.push(s);
14988         },this);
14989         
14990         this.selections= ns;
14991         this.fireEvent("selectionchange", this, this.selections);
14992     },
14993
14994     /**
14995      * Gets a template node.
14996      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14997      * @return {HTMLElement} The node or null if it wasn't found
14998      */
14999     getNode : function(nodeInfo){
15000         if(typeof nodeInfo == "string"){
15001             return document.getElementById(nodeInfo);
15002         }else if(typeof nodeInfo == "number"){
15003             return this.nodes[nodeInfo];
15004         }
15005         return nodeInfo;
15006     },
15007
15008     /**
15009      * Gets a range template nodes.
15010      * @param {Number} startIndex
15011      * @param {Number} endIndex
15012      * @return {Array} An array of nodes
15013      */
15014     getNodes : function(start, end){
15015         var ns = this.nodes;
15016         start = start || 0;
15017         end = typeof end == "undefined" ? ns.length - 1 : end;
15018         var nodes = [];
15019         if(start <= end){
15020             for(var i = start; i <= end; i++){
15021                 nodes.push(ns[i]);
15022             }
15023         } else{
15024             for(var i = start; i >= end; i--){
15025                 nodes.push(ns[i]);
15026             }
15027         }
15028         return nodes;
15029     },
15030
15031     /**
15032      * Finds the index of the passed node
15033      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15034      * @return {Number} The index of the node or -1
15035      */
15036     indexOf : function(node){
15037         node = this.getNode(node);
15038         if(typeof node.nodeIndex == "number"){
15039             return node.nodeIndex;
15040         }
15041         var ns = this.nodes;
15042         for(var i = 0, len = ns.length; i < len; i++){
15043             if(ns[i] == node){
15044                 return i;
15045             }
15046         }
15047         return -1;
15048     }
15049 });
15050 /*
15051  * - LGPL
15052  *
15053  * based on jquery fullcalendar
15054  * 
15055  */
15056
15057 Roo.bootstrap = Roo.bootstrap || {};
15058 /**
15059  * @class Roo.bootstrap.Calendar
15060  * @extends Roo.bootstrap.Component
15061  * Bootstrap Calendar class
15062  * @cfg {Boolean} loadMask (true|false) default false
15063  * @cfg {Object} header generate the user specific header of the calendar, default false
15064
15065  * @constructor
15066  * Create a new Container
15067  * @param {Object} config The config object
15068  */
15069
15070
15071
15072 Roo.bootstrap.Calendar = function(config){
15073     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15074      this.addEvents({
15075         /**
15076              * @event select
15077              * Fires when a date is selected
15078              * @param {DatePicker} this
15079              * @param {Date} date The selected date
15080              */
15081         'select': true,
15082         /**
15083              * @event monthchange
15084              * Fires when the displayed month changes 
15085              * @param {DatePicker} this
15086              * @param {Date} date The selected month
15087              */
15088         'monthchange': true,
15089         /**
15090              * @event evententer
15091              * Fires when mouse over an event
15092              * @param {Calendar} this
15093              * @param {event} Event
15094              */
15095         'evententer': true,
15096         /**
15097              * @event eventleave
15098              * Fires when the mouse leaves an
15099              * @param {Calendar} this
15100              * @param {event}
15101              */
15102         'eventleave': true,
15103         /**
15104              * @event eventclick
15105              * Fires when the mouse click an
15106              * @param {Calendar} this
15107              * @param {event}
15108              */
15109         'eventclick': true
15110         
15111     });
15112
15113 };
15114
15115 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15116     
15117      /**
15118      * @cfg {Number} startDay
15119      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15120      */
15121     startDay : 0,
15122     
15123     loadMask : false,
15124     
15125     header : false,
15126       
15127     getAutoCreate : function(){
15128         
15129         
15130         var fc_button = function(name, corner, style, content ) {
15131             return Roo.apply({},{
15132                 tag : 'span',
15133                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15134                          (corner.length ?
15135                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15136                             ''
15137                         ),
15138                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15139                 unselectable: 'on'
15140             });
15141         };
15142         
15143         var header = {};
15144         
15145         if(!this.header){
15146             header = {
15147                 tag : 'table',
15148                 cls : 'fc-header',
15149                 style : 'width:100%',
15150                 cn : [
15151                     {
15152                         tag: 'tr',
15153                         cn : [
15154                             {
15155                                 tag : 'td',
15156                                 cls : 'fc-header-left',
15157                                 cn : [
15158                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15159                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15160                                     { tag: 'span', cls: 'fc-header-space' },
15161                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15162
15163
15164                                 ]
15165                             },
15166
15167                             {
15168                                 tag : 'td',
15169                                 cls : 'fc-header-center',
15170                                 cn : [
15171                                     {
15172                                         tag: 'span',
15173                                         cls: 'fc-header-title',
15174                                         cn : {
15175                                             tag: 'H2',
15176                                             html : 'month / year'
15177                                         }
15178                                     }
15179
15180                                 ]
15181                             },
15182                             {
15183                                 tag : 'td',
15184                                 cls : 'fc-header-right',
15185                                 cn : [
15186                               /*      fc_button('month', 'left', '', 'month' ),
15187                                     fc_button('week', '', '', 'week' ),
15188                                     fc_button('day', 'right', '', 'day' )
15189                                 */    
15190
15191                                 ]
15192                             }
15193
15194                         ]
15195                     }
15196                 ]
15197             };
15198         }
15199         
15200         header = this.header;
15201         
15202        
15203         var cal_heads = function() {
15204             var ret = [];
15205             // fixme - handle this.
15206             
15207             for (var i =0; i < Date.dayNames.length; i++) {
15208                 var d = Date.dayNames[i];
15209                 ret.push({
15210                     tag: 'th',
15211                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15212                     html : d.substring(0,3)
15213                 });
15214                 
15215             }
15216             ret[0].cls += ' fc-first';
15217             ret[6].cls += ' fc-last';
15218             return ret;
15219         };
15220         var cal_cell = function(n) {
15221             return  {
15222                 tag: 'td',
15223                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15224                 cn : [
15225                     {
15226                         cn : [
15227                             {
15228                                 cls: 'fc-day-number',
15229                                 html: 'D'
15230                             },
15231                             {
15232                                 cls: 'fc-day-content',
15233                              
15234                                 cn : [
15235                                      {
15236                                         style: 'position: relative;' // height: 17px;
15237                                     }
15238                                 ]
15239                             }
15240                             
15241                             
15242                         ]
15243                     }
15244                 ]
15245                 
15246             }
15247         };
15248         var cal_rows = function() {
15249             
15250             var ret = [];
15251             for (var r = 0; r < 6; r++) {
15252                 var row= {
15253                     tag : 'tr',
15254                     cls : 'fc-week',
15255                     cn : []
15256                 };
15257                 
15258                 for (var i =0; i < Date.dayNames.length; i++) {
15259                     var d = Date.dayNames[i];
15260                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15261
15262                 }
15263                 row.cn[0].cls+=' fc-first';
15264                 row.cn[0].cn[0].style = 'min-height:90px';
15265                 row.cn[6].cls+=' fc-last';
15266                 ret.push(row);
15267                 
15268             }
15269             ret[0].cls += ' fc-first';
15270             ret[4].cls += ' fc-prev-last';
15271             ret[5].cls += ' fc-last';
15272             return ret;
15273             
15274         };
15275         
15276         var cal_table = {
15277             tag: 'table',
15278             cls: 'fc-border-separate',
15279             style : 'width:100%',
15280             cellspacing  : 0,
15281             cn : [
15282                 { 
15283                     tag: 'thead',
15284                     cn : [
15285                         { 
15286                             tag: 'tr',
15287                             cls : 'fc-first fc-last',
15288                             cn : cal_heads()
15289                         }
15290                     ]
15291                 },
15292                 { 
15293                     tag: 'tbody',
15294                     cn : cal_rows()
15295                 }
15296                   
15297             ]
15298         };
15299          
15300          var cfg = {
15301             cls : 'fc fc-ltr',
15302             cn : [
15303                 header,
15304                 {
15305                     cls : 'fc-content',
15306                     style : "position: relative;",
15307                     cn : [
15308                         {
15309                             cls : 'fc-view fc-view-month fc-grid',
15310                             style : 'position: relative',
15311                             unselectable : 'on',
15312                             cn : [
15313                                 {
15314                                     cls : 'fc-event-container',
15315                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15316                                 },
15317                                 cal_table
15318                             ]
15319                         }
15320                     ]
15321     
15322                 }
15323            ] 
15324             
15325         };
15326         
15327          
15328         
15329         return cfg;
15330     },
15331     
15332     
15333     initEvents : function()
15334     {
15335         if(!this.store){
15336             throw "can not find store for calendar";
15337         }
15338         
15339         var mark = {
15340             tag: "div",
15341             cls:"x-dlg-mask",
15342             style: "text-align:center",
15343             cn: [
15344                 {
15345                     tag: "div",
15346                     style: "background-color:white;width:50%;margin:250 auto",
15347                     cn: [
15348                         {
15349                             tag: "img",
15350                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15351                         },
15352                         {
15353                             tag: "span",
15354                             html: "Loading"
15355                         }
15356                         
15357                     ]
15358                 }
15359             ]
15360         };
15361         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15362         
15363         var size = this.el.select('.fc-content', true).first().getSize();
15364         this.maskEl.setSize(size.width, size.height);
15365         this.maskEl.enableDisplayMode("block");
15366         if(!this.loadMask){
15367             this.maskEl.hide();
15368         }
15369         
15370         this.store = Roo.factory(this.store, Roo.data);
15371         this.store.on('load', this.onLoad, this);
15372         this.store.on('beforeload', this.onBeforeLoad, this);
15373         
15374         this.resize();
15375         
15376         this.cells = this.el.select('.fc-day',true);
15377         //Roo.log(this.cells);
15378         this.textNodes = this.el.query('.fc-day-number');
15379         this.cells.addClassOnOver('fc-state-hover');
15380         
15381         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15382         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15383         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15384         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15385         
15386         this.on('monthchange', this.onMonthChange, this);
15387         
15388         this.update(new Date().clearTime());
15389     },
15390     
15391     resize : function() {
15392         var sz  = this.el.getSize();
15393         
15394         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15395         this.el.select('.fc-day-content div',true).setHeight(34);
15396     },
15397     
15398     
15399     // private
15400     showPrevMonth : function(e){
15401         this.update(this.activeDate.add("mo", -1));
15402     },
15403     showToday : function(e){
15404         this.update(new Date().clearTime());
15405     },
15406     // private
15407     showNextMonth : function(e){
15408         this.update(this.activeDate.add("mo", 1));
15409     },
15410
15411     // private
15412     showPrevYear : function(){
15413         this.update(this.activeDate.add("y", -1));
15414     },
15415
15416     // private
15417     showNextYear : function(){
15418         this.update(this.activeDate.add("y", 1));
15419     },
15420
15421     
15422    // private
15423     update : function(date)
15424     {
15425         var vd = this.activeDate;
15426         this.activeDate = date;
15427 //        if(vd && this.el){
15428 //            var t = date.getTime();
15429 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15430 //                Roo.log('using add remove');
15431 //                
15432 //                this.fireEvent('monthchange', this, date);
15433 //                
15434 //                this.cells.removeClass("fc-state-highlight");
15435 //                this.cells.each(function(c){
15436 //                   if(c.dateValue == t){
15437 //                       c.addClass("fc-state-highlight");
15438 //                       setTimeout(function(){
15439 //                            try{c.dom.firstChild.focus();}catch(e){}
15440 //                       }, 50);
15441 //                       return false;
15442 //                   }
15443 //                   return true;
15444 //                });
15445 //                return;
15446 //            }
15447 //        }
15448         
15449         var days = date.getDaysInMonth();
15450         
15451         var firstOfMonth = date.getFirstDateOfMonth();
15452         var startingPos = firstOfMonth.getDay()-this.startDay;
15453         
15454         if(startingPos < this.startDay){
15455             startingPos += 7;
15456         }
15457         
15458         var pm = date.add(Date.MONTH, -1);
15459         var prevStart = pm.getDaysInMonth()-startingPos;
15460 //        
15461         this.cells = this.el.select('.fc-day',true);
15462         this.textNodes = this.el.query('.fc-day-number');
15463         this.cells.addClassOnOver('fc-state-hover');
15464         
15465         var cells = this.cells.elements;
15466         var textEls = this.textNodes;
15467         
15468         Roo.each(cells, function(cell){
15469             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15470         });
15471         
15472         days += startingPos;
15473
15474         // convert everything to numbers so it's fast
15475         var day = 86400000;
15476         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15477         //Roo.log(d);
15478         //Roo.log(pm);
15479         //Roo.log(prevStart);
15480         
15481         var today = new Date().clearTime().getTime();
15482         var sel = date.clearTime().getTime();
15483         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15484         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15485         var ddMatch = this.disabledDatesRE;
15486         var ddText = this.disabledDatesText;
15487         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15488         var ddaysText = this.disabledDaysText;
15489         var format = this.format;
15490         
15491         var setCellClass = function(cal, cell){
15492             cell.row = 0;
15493             cell.events = [];
15494             cell.more = [];
15495             //Roo.log('set Cell Class');
15496             cell.title = "";
15497             var t = d.getTime();
15498             
15499             //Roo.log(d);
15500             
15501             cell.dateValue = t;
15502             if(t == today){
15503                 cell.className += " fc-today";
15504                 cell.className += " fc-state-highlight";
15505                 cell.title = cal.todayText;
15506             }
15507             if(t == sel){
15508                 // disable highlight in other month..
15509                 //cell.className += " fc-state-highlight";
15510                 
15511             }
15512             // disabling
15513             if(t < min) {
15514                 cell.className = " fc-state-disabled";
15515                 cell.title = cal.minText;
15516                 return;
15517             }
15518             if(t > max) {
15519                 cell.className = " fc-state-disabled";
15520                 cell.title = cal.maxText;
15521                 return;
15522             }
15523             if(ddays){
15524                 if(ddays.indexOf(d.getDay()) != -1){
15525                     cell.title = ddaysText;
15526                     cell.className = " fc-state-disabled";
15527                 }
15528             }
15529             if(ddMatch && format){
15530                 var fvalue = d.dateFormat(format);
15531                 if(ddMatch.test(fvalue)){
15532                     cell.title = ddText.replace("%0", fvalue);
15533                     cell.className = " fc-state-disabled";
15534                 }
15535             }
15536             
15537             if (!cell.initialClassName) {
15538                 cell.initialClassName = cell.dom.className;
15539             }
15540             
15541             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15542         };
15543
15544         var i = 0;
15545         
15546         for(; i < startingPos; i++) {
15547             textEls[i].innerHTML = (++prevStart);
15548             d.setDate(d.getDate()+1);
15549             
15550             cells[i].className = "fc-past fc-other-month";
15551             setCellClass(this, cells[i]);
15552         }
15553         
15554         var intDay = 0;
15555         
15556         for(; i < days; i++){
15557             intDay = i - startingPos + 1;
15558             textEls[i].innerHTML = (intDay);
15559             d.setDate(d.getDate()+1);
15560             
15561             cells[i].className = ''; // "x-date-active";
15562             setCellClass(this, cells[i]);
15563         }
15564         var extraDays = 0;
15565         
15566         for(; i < 42; i++) {
15567             textEls[i].innerHTML = (++extraDays);
15568             d.setDate(d.getDate()+1);
15569             
15570             cells[i].className = "fc-future fc-other-month";
15571             setCellClass(this, cells[i]);
15572         }
15573         
15574         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15575         
15576         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15577         
15578         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15579         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15580         
15581         if(totalRows != 6){
15582             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15583             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15584         }
15585         
15586         this.fireEvent('monthchange', this, date);
15587         
15588         
15589         /*
15590         if(!this.internalRender){
15591             var main = this.el.dom.firstChild;
15592             var w = main.offsetWidth;
15593             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15594             Roo.fly(main).setWidth(w);
15595             this.internalRender = true;
15596             // opera does not respect the auto grow header center column
15597             // then, after it gets a width opera refuses to recalculate
15598             // without a second pass
15599             if(Roo.isOpera && !this.secondPass){
15600                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15601                 this.secondPass = true;
15602                 this.update.defer(10, this, [date]);
15603             }
15604         }
15605         */
15606         
15607     },
15608     
15609     findCell : function(dt) {
15610         dt = dt.clearTime().getTime();
15611         var ret = false;
15612         this.cells.each(function(c){
15613             //Roo.log("check " +c.dateValue + '?=' + dt);
15614             if(c.dateValue == dt){
15615                 ret = c;
15616                 return false;
15617             }
15618             return true;
15619         });
15620         
15621         return ret;
15622     },
15623     
15624     findCells : function(ev) {
15625         var s = ev.start.clone().clearTime().getTime();
15626        // Roo.log(s);
15627         var e= ev.end.clone().clearTime().getTime();
15628        // Roo.log(e);
15629         var ret = [];
15630         this.cells.each(function(c){
15631              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15632             
15633             if(c.dateValue > e){
15634                 return ;
15635             }
15636             if(c.dateValue < s){
15637                 return ;
15638             }
15639             ret.push(c);
15640         });
15641         
15642         return ret;    
15643     },
15644     
15645 //    findBestRow: function(cells)
15646 //    {
15647 //        var ret = 0;
15648 //        
15649 //        for (var i =0 ; i < cells.length;i++) {
15650 //            ret  = Math.max(cells[i].rows || 0,ret);
15651 //        }
15652 //        return ret;
15653 //        
15654 //    },
15655     
15656     
15657     addItem : function(ev)
15658     {
15659         // look for vertical location slot in
15660         var cells = this.findCells(ev);
15661         
15662 //        ev.row = this.findBestRow(cells);
15663         
15664         // work out the location.
15665         
15666         var crow = false;
15667         var rows = [];
15668         for(var i =0; i < cells.length; i++) {
15669             
15670             cells[i].row = cells[0].row;
15671             
15672             if(i == 0){
15673                 cells[i].row = cells[i].row + 1;
15674             }
15675             
15676             if (!crow) {
15677                 crow = {
15678                     start : cells[i],
15679                     end :  cells[i]
15680                 };
15681                 continue;
15682             }
15683             if (crow.start.getY() == cells[i].getY()) {
15684                 // on same row.
15685                 crow.end = cells[i];
15686                 continue;
15687             }
15688             // different row.
15689             rows.push(crow);
15690             crow = {
15691                 start: cells[i],
15692                 end : cells[i]
15693             };
15694             
15695         }
15696         
15697         rows.push(crow);
15698         ev.els = [];
15699         ev.rows = rows;
15700         ev.cells = cells;
15701         
15702         cells[0].events.push(ev);
15703         
15704         this.calevents.push(ev);
15705     },
15706     
15707     clearEvents: function() {
15708         
15709         if(!this.calevents){
15710             return;
15711         }
15712         
15713         Roo.each(this.cells.elements, function(c){
15714             c.row = 0;
15715             c.events = [];
15716             c.more = [];
15717         });
15718         
15719         Roo.each(this.calevents, function(e) {
15720             Roo.each(e.els, function(el) {
15721                 el.un('mouseenter' ,this.onEventEnter, this);
15722                 el.un('mouseleave' ,this.onEventLeave, this);
15723                 el.remove();
15724             },this);
15725         },this);
15726         
15727         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15728             e.remove();
15729         });
15730         
15731     },
15732     
15733     renderEvents: function()
15734     {   
15735         var _this = this;
15736         
15737         this.cells.each(function(c) {
15738             
15739             if(c.row < 5){
15740                 return;
15741             }
15742             
15743             var ev = c.events;
15744             
15745             var r = 4;
15746             if(c.row != c.events.length){
15747                 r = 4 - (4 - (c.row - c.events.length));
15748             }
15749             
15750             c.events = ev.slice(0, r);
15751             c.more = ev.slice(r);
15752             
15753             if(c.more.length && c.more.length == 1){
15754                 c.events.push(c.more.pop());
15755             }
15756             
15757             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15758             
15759         });
15760             
15761         this.cells.each(function(c) {
15762             
15763             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15764             
15765             
15766             for (var e = 0; e < c.events.length; e++){
15767                 var ev = c.events[e];
15768                 var rows = ev.rows;
15769                 
15770                 for(var i = 0; i < rows.length; i++) {
15771                 
15772                     // how many rows should it span..
15773
15774                     var  cfg = {
15775                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15776                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15777
15778                         unselectable : "on",
15779                         cn : [
15780                             {
15781                                 cls: 'fc-event-inner',
15782                                 cn : [
15783     //                                {
15784     //                                  tag:'span',
15785     //                                  cls: 'fc-event-time',
15786     //                                  html : cells.length > 1 ? '' : ev.time
15787     //                                },
15788                                     {
15789                                       tag:'span',
15790                                       cls: 'fc-event-title',
15791                                       html : String.format('{0}', ev.title)
15792                                     }
15793
15794
15795                                 ]
15796                             },
15797                             {
15798                                 cls: 'ui-resizable-handle ui-resizable-e',
15799                                 html : '&nbsp;&nbsp;&nbsp'
15800                             }
15801
15802                         ]
15803                     };
15804
15805                     if (i == 0) {
15806                         cfg.cls += ' fc-event-start';
15807                     }
15808                     if ((i+1) == rows.length) {
15809                         cfg.cls += ' fc-event-end';
15810                     }
15811
15812                     var ctr = _this.el.select('.fc-event-container',true).first();
15813                     var cg = ctr.createChild(cfg);
15814
15815                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15816                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15817
15818                     var r = (c.more.length) ? 1 : 0;
15819                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15820                     cg.setWidth(ebox.right - sbox.x -2);
15821
15822                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15823                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15824                     cg.on('click', _this.onEventClick, _this, ev);
15825
15826                     ev.els.push(cg);
15827                     
15828                 }
15829                 
15830             }
15831             
15832             
15833             if(c.more.length){
15834                 var  cfg = {
15835                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15836                     style : 'position: absolute',
15837                     unselectable : "on",
15838                     cn : [
15839                         {
15840                             cls: 'fc-event-inner',
15841                             cn : [
15842                                 {
15843                                   tag:'span',
15844                                   cls: 'fc-event-title',
15845                                   html : 'More'
15846                                 }
15847
15848
15849                             ]
15850                         },
15851                         {
15852                             cls: 'ui-resizable-handle ui-resizable-e',
15853                             html : '&nbsp;&nbsp;&nbsp'
15854                         }
15855
15856                     ]
15857                 };
15858
15859                 var ctr = _this.el.select('.fc-event-container',true).first();
15860                 var cg = ctr.createChild(cfg);
15861
15862                 var sbox = c.select('.fc-day-content',true).first().getBox();
15863                 var ebox = c.select('.fc-day-content',true).first().getBox();
15864                 //Roo.log(cg);
15865                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15866                 cg.setWidth(ebox.right - sbox.x -2);
15867
15868                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15869                 
15870             }
15871             
15872         });
15873         
15874         
15875         
15876     },
15877     
15878     onEventEnter: function (e, el,event,d) {
15879         this.fireEvent('evententer', this, el, event);
15880     },
15881     
15882     onEventLeave: function (e, el,event,d) {
15883         this.fireEvent('eventleave', this, el, event);
15884     },
15885     
15886     onEventClick: function (e, el,event,d) {
15887         this.fireEvent('eventclick', this, el, event);
15888     },
15889     
15890     onMonthChange: function () {
15891         this.store.load();
15892     },
15893     
15894     onMoreEventClick: function(e, el, more)
15895     {
15896         var _this = this;
15897         
15898         this.calpopover.placement = 'right';
15899         this.calpopover.setTitle('More');
15900         
15901         this.calpopover.setContent('');
15902         
15903         var ctr = this.calpopover.el.select('.popover-content', true).first();
15904         
15905         Roo.each(more, function(m){
15906             var cfg = {
15907                 cls : 'fc-event-hori fc-event-draggable',
15908                 html : m.title
15909             };
15910             var cg = ctr.createChild(cfg);
15911             
15912             cg.on('click', _this.onEventClick, _this, m);
15913         });
15914         
15915         this.calpopover.show(el);
15916         
15917         
15918     },
15919     
15920     onLoad: function () 
15921     {   
15922         this.calevents = [];
15923         var cal = this;
15924         
15925         if(this.store.getCount() > 0){
15926             this.store.data.each(function(d){
15927                cal.addItem({
15928                     id : d.data.id,
15929                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15930                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15931                     time : d.data.start_time,
15932                     title : d.data.title,
15933                     description : d.data.description,
15934                     venue : d.data.venue
15935                 });
15936             });
15937         }
15938         
15939         this.renderEvents();
15940         
15941         if(this.calevents.length && this.loadMask){
15942             this.maskEl.hide();
15943         }
15944     },
15945     
15946     onBeforeLoad: function()
15947     {
15948         this.clearEvents();
15949         if(this.loadMask){
15950             this.maskEl.show();
15951         }
15952     }
15953 });
15954
15955  
15956  /*
15957  * - LGPL
15958  *
15959  * element
15960  * 
15961  */
15962
15963 /**
15964  * @class Roo.bootstrap.Popover
15965  * @extends Roo.bootstrap.Component
15966  * Bootstrap Popover class
15967  * @cfg {String} html contents of the popover   (or false to use children..)
15968  * @cfg {String} title of popover (or false to hide)
15969  * @cfg {String} placement how it is placed
15970  * @cfg {String} trigger click || hover (or false to trigger manually)
15971  * @cfg {String} over what (parent or false to trigger manually.)
15972  * @cfg {Number} delay - delay before showing
15973  
15974  * @constructor
15975  * Create a new Popover
15976  * @param {Object} config The config object
15977  */
15978
15979 Roo.bootstrap.Popover = function(config){
15980     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15981     
15982     this.addEvents({
15983         // raw events
15984          /**
15985          * @event show
15986          * After the popover show
15987          * 
15988          * @param {Roo.bootstrap.Popover} this
15989          */
15990         "show" : true,
15991         /**
15992          * @event hide
15993          * After the popover hide
15994          * 
15995          * @param {Roo.bootstrap.Popover} this
15996          */
15997         "hide" : true
15998     });
15999 };
16000
16001 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16002     
16003     title: 'Fill in a title',
16004     html: false,
16005     
16006     placement : 'right',
16007     trigger : 'hover', // hover
16008     
16009     delay : 0,
16010     
16011     over: 'parent',
16012     
16013     can_build_overlaid : false,
16014     
16015     getChildContainer : function()
16016     {
16017         return this.el.select('.popover-content',true).first();
16018     },
16019     
16020     getAutoCreate : function(){
16021          
16022         var cfg = {
16023            cls : 'popover roo-dynamic',
16024            style: 'display:block',
16025            cn : [
16026                 {
16027                     cls : 'arrow'
16028                 },
16029                 {
16030                     cls : 'popover-inner',
16031                     cn : [
16032                         {
16033                             tag: 'h3',
16034                             cls: 'popover-title',
16035                             html : this.title
16036                         },
16037                         {
16038                             cls : 'popover-content',
16039                             html : this.html
16040                         }
16041                     ]
16042                     
16043                 }
16044            ]
16045         };
16046         
16047         return cfg;
16048     },
16049     setTitle: function(str)
16050     {
16051         this.title = str;
16052         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16053     },
16054     setContent: function(str)
16055     {
16056         this.html = str;
16057         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16058     },
16059     // as it get's added to the bottom of the page.
16060     onRender : function(ct, position)
16061     {
16062         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16063         if(!this.el){
16064             var cfg = Roo.apply({},  this.getAutoCreate());
16065             cfg.id = Roo.id();
16066             
16067             if (this.cls) {
16068                 cfg.cls += ' ' + this.cls;
16069             }
16070             if (this.style) {
16071                 cfg.style = this.style;
16072             }
16073             //Roo.log("adding to ");
16074             this.el = Roo.get(document.body).createChild(cfg, position);
16075 //            Roo.log(this.el);
16076         }
16077         this.initEvents();
16078     },
16079     
16080     initEvents : function()
16081     {
16082         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16083         this.el.enableDisplayMode('block');
16084         this.el.hide();
16085         if (this.over === false) {
16086             return; 
16087         }
16088         if (this.triggers === false) {
16089             return;
16090         }
16091         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16092         var triggers = this.trigger ? this.trigger.split(' ') : [];
16093         Roo.each(triggers, function(trigger) {
16094         
16095             if (trigger == 'click') {
16096                 on_el.on('click', this.toggle, this);
16097             } else if (trigger != 'manual') {
16098                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16099                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16100       
16101                 on_el.on(eventIn  ,this.enter, this);
16102                 on_el.on(eventOut, this.leave, this);
16103             }
16104         }, this);
16105         
16106     },
16107     
16108     
16109     // private
16110     timeout : null,
16111     hoverState : null,
16112     
16113     toggle : function () {
16114         this.hoverState == 'in' ? this.leave() : this.enter();
16115     },
16116     
16117     enter : function () {
16118         
16119         clearTimeout(this.timeout);
16120     
16121         this.hoverState = 'in';
16122     
16123         if (!this.delay || !this.delay.show) {
16124             this.show();
16125             return;
16126         }
16127         var _t = this;
16128         this.timeout = setTimeout(function () {
16129             if (_t.hoverState == 'in') {
16130                 _t.show();
16131             }
16132         }, this.delay.show)
16133     },
16134     
16135     leave : function() {
16136         clearTimeout(this.timeout);
16137     
16138         this.hoverState = 'out';
16139     
16140         if (!this.delay || !this.delay.hide) {
16141             this.hide();
16142             return;
16143         }
16144         var _t = this;
16145         this.timeout = setTimeout(function () {
16146             if (_t.hoverState == 'out') {
16147                 _t.hide();
16148             }
16149         }, this.delay.hide)
16150     },
16151     
16152     show : function (on_el)
16153     {
16154         if (!on_el) {
16155             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16156         }
16157         
16158         // set content.
16159         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16160         if (this.html !== false) {
16161             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16162         }
16163         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16164         if (!this.title.length) {
16165             this.el.select('.popover-title',true).hide();
16166         }
16167         
16168         var placement = typeof this.placement == 'function' ?
16169             this.placement.call(this, this.el, on_el) :
16170             this.placement;
16171             
16172         var autoToken = /\s?auto?\s?/i;
16173         var autoPlace = autoToken.test(placement);
16174         if (autoPlace) {
16175             placement = placement.replace(autoToken, '') || 'top';
16176         }
16177         
16178         //this.el.detach()
16179         //this.el.setXY([0,0]);
16180         this.el.show();
16181         this.el.dom.style.display='block';
16182         this.el.addClass(placement);
16183         
16184         //this.el.appendTo(on_el);
16185         
16186         var p = this.getPosition();
16187         var box = this.el.getBox();
16188         
16189         if (autoPlace) {
16190             // fixme..
16191         }
16192         var align = Roo.bootstrap.Popover.alignment[placement];
16193         this.el.alignTo(on_el, align[0],align[1]);
16194         //var arrow = this.el.select('.arrow',true).first();
16195         //arrow.set(align[2], 
16196         
16197         this.el.addClass('in');
16198         
16199         
16200         if (this.el.hasClass('fade')) {
16201             // fade it?
16202         }
16203         
16204         this.hoverState = 'in';
16205         
16206         this.fireEvent('show', this);
16207         
16208     },
16209     hide : function()
16210     {
16211         this.el.setXY([0,0]);
16212         this.el.removeClass('in');
16213         this.el.hide();
16214         this.hoverState = null;
16215         
16216         this.fireEvent('hide', this);
16217     }
16218     
16219 });
16220
16221 Roo.bootstrap.Popover.alignment = {
16222     'left' : ['r-l', [-10,0], 'right'],
16223     'right' : ['l-r', [10,0], 'left'],
16224     'bottom' : ['t-b', [0,10], 'top'],
16225     'top' : [ 'b-t', [0,-10], 'bottom']
16226 };
16227
16228  /*
16229  * - LGPL
16230  *
16231  * Progress
16232  * 
16233  */
16234
16235 /**
16236  * @class Roo.bootstrap.Progress
16237  * @extends Roo.bootstrap.Component
16238  * Bootstrap Progress class
16239  * @cfg {Boolean} striped striped of the progress bar
16240  * @cfg {Boolean} active animated of the progress bar
16241  * 
16242  * 
16243  * @constructor
16244  * Create a new Progress
16245  * @param {Object} config The config object
16246  */
16247
16248 Roo.bootstrap.Progress = function(config){
16249     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16250 };
16251
16252 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16253     
16254     striped : false,
16255     active: false,
16256     
16257     getAutoCreate : function(){
16258         var cfg = {
16259             tag: 'div',
16260             cls: 'progress'
16261         };
16262         
16263         
16264         if(this.striped){
16265             cfg.cls += ' progress-striped';
16266         }
16267       
16268         if(this.active){
16269             cfg.cls += ' active';
16270         }
16271         
16272         
16273         return cfg;
16274     }
16275    
16276 });
16277
16278  
16279
16280  /*
16281  * - LGPL
16282  *
16283  * ProgressBar
16284  * 
16285  */
16286
16287 /**
16288  * @class Roo.bootstrap.ProgressBar
16289  * @extends Roo.bootstrap.Component
16290  * Bootstrap ProgressBar class
16291  * @cfg {Number} aria_valuenow aria-value now
16292  * @cfg {Number} aria_valuemin aria-value min
16293  * @cfg {Number} aria_valuemax aria-value max
16294  * @cfg {String} label label for the progress bar
16295  * @cfg {String} panel (success | info | warning | danger )
16296  * @cfg {String} role role of the progress bar
16297  * @cfg {String} sr_only text
16298  * 
16299  * 
16300  * @constructor
16301  * Create a new ProgressBar
16302  * @param {Object} config The config object
16303  */
16304
16305 Roo.bootstrap.ProgressBar = function(config){
16306     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16307 };
16308
16309 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16310     
16311     aria_valuenow : 0,
16312     aria_valuemin : 0,
16313     aria_valuemax : 100,
16314     label : false,
16315     panel : false,
16316     role : false,
16317     sr_only: false,
16318     
16319     getAutoCreate : function()
16320     {
16321         
16322         var cfg = {
16323             tag: 'div',
16324             cls: 'progress-bar',
16325             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16326         };
16327         
16328         if(this.sr_only){
16329             cfg.cn = {
16330                 tag: 'span',
16331                 cls: 'sr-only',
16332                 html: this.sr_only
16333             }
16334         }
16335         
16336         if(this.role){
16337             cfg.role = this.role;
16338         }
16339         
16340         if(this.aria_valuenow){
16341             cfg['aria-valuenow'] = this.aria_valuenow;
16342         }
16343         
16344         if(this.aria_valuemin){
16345             cfg['aria-valuemin'] = this.aria_valuemin;
16346         }
16347         
16348         if(this.aria_valuemax){
16349             cfg['aria-valuemax'] = this.aria_valuemax;
16350         }
16351         
16352         if(this.label && !this.sr_only){
16353             cfg.html = this.label;
16354         }
16355         
16356         if(this.panel){
16357             cfg.cls += ' progress-bar-' + this.panel;
16358         }
16359         
16360         return cfg;
16361     },
16362     
16363     update : function(aria_valuenow)
16364     {
16365         this.aria_valuenow = aria_valuenow;
16366         
16367         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16368     }
16369    
16370 });
16371
16372  
16373
16374  /*
16375  * - LGPL
16376  *
16377  * column
16378  * 
16379  */
16380
16381 /**
16382  * @class Roo.bootstrap.TabGroup
16383  * @extends Roo.bootstrap.Column
16384  * Bootstrap Column class
16385  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16386  * @cfg {Boolean} carousel true to make the group behave like a carousel
16387  * @cfg {Boolean} bullets show bullets for the panels
16388  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16389  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16390  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16391  * 
16392  * @constructor
16393  * Create a new TabGroup
16394  * @param {Object} config The config object
16395  */
16396
16397 Roo.bootstrap.TabGroup = function(config){
16398     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16399     if (!this.navId) {
16400         this.navId = Roo.id();
16401     }
16402     this.tabs = [];
16403     Roo.bootstrap.TabGroup.register(this);
16404     
16405 };
16406
16407 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16408     
16409     carousel : false,
16410     transition : false,
16411     bullets : 0,
16412     timer : 0,
16413     autoslide : false,
16414     slideFn : false,
16415     slideOnTouch : false,
16416     
16417     getAutoCreate : function()
16418     {
16419         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16420         
16421         cfg.cls += ' tab-content';
16422         
16423         if (this.carousel) {
16424             cfg.cls += ' carousel slide';
16425             
16426             cfg.cn = [{
16427                cls : 'carousel-inner'
16428             }];
16429         
16430             if(this.bullets  && !Roo.isTouch){
16431                 
16432                 var bullets = {
16433                     cls : 'carousel-bullets',
16434                     cn : []
16435                 };
16436                
16437                 if(this.bullets_cls){
16438                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16439                 }
16440                  /*
16441                 for (var i = 0; i < this.bullets; i++){
16442                     bullets.cn.push({
16443                         cls : 'bullet bullet-' + i
16444                     });
16445                 }
16446                 */
16447                 bullets.cn.push({
16448                     cls : 'clear'
16449                 });
16450                 
16451                 cfg.cn[0].cn = bullets;
16452             }
16453         }
16454         
16455         return cfg;
16456     },
16457     
16458     initEvents:  function()
16459     {
16460         if(Roo.isTouch && this.slideOnTouch){
16461             this.el.on("touchstart", this.onTouchStart, this);
16462         }
16463         
16464         if(this.autoslide){
16465             var _this = this;
16466             
16467             this.slideFn = window.setInterval(function() {
16468                 _this.showPanelNext();
16469             }, this.timer);
16470         }
16471         
16472     },
16473     
16474     onTouchStart : function(e, el, o)
16475     {
16476         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16477             return;
16478         }
16479         
16480         this.showPanelNext();
16481     },
16482     
16483     getChildContainer : function()
16484     {
16485         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16486     },
16487     
16488     /**
16489     * register a Navigation item
16490     * @param {Roo.bootstrap.NavItem} the navitem to add
16491     */
16492     register : function(item)
16493     {
16494         this.tabs.push( item);
16495         item.navId = this.navId; // not really needed..
16496         this.addBullet();
16497     
16498     },
16499     
16500     getActivePanel : function()
16501     {
16502         var r = false;
16503         Roo.each(this.tabs, function(t) {
16504             if (t.active) {
16505                 r = t;
16506                 return false;
16507             }
16508             return null;
16509         });
16510         return r;
16511         
16512     },
16513     getPanelByName : function(n)
16514     {
16515         var r = false;
16516         Roo.each(this.tabs, function(t) {
16517             if (t.tabId == n) {
16518                 r = t;
16519                 return false;
16520             }
16521             return null;
16522         });
16523         return r;
16524     },
16525     indexOfPanel : function(p)
16526     {
16527         var r = false;
16528         Roo.each(this.tabs, function(t,i) {
16529             if (t.tabId == p.tabId) {
16530                 r = i;
16531                 return false;
16532             }
16533             return null;
16534         });
16535         return r;
16536     },
16537     /**
16538      * show a specific panel
16539      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16540      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16541      */
16542     showPanel : function (pan)
16543     {
16544         if(this.transition || typeof(pan) == 'undefined'){
16545             Roo.log("waiting for the transitionend");
16546             return;
16547         }
16548         
16549         if (typeof(pan) == 'number') {
16550             pan = this.tabs[pan];
16551         }
16552         
16553         if (typeof(pan) == 'string') {
16554             pan = this.getPanelByName(pan);
16555         }
16556         
16557         var cur = this.getActivePanel();
16558         
16559         if(!pan || !cur){
16560             Roo.log('pan or acitve pan is undefined');
16561             return false;
16562         }
16563         
16564         if (pan.tabId == this.getActivePanel().tabId) {
16565             return true;
16566         }
16567         
16568         if (false === cur.fireEvent('beforedeactivate')) {
16569             return false;
16570         }
16571         
16572         if(this.bullets > 0 && !Roo.isTouch){
16573             this.setActiveBullet(this.indexOfPanel(pan));
16574         }
16575         
16576         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16577             
16578             this.transition = true;
16579             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16580             var lr = dir == 'next' ? 'left' : 'right';
16581             pan.el.addClass(dir); // or prev
16582             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16583             cur.el.addClass(lr); // or right
16584             pan.el.addClass(lr);
16585             
16586             var _this = this;
16587             cur.el.on('transitionend', function() {
16588                 Roo.log("trans end?");
16589                 
16590                 pan.el.removeClass([lr,dir]);
16591                 pan.setActive(true);
16592                 
16593                 cur.el.removeClass([lr]);
16594                 cur.setActive(false);
16595                 
16596                 _this.transition = false;
16597                 
16598             }, this, { single:  true } );
16599             
16600             return true;
16601         }
16602         
16603         cur.setActive(false);
16604         pan.setActive(true);
16605         
16606         return true;
16607         
16608     },
16609     showPanelNext : function()
16610     {
16611         var i = this.indexOfPanel(this.getActivePanel());
16612         
16613         if (i >= this.tabs.length - 1 && !this.autoslide) {
16614             return;
16615         }
16616         
16617         if (i >= this.tabs.length - 1 && this.autoslide) {
16618             i = -1;
16619         }
16620         
16621         this.showPanel(this.tabs[i+1]);
16622     },
16623     
16624     showPanelPrev : function()
16625     {
16626         var i = this.indexOfPanel(this.getActivePanel());
16627         
16628         if (i  < 1 && !this.autoslide) {
16629             return;
16630         }
16631         
16632         if (i < 1 && this.autoslide) {
16633             i = this.tabs.length;
16634         }
16635         
16636         this.showPanel(this.tabs[i-1]);
16637     },
16638     
16639     
16640     addBullet: function()
16641     {
16642         if(!this.bullets || Roo.isTouch){
16643             return;
16644         }
16645         var ctr = this.el.select('.carousel-bullets',true).first();
16646         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16647         var bullet = ctr.createChild({
16648             cls : 'bullet bullet-' + i
16649         },ctr.dom.lastChild);
16650         
16651         
16652         var _this = this;
16653         
16654         bullet.on('click', (function(e, el, o, ii, t){
16655
16656             e.preventDefault();
16657
16658             this.showPanel(ii);
16659
16660             if(this.autoslide && this.slideFn){
16661                 clearInterval(this.slideFn);
16662                 this.slideFn = window.setInterval(function() {
16663                     _this.showPanelNext();
16664                 }, this.timer);
16665             }
16666
16667         }).createDelegate(this, [i, bullet], true));
16668                 
16669         
16670     },
16671      
16672     setActiveBullet : function(i)
16673     {
16674         if(Roo.isTouch){
16675             return;
16676         }
16677         
16678         Roo.each(this.el.select('.bullet', true).elements, function(el){
16679             el.removeClass('selected');
16680         });
16681
16682         var bullet = this.el.select('.bullet-' + i, true).first();
16683         
16684         if(!bullet){
16685             return;
16686         }
16687         
16688         bullet.addClass('selected');
16689     }
16690     
16691     
16692   
16693 });
16694
16695  
16696
16697  
16698  
16699 Roo.apply(Roo.bootstrap.TabGroup, {
16700     
16701     groups: {},
16702      /**
16703     * register a Navigation Group
16704     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16705     */
16706     register : function(navgrp)
16707     {
16708         this.groups[navgrp.navId] = navgrp;
16709         
16710     },
16711     /**
16712     * fetch a Navigation Group based on the navigation ID
16713     * if one does not exist , it will get created.
16714     * @param {string} the navgroup to add
16715     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16716     */
16717     get: function(navId) {
16718         if (typeof(this.groups[navId]) == 'undefined') {
16719             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16720         }
16721         return this.groups[navId] ;
16722     }
16723     
16724     
16725     
16726 });
16727
16728  /*
16729  * - LGPL
16730  *
16731  * TabPanel
16732  * 
16733  */
16734
16735 /**
16736  * @class Roo.bootstrap.TabPanel
16737  * @extends Roo.bootstrap.Component
16738  * Bootstrap TabPanel class
16739  * @cfg {Boolean} active panel active
16740  * @cfg {String} html panel content
16741  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16742  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16743  * 
16744  * 
16745  * @constructor
16746  * Create a new TabPanel
16747  * @param {Object} config The config object
16748  */
16749
16750 Roo.bootstrap.TabPanel = function(config){
16751     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16752     this.addEvents({
16753         /**
16754              * @event changed
16755              * Fires when the active status changes
16756              * @param {Roo.bootstrap.TabPanel} this
16757              * @param {Boolean} state the new state
16758             
16759          */
16760         'changed': true,
16761         /**
16762              * @event beforedeactivate
16763              * Fires before a tab is de-activated - can be used to do validation on a form.
16764              * @param {Roo.bootstrap.TabPanel} this
16765              * @return {Boolean} false if there is an error
16766             
16767          */
16768         'beforedeactivate': true
16769      });
16770     
16771     this.tabId = this.tabId || Roo.id();
16772   
16773 };
16774
16775 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16776     
16777     active: false,
16778     html: false,
16779     tabId: false,
16780     navId : false,
16781     
16782     getAutoCreate : function(){
16783         var cfg = {
16784             tag: 'div',
16785             // item is needed for carousel - not sure if it has any effect otherwise
16786             cls: 'tab-pane item',
16787             html: this.html || ''
16788         };
16789         
16790         if(this.active){
16791             cfg.cls += ' active';
16792         }
16793         
16794         if(this.tabId){
16795             cfg.tabId = this.tabId;
16796         }
16797         
16798         
16799         return cfg;
16800     },
16801     
16802     initEvents:  function()
16803     {
16804         var p = this.parent();
16805         this.navId = this.navId || p.navId;
16806         
16807         if (typeof(this.navId) != 'undefined') {
16808             // not really needed.. but just in case.. parent should be a NavGroup.
16809             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16810             
16811             tg.register(this);
16812             
16813             var i = tg.tabs.length - 1;
16814             
16815             if(this.active && tg.bullets > 0 && i < tg.bullets){
16816                 tg.setActiveBullet(i);
16817             }
16818         }
16819         
16820     },
16821     
16822     
16823     onRender : function(ct, position)
16824     {
16825        // Roo.log("Call onRender: " + this.xtype);
16826         
16827         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16828         
16829         
16830         
16831         
16832         
16833     },
16834     
16835     setActive: function(state)
16836     {
16837         Roo.log("panel - set active " + this.tabId + "=" + state);
16838         
16839         this.active = state;
16840         if (!state) {
16841             this.el.removeClass('active');
16842             
16843         } else  if (!this.el.hasClass('active')) {
16844             this.el.addClass('active');
16845         }
16846         
16847         this.fireEvent('changed', this, state);
16848     }
16849     
16850     
16851 });
16852  
16853
16854  
16855
16856  /*
16857  * - LGPL
16858  *
16859  * DateField
16860  * 
16861  */
16862
16863 /**
16864  * @class Roo.bootstrap.DateField
16865  * @extends Roo.bootstrap.Input
16866  * Bootstrap DateField class
16867  * @cfg {Number} weekStart default 0
16868  * @cfg {String} viewMode default empty, (months|years)
16869  * @cfg {String} minViewMode default empty, (months|years)
16870  * @cfg {Number} startDate default -Infinity
16871  * @cfg {Number} endDate default Infinity
16872  * @cfg {Boolean} todayHighlight default false
16873  * @cfg {Boolean} todayBtn default false
16874  * @cfg {Boolean} calendarWeeks default false
16875  * @cfg {Object} daysOfWeekDisabled default empty
16876  * @cfg {Boolean} singleMode default false (true | false)
16877  * 
16878  * @cfg {Boolean} keyboardNavigation default true
16879  * @cfg {String} language default en
16880  * 
16881  * @constructor
16882  * Create a new DateField
16883  * @param {Object} config The config object
16884  */
16885
16886 Roo.bootstrap.DateField = function(config){
16887     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16888      this.addEvents({
16889             /**
16890              * @event show
16891              * Fires when this field show.
16892              * @param {Roo.bootstrap.DateField} this
16893              * @param {Mixed} date The date value
16894              */
16895             show : true,
16896             /**
16897              * @event show
16898              * Fires when this field hide.
16899              * @param {Roo.bootstrap.DateField} this
16900              * @param {Mixed} date The date value
16901              */
16902             hide : true,
16903             /**
16904              * @event select
16905              * Fires when select a date.
16906              * @param {Roo.bootstrap.DateField} this
16907              * @param {Mixed} date The date value
16908              */
16909             select : true,
16910             /**
16911              * @event beforeselect
16912              * Fires when before select a date.
16913              * @param {Roo.bootstrap.DateField} this
16914              * @param {Mixed} date The date value
16915              */
16916             beforeselect : true
16917         });
16918 };
16919
16920 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16921     
16922     /**
16923      * @cfg {String} format
16924      * The default date format string which can be overriden for localization support.  The format must be
16925      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16926      */
16927     format : "m/d/y",
16928     /**
16929      * @cfg {String} altFormats
16930      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16931      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16932      */
16933     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16934     
16935     weekStart : 0,
16936     
16937     viewMode : '',
16938     
16939     minViewMode : '',
16940     
16941     todayHighlight : false,
16942     
16943     todayBtn: false,
16944     
16945     language: 'en',
16946     
16947     keyboardNavigation: true,
16948     
16949     calendarWeeks: false,
16950     
16951     startDate: -Infinity,
16952     
16953     endDate: Infinity,
16954     
16955     daysOfWeekDisabled: [],
16956     
16957     _events: [],
16958     
16959     singleMode : false,
16960     
16961     UTCDate: function()
16962     {
16963         return new Date(Date.UTC.apply(Date, arguments));
16964     },
16965     
16966     UTCToday: function()
16967     {
16968         var today = new Date();
16969         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16970     },
16971     
16972     getDate: function() {
16973             var d = this.getUTCDate();
16974             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16975     },
16976     
16977     getUTCDate: function() {
16978             return this.date;
16979     },
16980     
16981     setDate: function(d) {
16982             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16983     },
16984     
16985     setUTCDate: function(d) {
16986             this.date = d;
16987             this.setValue(this.formatDate(this.date));
16988     },
16989         
16990     onRender: function(ct, position)
16991     {
16992         
16993         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16994         
16995         this.language = this.language || 'en';
16996         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16997         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16998         
16999         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17000         this.format = this.format || 'm/d/y';
17001         this.isInline = false;
17002         this.isInput = true;
17003         this.component = this.el.select('.add-on', true).first() || false;
17004         this.component = (this.component && this.component.length === 0) ? false : this.component;
17005         this.hasInput = this.component && this.inputEL().length;
17006         
17007         if (typeof(this.minViewMode === 'string')) {
17008             switch (this.minViewMode) {
17009                 case 'months':
17010                     this.minViewMode = 1;
17011                     break;
17012                 case 'years':
17013                     this.minViewMode = 2;
17014                     break;
17015                 default:
17016                     this.minViewMode = 0;
17017                     break;
17018             }
17019         }
17020         
17021         if (typeof(this.viewMode === 'string')) {
17022             switch (this.viewMode) {
17023                 case 'months':
17024                     this.viewMode = 1;
17025                     break;
17026                 case 'years':
17027                     this.viewMode = 2;
17028                     break;
17029                 default:
17030                     this.viewMode = 0;
17031                     break;
17032             }
17033         }
17034                 
17035         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17036         
17037 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17038         
17039         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17040         
17041         this.picker().on('mousedown', this.onMousedown, this);
17042         this.picker().on('click', this.onClick, this);
17043         
17044         this.picker().addClass('datepicker-dropdown');
17045         
17046         this.startViewMode = this.viewMode;
17047         
17048         if(this.singleMode){
17049             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17050                 v.setVisibilityMode(Roo.Element.DISPLAY);
17051                 v.hide();
17052             });
17053             
17054             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17055                 v.setStyle('width', '189px');
17056             });
17057         }
17058         
17059         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17060             if(!this.calendarWeeks){
17061                 v.remove();
17062                 return;
17063             }
17064             
17065             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17066             v.attr('colspan', function(i, val){
17067                 return parseInt(val) + 1;
17068             });
17069         });
17070                         
17071         
17072         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17073         
17074         this.setStartDate(this.startDate);
17075         this.setEndDate(this.endDate);
17076         
17077         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17078         
17079         this.fillDow();
17080         this.fillMonths();
17081         this.update();
17082         this.showMode();
17083         
17084         if(this.isInline) {
17085             this.show();
17086         }
17087     },
17088     
17089     picker : function()
17090     {
17091         return this.pickerEl;
17092 //        return this.el.select('.datepicker', true).first();
17093     },
17094     
17095     fillDow: function()
17096     {
17097         var dowCnt = this.weekStart;
17098         
17099         var dow = {
17100             tag: 'tr',
17101             cn: [
17102                 
17103             ]
17104         };
17105         
17106         if(this.calendarWeeks){
17107             dow.cn.push({
17108                 tag: 'th',
17109                 cls: 'cw',
17110                 html: '&nbsp;'
17111             })
17112         }
17113         
17114         while (dowCnt < this.weekStart + 7) {
17115             dow.cn.push({
17116                 tag: 'th',
17117                 cls: 'dow',
17118                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17119             });
17120         }
17121         
17122         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17123     },
17124     
17125     fillMonths: function()
17126     {    
17127         var i = 0;
17128         var months = this.picker().select('>.datepicker-months td', true).first();
17129         
17130         months.dom.innerHTML = '';
17131         
17132         while (i < 12) {
17133             var month = {
17134                 tag: 'span',
17135                 cls: 'month',
17136                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17137             };
17138             
17139             months.createChild(month);
17140         }
17141         
17142     },
17143     
17144     update: function()
17145     {
17146         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;
17147         
17148         if (this.date < this.startDate) {
17149             this.viewDate = new Date(this.startDate);
17150         } else if (this.date > this.endDate) {
17151             this.viewDate = new Date(this.endDate);
17152         } else {
17153             this.viewDate = new Date(this.date);
17154         }
17155         
17156         this.fill();
17157     },
17158     
17159     fill: function() 
17160     {
17161         var d = new Date(this.viewDate),
17162                 year = d.getUTCFullYear(),
17163                 month = d.getUTCMonth(),
17164                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17165                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17166                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17167                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17168                 currentDate = this.date && this.date.valueOf(),
17169                 today = this.UTCToday();
17170         
17171         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17172         
17173 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17174         
17175 //        this.picker.select('>tfoot th.today').
17176 //                                              .text(dates[this.language].today)
17177 //                                              .toggle(this.todayBtn !== false);
17178     
17179         this.updateNavArrows();
17180         this.fillMonths();
17181                                                 
17182         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17183         
17184         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17185          
17186         prevMonth.setUTCDate(day);
17187         
17188         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17189         
17190         var nextMonth = new Date(prevMonth);
17191         
17192         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17193         
17194         nextMonth = nextMonth.valueOf();
17195         
17196         var fillMonths = false;
17197         
17198         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17199         
17200         while(prevMonth.valueOf() < nextMonth) {
17201             var clsName = '';
17202             
17203             if (prevMonth.getUTCDay() === this.weekStart) {
17204                 if(fillMonths){
17205                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17206                 }
17207                     
17208                 fillMonths = {
17209                     tag: 'tr',
17210                     cn: []
17211                 };
17212                 
17213                 if(this.calendarWeeks){
17214                     // ISO 8601: First week contains first thursday.
17215                     // ISO also states week starts on Monday, but we can be more abstract here.
17216                     var
17217                     // Start of current week: based on weekstart/current date
17218                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17219                     // Thursday of this week
17220                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17221                     // First Thursday of year, year from thursday
17222                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17223                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17224                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17225                     
17226                     fillMonths.cn.push({
17227                         tag: 'td',
17228                         cls: 'cw',
17229                         html: calWeek
17230                     });
17231                 }
17232             }
17233             
17234             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17235                 clsName += ' old';
17236             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17237                 clsName += ' new';
17238             }
17239             if (this.todayHighlight &&
17240                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17241                 prevMonth.getUTCMonth() == today.getMonth() &&
17242                 prevMonth.getUTCDate() == today.getDate()) {
17243                 clsName += ' today';
17244             }
17245             
17246             if (currentDate && prevMonth.valueOf() === currentDate) {
17247                 clsName += ' active';
17248             }
17249             
17250             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17251                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17252                     clsName += ' disabled';
17253             }
17254             
17255             fillMonths.cn.push({
17256                 tag: 'td',
17257                 cls: 'day ' + clsName,
17258                 html: prevMonth.getDate()
17259             });
17260             
17261             prevMonth.setDate(prevMonth.getDate()+1);
17262         }
17263           
17264         var currentYear = this.date && this.date.getUTCFullYear();
17265         var currentMonth = this.date && this.date.getUTCMonth();
17266         
17267         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17268         
17269         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17270             v.removeClass('active');
17271             
17272             if(currentYear === year && k === currentMonth){
17273                 v.addClass('active');
17274             }
17275             
17276             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17277                 v.addClass('disabled');
17278             }
17279             
17280         });
17281         
17282         
17283         year = parseInt(year/10, 10) * 10;
17284         
17285         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17286         
17287         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17288         
17289         year -= 1;
17290         for (var i = -1; i < 11; i++) {
17291             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17292                 tag: 'span',
17293                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17294                 html: year
17295             });
17296             
17297             year += 1;
17298         }
17299     },
17300     
17301     showMode: function(dir) 
17302     {
17303         if (dir) {
17304             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17305         }
17306         
17307         Roo.each(this.picker().select('>div',true).elements, function(v){
17308             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17309             v.hide();
17310         });
17311         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17312     },
17313     
17314     place: function()
17315     {
17316         if(this.isInline) {
17317             return;
17318         }
17319         
17320         this.picker().removeClass(['bottom', 'top']);
17321         
17322         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17323             /*
17324              * place to the top of element!
17325              *
17326              */
17327             
17328             this.picker().addClass('top');
17329             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17330             
17331             return;
17332         }
17333         
17334         this.picker().addClass('bottom');
17335         
17336         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17337     },
17338     
17339     parseDate : function(value)
17340     {
17341         if(!value || value instanceof Date){
17342             return value;
17343         }
17344         var v = Date.parseDate(value, this.format);
17345         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17346             v = Date.parseDate(value, 'Y-m-d');
17347         }
17348         if(!v && this.altFormats){
17349             if(!this.altFormatsArray){
17350                 this.altFormatsArray = this.altFormats.split("|");
17351             }
17352             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17353                 v = Date.parseDate(value, this.altFormatsArray[i]);
17354             }
17355         }
17356         return v;
17357     },
17358     
17359     formatDate : function(date, fmt)
17360     {   
17361         return (!date || !(date instanceof Date)) ?
17362         date : date.dateFormat(fmt || this.format);
17363     },
17364     
17365     onFocus : function()
17366     {
17367         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17368         this.show();
17369     },
17370     
17371     onBlur : function()
17372     {
17373         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17374         
17375         var d = this.inputEl().getValue();
17376         
17377         this.setValue(d);
17378                 
17379         this.hide();
17380     },
17381     
17382     show : function()
17383     {
17384         this.picker().show();
17385         this.update();
17386         this.place();
17387         
17388         this.fireEvent('show', this, this.date);
17389     },
17390     
17391     hide : function()
17392     {
17393         if(this.isInline) {
17394             return;
17395         }
17396         this.picker().hide();
17397         this.viewMode = this.startViewMode;
17398         this.showMode();
17399         
17400         this.fireEvent('hide', this, this.date);
17401         
17402     },
17403     
17404     onMousedown: function(e)
17405     {
17406         e.stopPropagation();
17407         e.preventDefault();
17408     },
17409     
17410     keyup: function(e)
17411     {
17412         Roo.bootstrap.DateField.superclass.keyup.call(this);
17413         this.update();
17414     },
17415
17416     setValue: function(v)
17417     {
17418         if(this.fireEvent('beforeselect', this, v) !== false){
17419             var d = new Date(this.parseDate(v) ).clearTime();
17420         
17421             if(isNaN(d.getTime())){
17422                 this.date = this.viewDate = '';
17423                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17424                 return;
17425             }
17426
17427             v = this.formatDate(d);
17428
17429             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17430
17431             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17432
17433             this.update();
17434
17435             this.fireEvent('select', this, this.date);
17436         }
17437     },
17438     
17439     getValue: function()
17440     {
17441         return this.formatDate(this.date);
17442     },
17443     
17444     fireKey: function(e)
17445     {
17446         if (!this.picker().isVisible()){
17447             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17448                 this.show();
17449             }
17450             return;
17451         }
17452         
17453         var dateChanged = false,
17454         dir, day, month,
17455         newDate, newViewDate;
17456         
17457         switch(e.keyCode){
17458             case 27: // escape
17459                 this.hide();
17460                 e.preventDefault();
17461                 break;
17462             case 37: // left
17463             case 39: // right
17464                 if (!this.keyboardNavigation) {
17465                     break;
17466                 }
17467                 dir = e.keyCode == 37 ? -1 : 1;
17468                 
17469                 if (e.ctrlKey){
17470                     newDate = this.moveYear(this.date, dir);
17471                     newViewDate = this.moveYear(this.viewDate, dir);
17472                 } else if (e.shiftKey){
17473                     newDate = this.moveMonth(this.date, dir);
17474                     newViewDate = this.moveMonth(this.viewDate, dir);
17475                 } else {
17476                     newDate = new Date(this.date);
17477                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17478                     newViewDate = new Date(this.viewDate);
17479                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17480                 }
17481                 if (this.dateWithinRange(newDate)){
17482                     this.date = newDate;
17483                     this.viewDate = newViewDate;
17484                     this.setValue(this.formatDate(this.date));
17485 //                    this.update();
17486                     e.preventDefault();
17487                     dateChanged = true;
17488                 }
17489                 break;
17490             case 38: // up
17491             case 40: // down
17492                 if (!this.keyboardNavigation) {
17493                     break;
17494                 }
17495                 dir = e.keyCode == 38 ? -1 : 1;
17496                 if (e.ctrlKey){
17497                     newDate = this.moveYear(this.date, dir);
17498                     newViewDate = this.moveYear(this.viewDate, dir);
17499                 } else if (e.shiftKey){
17500                     newDate = this.moveMonth(this.date, dir);
17501                     newViewDate = this.moveMonth(this.viewDate, dir);
17502                 } else {
17503                     newDate = new Date(this.date);
17504                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17505                     newViewDate = new Date(this.viewDate);
17506                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17507                 }
17508                 if (this.dateWithinRange(newDate)){
17509                     this.date = newDate;
17510                     this.viewDate = newViewDate;
17511                     this.setValue(this.formatDate(this.date));
17512 //                    this.update();
17513                     e.preventDefault();
17514                     dateChanged = true;
17515                 }
17516                 break;
17517             case 13: // enter
17518                 this.setValue(this.formatDate(this.date));
17519                 this.hide();
17520                 e.preventDefault();
17521                 break;
17522             case 9: // tab
17523                 this.setValue(this.formatDate(this.date));
17524                 this.hide();
17525                 break;
17526             case 16: // shift
17527             case 17: // ctrl
17528             case 18: // alt
17529                 break;
17530             default :
17531                 this.hide();
17532                 
17533         }
17534     },
17535     
17536     
17537     onClick: function(e) 
17538     {
17539         e.stopPropagation();
17540         e.preventDefault();
17541         
17542         var target = e.getTarget();
17543         
17544         if(target.nodeName.toLowerCase() === 'i'){
17545             target = Roo.get(target).dom.parentNode;
17546         }
17547         
17548         var nodeName = target.nodeName;
17549         var className = target.className;
17550         var html = target.innerHTML;
17551         //Roo.log(nodeName);
17552         
17553         switch(nodeName.toLowerCase()) {
17554             case 'th':
17555                 switch(className) {
17556                     case 'switch':
17557                         this.showMode(1);
17558                         break;
17559                     case 'prev':
17560                     case 'next':
17561                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17562                         switch(this.viewMode){
17563                                 case 0:
17564                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17565                                         break;
17566                                 case 1:
17567                                 case 2:
17568                                         this.viewDate = this.moveYear(this.viewDate, dir);
17569                                         break;
17570                         }
17571                         this.fill();
17572                         break;
17573                     case 'today':
17574                         var date = new Date();
17575                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17576 //                        this.fill()
17577                         this.setValue(this.formatDate(this.date));
17578                         
17579                         this.hide();
17580                         break;
17581                 }
17582                 break;
17583             case 'span':
17584                 if (className.indexOf('disabled') < 0) {
17585                     this.viewDate.setUTCDate(1);
17586                     if (className.indexOf('month') > -1) {
17587                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17588                     } else {
17589                         var year = parseInt(html, 10) || 0;
17590                         this.viewDate.setUTCFullYear(year);
17591                         
17592                     }
17593                     
17594                     if(this.singleMode){
17595                         this.setValue(this.formatDate(this.viewDate));
17596                         this.hide();
17597                         return;
17598                     }
17599                     
17600                     this.showMode(-1);
17601                     this.fill();
17602                 }
17603                 break;
17604                 
17605             case 'td':
17606                 //Roo.log(className);
17607                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17608                     var day = parseInt(html, 10) || 1;
17609                     var year = this.viewDate.getUTCFullYear(),
17610                         month = this.viewDate.getUTCMonth();
17611
17612                     if (className.indexOf('old') > -1) {
17613                         if(month === 0 ){
17614                             month = 11;
17615                             year -= 1;
17616                         }else{
17617                             month -= 1;
17618                         }
17619                     } else if (className.indexOf('new') > -1) {
17620                         if (month == 11) {
17621                             month = 0;
17622                             year += 1;
17623                         } else {
17624                             month += 1;
17625                         }
17626                     }
17627                     //Roo.log([year,month,day]);
17628                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17629                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17630 //                    this.fill();
17631                     //Roo.log(this.formatDate(this.date));
17632                     this.setValue(this.formatDate(this.date));
17633                     this.hide();
17634                 }
17635                 break;
17636         }
17637     },
17638     
17639     setStartDate: function(startDate)
17640     {
17641         this.startDate = startDate || -Infinity;
17642         if (this.startDate !== -Infinity) {
17643             this.startDate = this.parseDate(this.startDate);
17644         }
17645         this.update();
17646         this.updateNavArrows();
17647     },
17648
17649     setEndDate: function(endDate)
17650     {
17651         this.endDate = endDate || Infinity;
17652         if (this.endDate !== Infinity) {
17653             this.endDate = this.parseDate(this.endDate);
17654         }
17655         this.update();
17656         this.updateNavArrows();
17657     },
17658     
17659     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17660     {
17661         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17662         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17663             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17664         }
17665         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17666             return parseInt(d, 10);
17667         });
17668         this.update();
17669         this.updateNavArrows();
17670     },
17671     
17672     updateNavArrows: function() 
17673     {
17674         if(this.singleMode){
17675             return;
17676         }
17677         
17678         var d = new Date(this.viewDate),
17679         year = d.getUTCFullYear(),
17680         month = d.getUTCMonth();
17681         
17682         Roo.each(this.picker().select('.prev', true).elements, function(v){
17683             v.show();
17684             switch (this.viewMode) {
17685                 case 0:
17686
17687                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17688                         v.hide();
17689                     }
17690                     break;
17691                 case 1:
17692                 case 2:
17693                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17694                         v.hide();
17695                     }
17696                     break;
17697             }
17698         });
17699         
17700         Roo.each(this.picker().select('.next', true).elements, function(v){
17701             v.show();
17702             switch (this.viewMode) {
17703                 case 0:
17704
17705                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17706                         v.hide();
17707                     }
17708                     break;
17709                 case 1:
17710                 case 2:
17711                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17712                         v.hide();
17713                     }
17714                     break;
17715             }
17716         })
17717     },
17718     
17719     moveMonth: function(date, dir)
17720     {
17721         if (!dir) {
17722             return date;
17723         }
17724         var new_date = new Date(date.valueOf()),
17725         day = new_date.getUTCDate(),
17726         month = new_date.getUTCMonth(),
17727         mag = Math.abs(dir),
17728         new_month, test;
17729         dir = dir > 0 ? 1 : -1;
17730         if (mag == 1){
17731             test = dir == -1
17732             // If going back one month, make sure month is not current month
17733             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17734             ? function(){
17735                 return new_date.getUTCMonth() == month;
17736             }
17737             // If going forward one month, make sure month is as expected
17738             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17739             : function(){
17740                 return new_date.getUTCMonth() != new_month;
17741             };
17742             new_month = month + dir;
17743             new_date.setUTCMonth(new_month);
17744             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17745             if (new_month < 0 || new_month > 11) {
17746                 new_month = (new_month + 12) % 12;
17747             }
17748         } else {
17749             // For magnitudes >1, move one month at a time...
17750             for (var i=0; i<mag; i++) {
17751                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17752                 new_date = this.moveMonth(new_date, dir);
17753             }
17754             // ...then reset the day, keeping it in the new month
17755             new_month = new_date.getUTCMonth();
17756             new_date.setUTCDate(day);
17757             test = function(){
17758                 return new_month != new_date.getUTCMonth();
17759             };
17760         }
17761         // Common date-resetting loop -- if date is beyond end of month, make it
17762         // end of month
17763         while (test()){
17764             new_date.setUTCDate(--day);
17765             new_date.setUTCMonth(new_month);
17766         }
17767         return new_date;
17768     },
17769
17770     moveYear: function(date, dir)
17771     {
17772         return this.moveMonth(date, dir*12);
17773     },
17774
17775     dateWithinRange: function(date)
17776     {
17777         return date >= this.startDate && date <= this.endDate;
17778     },
17779
17780     
17781     remove: function() 
17782     {
17783         this.picker().remove();
17784     }
17785    
17786 });
17787
17788 Roo.apply(Roo.bootstrap.DateField,  {
17789     
17790     head : {
17791         tag: 'thead',
17792         cn: [
17793         {
17794             tag: 'tr',
17795             cn: [
17796             {
17797                 tag: 'th',
17798                 cls: 'prev',
17799                 html: '<i class="fa fa-arrow-left"/>'
17800             },
17801             {
17802                 tag: 'th',
17803                 cls: 'switch',
17804                 colspan: '5'
17805             },
17806             {
17807                 tag: 'th',
17808                 cls: 'next',
17809                 html: '<i class="fa fa-arrow-right"/>'
17810             }
17811
17812             ]
17813         }
17814         ]
17815     },
17816     
17817     content : {
17818         tag: 'tbody',
17819         cn: [
17820         {
17821             tag: 'tr',
17822             cn: [
17823             {
17824                 tag: 'td',
17825                 colspan: '7'
17826             }
17827             ]
17828         }
17829         ]
17830     },
17831     
17832     footer : {
17833         tag: 'tfoot',
17834         cn: [
17835         {
17836             tag: 'tr',
17837             cn: [
17838             {
17839                 tag: 'th',
17840                 colspan: '7',
17841                 cls: 'today'
17842             }
17843                     
17844             ]
17845         }
17846         ]
17847     },
17848     
17849     dates:{
17850         en: {
17851             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17852             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17853             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17854             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17855             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17856             today: "Today"
17857         }
17858     },
17859     
17860     modes: [
17861     {
17862         clsName: 'days',
17863         navFnc: 'Month',
17864         navStep: 1
17865     },
17866     {
17867         clsName: 'months',
17868         navFnc: 'FullYear',
17869         navStep: 1
17870     },
17871     {
17872         clsName: 'years',
17873         navFnc: 'FullYear',
17874         navStep: 10
17875     }]
17876 });
17877
17878 Roo.apply(Roo.bootstrap.DateField,  {
17879   
17880     template : {
17881         tag: 'div',
17882         cls: 'datepicker dropdown-menu roo-dynamic',
17883         cn: [
17884         {
17885             tag: 'div',
17886             cls: 'datepicker-days',
17887             cn: [
17888             {
17889                 tag: 'table',
17890                 cls: 'table-condensed',
17891                 cn:[
17892                 Roo.bootstrap.DateField.head,
17893                 {
17894                     tag: 'tbody'
17895                 },
17896                 Roo.bootstrap.DateField.footer
17897                 ]
17898             }
17899             ]
17900         },
17901         {
17902             tag: 'div',
17903             cls: 'datepicker-months',
17904             cn: [
17905             {
17906                 tag: 'table',
17907                 cls: 'table-condensed',
17908                 cn:[
17909                 Roo.bootstrap.DateField.head,
17910                 Roo.bootstrap.DateField.content,
17911                 Roo.bootstrap.DateField.footer
17912                 ]
17913             }
17914             ]
17915         },
17916         {
17917             tag: 'div',
17918             cls: 'datepicker-years',
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     }
17933 });
17934
17935  
17936
17937  /*
17938  * - LGPL
17939  *
17940  * TimeField
17941  * 
17942  */
17943
17944 /**
17945  * @class Roo.bootstrap.TimeField
17946  * @extends Roo.bootstrap.Input
17947  * Bootstrap DateField class
17948  * 
17949  * 
17950  * @constructor
17951  * Create a new TimeField
17952  * @param {Object} config The config object
17953  */
17954
17955 Roo.bootstrap.TimeField = function(config){
17956     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17957     this.addEvents({
17958             /**
17959              * @event show
17960              * Fires when this field show.
17961              * @param {Roo.bootstrap.DateField} thisthis
17962              * @param {Mixed} date The date value
17963              */
17964             show : true,
17965             /**
17966              * @event show
17967              * Fires when this field hide.
17968              * @param {Roo.bootstrap.DateField} this
17969              * @param {Mixed} date The date value
17970              */
17971             hide : true,
17972             /**
17973              * @event select
17974              * Fires when select a date.
17975              * @param {Roo.bootstrap.DateField} this
17976              * @param {Mixed} date The date value
17977              */
17978             select : true
17979         });
17980 };
17981
17982 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17983     
17984     /**
17985      * @cfg {String} format
17986      * The default time format string which can be overriden for localization support.  The format must be
17987      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17988      */
17989     format : "H:i",
17990        
17991     onRender: function(ct, position)
17992     {
17993         
17994         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17995                 
17996         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17997         
17998         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17999         
18000         this.pop = this.picker().select('>.datepicker-time',true).first();
18001         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18002         
18003         this.picker().on('mousedown', this.onMousedown, this);
18004         this.picker().on('click', this.onClick, this);
18005         
18006         this.picker().addClass('datepicker-dropdown');
18007     
18008         this.fillTime();
18009         this.update();
18010             
18011         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18012         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18013         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18014         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18015         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18016         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18017
18018     },
18019     
18020     fireKey: function(e){
18021         if (!this.picker().isVisible()){
18022             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18023                 this.show();
18024             }
18025             return;
18026         }
18027
18028         e.preventDefault();
18029         
18030         switch(e.keyCode){
18031             case 27: // escape
18032                 this.hide();
18033                 break;
18034             case 37: // left
18035             case 39: // right
18036                 this.onTogglePeriod();
18037                 break;
18038             case 38: // up
18039                 this.onIncrementMinutes();
18040                 break;
18041             case 40: // down
18042                 this.onDecrementMinutes();
18043                 break;
18044             case 13: // enter
18045             case 9: // tab
18046                 this.setTime();
18047                 break;
18048         }
18049     },
18050     
18051     onClick: function(e) {
18052         e.stopPropagation();
18053         e.preventDefault();
18054     },
18055     
18056     picker : function()
18057     {
18058         return this.el.select('.datepicker', true).first();
18059     },
18060     
18061     fillTime: function()
18062     {    
18063         var time = this.pop.select('tbody', true).first();
18064         
18065         time.dom.innerHTML = '';
18066         
18067         time.createChild({
18068             tag: 'tr',
18069             cn: [
18070                 {
18071                     tag: 'td',
18072                     cn: [
18073                         {
18074                             tag: 'a',
18075                             href: '#',
18076                             cls: 'btn',
18077                             cn: [
18078                                 {
18079                                     tag: 'span',
18080                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18081                                 }
18082                             ]
18083                         } 
18084                     ]
18085                 },
18086                 {
18087                     tag: 'td',
18088                     cls: 'separator'
18089                 },
18090                 {
18091                     tag: 'td',
18092                     cn: [
18093                         {
18094                             tag: 'a',
18095                             href: '#',
18096                             cls: 'btn',
18097                             cn: [
18098                                 {
18099                                     tag: 'span',
18100                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18101                                 }
18102                             ]
18103                         }
18104                     ]
18105                 },
18106                 {
18107                     tag: 'td',
18108                     cls: 'separator'
18109                 }
18110             ]
18111         });
18112         
18113         time.createChild({
18114             tag: 'tr',
18115             cn: [
18116                 {
18117                     tag: 'td',
18118                     cn: [
18119                         {
18120                             tag: 'span',
18121                             cls: 'timepicker-hour',
18122                             html: '00'
18123                         }  
18124                     ]
18125                 },
18126                 {
18127                     tag: 'td',
18128                     cls: 'separator',
18129                     html: ':'
18130                 },
18131                 {
18132                     tag: 'td',
18133                     cn: [
18134                         {
18135                             tag: 'span',
18136                             cls: 'timepicker-minute',
18137                             html: '00'
18138                         }  
18139                     ]
18140                 },
18141                 {
18142                     tag: 'td',
18143                     cls: 'separator'
18144                 },
18145                 {
18146                     tag: 'td',
18147                     cn: [
18148                         {
18149                             tag: 'button',
18150                             type: 'button',
18151                             cls: 'btn btn-primary period',
18152                             html: 'AM'
18153                             
18154                         }
18155                     ]
18156                 }
18157             ]
18158         });
18159         
18160         time.createChild({
18161             tag: 'tr',
18162             cn: [
18163                 {
18164                     tag: 'td',
18165                     cn: [
18166                         {
18167                             tag: 'a',
18168                             href: '#',
18169                             cls: 'btn',
18170                             cn: [
18171                                 {
18172                                     tag: 'span',
18173                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18174                                 }
18175                             ]
18176                         }
18177                     ]
18178                 },
18179                 {
18180                     tag: 'td',
18181                     cls: 'separator'
18182                 },
18183                 {
18184                     tag: 'td',
18185                     cn: [
18186                         {
18187                             tag: 'a',
18188                             href: '#',
18189                             cls: 'btn',
18190                             cn: [
18191                                 {
18192                                     tag: 'span',
18193                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18194                                 }
18195                             ]
18196                         }
18197                     ]
18198                 },
18199                 {
18200                     tag: 'td',
18201                     cls: 'separator'
18202                 }
18203             ]
18204         });
18205         
18206     },
18207     
18208     update: function()
18209     {
18210         
18211         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18212         
18213         this.fill();
18214     },
18215     
18216     fill: function() 
18217     {
18218         var hours = this.time.getHours();
18219         var minutes = this.time.getMinutes();
18220         var period = 'AM';
18221         
18222         if(hours > 11){
18223             period = 'PM';
18224         }
18225         
18226         if(hours == 0){
18227             hours = 12;
18228         }
18229         
18230         
18231         if(hours > 12){
18232             hours = hours - 12;
18233         }
18234         
18235         if(hours < 10){
18236             hours = '0' + hours;
18237         }
18238         
18239         if(minutes < 10){
18240             minutes = '0' + minutes;
18241         }
18242         
18243         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18244         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18245         this.pop.select('button', true).first().dom.innerHTML = period;
18246         
18247     },
18248     
18249     place: function()
18250     {   
18251         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18252         
18253         var cls = ['bottom'];
18254         
18255         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18256             cls.pop();
18257             cls.push('top');
18258         }
18259         
18260         cls.push('right');
18261         
18262         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18263             cls.pop();
18264             cls.push('left');
18265         }
18266         
18267         this.picker().addClass(cls.join('-'));
18268         
18269         var _this = this;
18270         
18271         Roo.each(cls, function(c){
18272             if(c == 'bottom'){
18273                 _this.picker().setTop(_this.inputEl().getHeight());
18274                 return;
18275             }
18276             if(c == 'top'){
18277                 _this.picker().setTop(0 - _this.picker().getHeight());
18278                 return;
18279             }
18280             
18281             if(c == 'left'){
18282                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18283                 return;
18284             }
18285             if(c == 'right'){
18286                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18287                 return;
18288             }
18289         });
18290         
18291     },
18292   
18293     onFocus : function()
18294     {
18295         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18296         this.show();
18297     },
18298     
18299     onBlur : function()
18300     {
18301         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18302         this.hide();
18303     },
18304     
18305     show : function()
18306     {
18307         this.picker().show();
18308         this.pop.show();
18309         this.update();
18310         this.place();
18311         
18312         this.fireEvent('show', this, this.date);
18313     },
18314     
18315     hide : function()
18316     {
18317         this.picker().hide();
18318         this.pop.hide();
18319         
18320         this.fireEvent('hide', this, this.date);
18321     },
18322     
18323     setTime : function()
18324     {
18325         this.hide();
18326         this.setValue(this.time.format(this.format));
18327         
18328         this.fireEvent('select', this, this.date);
18329         
18330         
18331     },
18332     
18333     onMousedown: function(e){
18334         e.stopPropagation();
18335         e.preventDefault();
18336     },
18337     
18338     onIncrementHours: function()
18339     {
18340         Roo.log('onIncrementHours');
18341         this.time = this.time.add(Date.HOUR, 1);
18342         this.update();
18343         
18344     },
18345     
18346     onDecrementHours: function()
18347     {
18348         Roo.log('onDecrementHours');
18349         this.time = this.time.add(Date.HOUR, -1);
18350         this.update();
18351     },
18352     
18353     onIncrementMinutes: function()
18354     {
18355         Roo.log('onIncrementMinutes');
18356         this.time = this.time.add(Date.MINUTE, 1);
18357         this.update();
18358     },
18359     
18360     onDecrementMinutes: function()
18361     {
18362         Roo.log('onDecrementMinutes');
18363         this.time = this.time.add(Date.MINUTE, -1);
18364         this.update();
18365     },
18366     
18367     onTogglePeriod: function()
18368     {
18369         Roo.log('onTogglePeriod');
18370         this.time = this.time.add(Date.HOUR, 12);
18371         this.update();
18372     }
18373     
18374    
18375 });
18376
18377 Roo.apply(Roo.bootstrap.TimeField,  {
18378     
18379     content : {
18380         tag: 'tbody',
18381         cn: [
18382             {
18383                 tag: 'tr',
18384                 cn: [
18385                 {
18386                     tag: 'td',
18387                     colspan: '7'
18388                 }
18389                 ]
18390             }
18391         ]
18392     },
18393     
18394     footer : {
18395         tag: 'tfoot',
18396         cn: [
18397             {
18398                 tag: 'tr',
18399                 cn: [
18400                 {
18401                     tag: 'th',
18402                     colspan: '7',
18403                     cls: '',
18404                     cn: [
18405                         {
18406                             tag: 'button',
18407                             cls: 'btn btn-info ok',
18408                             html: 'OK'
18409                         }
18410                     ]
18411                 }
18412
18413                 ]
18414             }
18415         ]
18416     }
18417 });
18418
18419 Roo.apply(Roo.bootstrap.TimeField,  {
18420   
18421     template : {
18422         tag: 'div',
18423         cls: 'datepicker dropdown-menu',
18424         cn: [
18425             {
18426                 tag: 'div',
18427                 cls: 'datepicker-time',
18428                 cn: [
18429                 {
18430                     tag: 'table',
18431                     cls: 'table-condensed',
18432                     cn:[
18433                     Roo.bootstrap.TimeField.content,
18434                     Roo.bootstrap.TimeField.footer
18435                     ]
18436                 }
18437                 ]
18438             }
18439         ]
18440     }
18441 });
18442
18443  
18444
18445  /*
18446  * - LGPL
18447  *
18448  * MonthField
18449  * 
18450  */
18451
18452 /**
18453  * @class Roo.bootstrap.MonthField
18454  * @extends Roo.bootstrap.Input
18455  * Bootstrap MonthField class
18456  * 
18457  * @cfg {String} language default en
18458  * 
18459  * @constructor
18460  * Create a new MonthField
18461  * @param {Object} config The config object
18462  */
18463
18464 Roo.bootstrap.MonthField = function(config){
18465     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18466     
18467     this.addEvents({
18468         /**
18469          * @event show
18470          * Fires when this field show.
18471          * @param {Roo.bootstrap.MonthField} this
18472          * @param {Mixed} date The date value
18473          */
18474         show : true,
18475         /**
18476          * @event show
18477          * Fires when this field hide.
18478          * @param {Roo.bootstrap.MonthField} this
18479          * @param {Mixed} date The date value
18480          */
18481         hide : true,
18482         /**
18483          * @event select
18484          * Fires when select a date.
18485          * @param {Roo.bootstrap.MonthField} this
18486          * @param {String} oldvalue The old value
18487          * @param {String} newvalue The new value
18488          */
18489         select : true
18490     });
18491 };
18492
18493 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18494     
18495     onRender: function(ct, position)
18496     {
18497         
18498         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18499         
18500         this.language = this.language || 'en';
18501         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18502         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18503         
18504         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18505         this.isInline = false;
18506         this.isInput = true;
18507         this.component = this.el.select('.add-on', true).first() || false;
18508         this.component = (this.component && this.component.length === 0) ? false : this.component;
18509         this.hasInput = this.component && this.inputEL().length;
18510         
18511         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18512         
18513         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18514         
18515         this.picker().on('mousedown', this.onMousedown, this);
18516         this.picker().on('click', this.onClick, this);
18517         
18518         this.picker().addClass('datepicker-dropdown');
18519         
18520         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18521             v.setStyle('width', '189px');
18522         });
18523         
18524         this.fillMonths();
18525         
18526         this.update();
18527         
18528         if(this.isInline) {
18529             this.show();
18530         }
18531         
18532     },
18533     
18534     setValue: function(v, suppressEvent)
18535     {   
18536         var o = this.getValue();
18537         
18538         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18539         
18540         this.update();
18541
18542         if(suppressEvent !== true){
18543             this.fireEvent('select', this, o, v);
18544         }
18545         
18546     },
18547     
18548     getValue: function()
18549     {
18550         return this.value;
18551     },
18552     
18553     onClick: function(e) 
18554     {
18555         e.stopPropagation();
18556         e.preventDefault();
18557         
18558         var target = e.getTarget();
18559         
18560         if(target.nodeName.toLowerCase() === 'i'){
18561             target = Roo.get(target).dom.parentNode;
18562         }
18563         
18564         var nodeName = target.nodeName;
18565         var className = target.className;
18566         var html = target.innerHTML;
18567         
18568         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18569             return;
18570         }
18571         
18572         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18573         
18574         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18575         
18576         this.hide();
18577                         
18578     },
18579     
18580     picker : function()
18581     {
18582         return this.pickerEl;
18583     },
18584     
18585     fillMonths: function()
18586     {    
18587         var i = 0;
18588         var months = this.picker().select('>.datepicker-months td', true).first();
18589         
18590         months.dom.innerHTML = '';
18591         
18592         while (i < 12) {
18593             var month = {
18594                 tag: 'span',
18595                 cls: 'month',
18596                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18597             };
18598             
18599             months.createChild(month);
18600         }
18601         
18602     },
18603     
18604     update: function()
18605     {
18606         var _this = this;
18607         
18608         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18609             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18610         }
18611         
18612         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18613             e.removeClass('active');
18614             
18615             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18616                 e.addClass('active');
18617             }
18618         })
18619     },
18620     
18621     place: function()
18622     {
18623         if(this.isInline) {
18624             return;
18625         }
18626         
18627         this.picker().removeClass(['bottom', 'top']);
18628         
18629         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18630             /*
18631              * place to the top of element!
18632              *
18633              */
18634             
18635             this.picker().addClass('top');
18636             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18637             
18638             return;
18639         }
18640         
18641         this.picker().addClass('bottom');
18642         
18643         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18644     },
18645     
18646     onFocus : function()
18647     {
18648         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18649         this.show();
18650     },
18651     
18652     onBlur : function()
18653     {
18654         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18655         
18656         var d = this.inputEl().getValue();
18657         
18658         this.setValue(d);
18659                 
18660         this.hide();
18661     },
18662     
18663     show : function()
18664     {
18665         this.picker().show();
18666         this.picker().select('>.datepicker-months', true).first().show();
18667         this.update();
18668         this.place();
18669         
18670         this.fireEvent('show', this, this.date);
18671     },
18672     
18673     hide : function()
18674     {
18675         if(this.isInline) {
18676             return;
18677         }
18678         this.picker().hide();
18679         this.fireEvent('hide', this, this.date);
18680         
18681     },
18682     
18683     onMousedown: function(e)
18684     {
18685         e.stopPropagation();
18686         e.preventDefault();
18687     },
18688     
18689     keyup: function(e)
18690     {
18691         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18692         this.update();
18693     },
18694
18695     fireKey: function(e)
18696     {
18697         if (!this.picker().isVisible()){
18698             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18699                 this.show();
18700             }
18701             return;
18702         }
18703         
18704         var dir;
18705         
18706         switch(e.keyCode){
18707             case 27: // escape
18708                 this.hide();
18709                 e.preventDefault();
18710                 break;
18711             case 37: // left
18712             case 39: // right
18713                 dir = e.keyCode == 37 ? -1 : 1;
18714                 
18715                 this.vIndex = this.vIndex + dir;
18716                 
18717                 if(this.vIndex < 0){
18718                     this.vIndex = 0;
18719                 }
18720                 
18721                 if(this.vIndex > 11){
18722                     this.vIndex = 11;
18723                 }
18724                 
18725                 if(isNaN(this.vIndex)){
18726                     this.vIndex = 0;
18727                 }
18728                 
18729                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18730                 
18731                 break;
18732             case 38: // up
18733             case 40: // down
18734                 
18735                 dir = e.keyCode == 38 ? -1 : 1;
18736                 
18737                 this.vIndex = this.vIndex + dir * 4;
18738                 
18739                 if(this.vIndex < 0){
18740                     this.vIndex = 0;
18741                 }
18742                 
18743                 if(this.vIndex > 11){
18744                     this.vIndex = 11;
18745                 }
18746                 
18747                 if(isNaN(this.vIndex)){
18748                     this.vIndex = 0;
18749                 }
18750                 
18751                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18752                 break;
18753                 
18754             case 13: // enter
18755                 
18756                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18757                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18758                 }
18759                 
18760                 this.hide();
18761                 e.preventDefault();
18762                 break;
18763             case 9: // tab
18764                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18765                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18766                 }
18767                 this.hide();
18768                 break;
18769             case 16: // shift
18770             case 17: // ctrl
18771             case 18: // alt
18772                 break;
18773             default :
18774                 this.hide();
18775                 
18776         }
18777     },
18778     
18779     remove: function() 
18780     {
18781         this.picker().remove();
18782     }
18783    
18784 });
18785
18786 Roo.apply(Roo.bootstrap.MonthField,  {
18787     
18788     content : {
18789         tag: 'tbody',
18790         cn: [
18791         {
18792             tag: 'tr',
18793             cn: [
18794             {
18795                 tag: 'td',
18796                 colspan: '7'
18797             }
18798             ]
18799         }
18800         ]
18801     },
18802     
18803     dates:{
18804         en: {
18805             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18806             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18807         }
18808     }
18809 });
18810
18811 Roo.apply(Roo.bootstrap.MonthField,  {
18812   
18813     template : {
18814         tag: 'div',
18815         cls: 'datepicker dropdown-menu roo-dynamic',
18816         cn: [
18817             {
18818                 tag: 'div',
18819                 cls: 'datepicker-months',
18820                 cn: [
18821                 {
18822                     tag: 'table',
18823                     cls: 'table-condensed',
18824                     cn:[
18825                         Roo.bootstrap.DateField.content
18826                     ]
18827                 }
18828                 ]
18829             }
18830         ]
18831     }
18832 });
18833
18834  
18835
18836  
18837  /*
18838  * - LGPL
18839  *
18840  * CheckBox
18841  * 
18842  */
18843
18844 /**
18845  * @class Roo.bootstrap.CheckBox
18846  * @extends Roo.bootstrap.Input
18847  * Bootstrap CheckBox class
18848  * 
18849  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18850  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18851  * @cfg {String} boxLabel The text that appears beside the checkbox
18852  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18853  * @cfg {Boolean} checked initnal the element
18854  * @cfg {Boolean} inline inline the element (default false)
18855  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18856  * 
18857  * @constructor
18858  * Create a new CheckBox
18859  * @param {Object} config The config object
18860  */
18861
18862 Roo.bootstrap.CheckBox = function(config){
18863     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18864    
18865     this.addEvents({
18866         /**
18867         * @event check
18868         * Fires when the element is checked or unchecked.
18869         * @param {Roo.bootstrap.CheckBox} this This input
18870         * @param {Boolean} checked The new checked value
18871         */
18872        check : true
18873     });
18874     
18875 };
18876
18877 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18878   
18879     inputType: 'checkbox',
18880     inputValue: 1,
18881     valueOff: 0,
18882     boxLabel: false,
18883     checked: false,
18884     weight : false,
18885     inline: false,
18886     
18887     getAutoCreate : function()
18888     {
18889         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18890         
18891         var id = Roo.id();
18892         
18893         var cfg = {};
18894         
18895         cfg.cls = 'form-group ' + this.inputType; //input-group
18896         
18897         if(this.inline){
18898             cfg.cls += ' ' + this.inputType + '-inline';
18899         }
18900         
18901         var input =  {
18902             tag: 'input',
18903             id : id,
18904             type : this.inputType,
18905             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18906             cls : 'roo-' + this.inputType, //'form-box',
18907             placeholder : this.placeholder || ''
18908             
18909         };
18910         
18911         if (this.weight) { // Validity check?
18912             cfg.cls += " " + this.inputType + "-" + this.weight;
18913         }
18914         
18915         if (this.disabled) {
18916             input.disabled=true;
18917         }
18918         
18919         if(this.checked){
18920             input.checked = this.checked;
18921         }
18922         
18923         if (this.name) {
18924             input.name = this.name;
18925         }
18926         
18927         if (this.size) {
18928             input.cls += ' input-' + this.size;
18929         }
18930         
18931         var settings=this;
18932         
18933         ['xs','sm','md','lg'].map(function(size){
18934             if (settings[size]) {
18935                 cfg.cls += ' col-' + size + '-' + settings[size];
18936             }
18937         });
18938         
18939         var inputblock = input;
18940          
18941         if (this.before || this.after) {
18942             
18943             inputblock = {
18944                 cls : 'input-group',
18945                 cn :  [] 
18946             };
18947             
18948             if (this.before) {
18949                 inputblock.cn.push({
18950                     tag :'span',
18951                     cls : 'input-group-addon',
18952                     html : this.before
18953                 });
18954             }
18955             
18956             inputblock.cn.push(input);
18957             
18958             if (this.after) {
18959                 inputblock.cn.push({
18960                     tag :'span',
18961                     cls : 'input-group-addon',
18962                     html : this.after
18963                 });
18964             }
18965             
18966         }
18967         
18968         if (align ==='left' && this.fieldLabel.length) {
18969 //                Roo.log("left and has label");
18970                 cfg.cn = [
18971                     
18972                     {
18973                         tag: 'label',
18974                         'for' :  id,
18975                         cls : 'control-label col-md-' + this.labelWidth,
18976                         html : this.fieldLabel
18977                         
18978                     },
18979                     {
18980                         cls : "col-md-" + (12 - this.labelWidth), 
18981                         cn: [
18982                             inputblock
18983                         ]
18984                     }
18985                     
18986                 ];
18987         } else if ( this.fieldLabel.length) {
18988 //                Roo.log(" label");
18989                 cfg.cn = [
18990                    
18991                     {
18992                         tag: this.boxLabel ? 'span' : 'label',
18993                         'for': id,
18994                         cls: 'control-label box-input-label',
18995                         //cls : 'input-group-addon',
18996                         html : this.fieldLabel
18997                         
18998                     },
18999                     
19000                     inputblock
19001                     
19002                 ];
19003
19004         } else {
19005             
19006 //                Roo.log(" no label && no align");
19007                 cfg.cn = [  inputblock ] ;
19008                 
19009                 
19010         }
19011         
19012         if(this.boxLabel){
19013              var boxLabelCfg = {
19014                 tag: 'label',
19015                 //'for': id, // box label is handled by onclick - so no for...
19016                 cls: 'box-label',
19017                 html: this.boxLabel
19018             };
19019             
19020             if(this.tooltip){
19021                 boxLabelCfg.tooltip = this.tooltip;
19022             }
19023              
19024             cfg.cn.push(boxLabelCfg);
19025         }
19026         
19027         
19028        
19029         return cfg;
19030         
19031     },
19032     
19033     /**
19034      * return the real input element.
19035      */
19036     inputEl: function ()
19037     {
19038         return this.el.select('input.roo-' + this.inputType,true).first();
19039     },
19040     
19041     labelEl: function()
19042     {
19043         return this.el.select('label.control-label',true).first();
19044     },
19045     /* depricated... */
19046     
19047     label: function()
19048     {
19049         return this.labelEl();
19050     },
19051     
19052     boxLabelEl: function()
19053     {
19054         return this.el.select('label.box-label',true).first();
19055     },
19056     
19057     initEvents : function()
19058     {
19059 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19060         
19061         this.inputEl().on('click', this.onClick,  this);
19062         
19063         if (this.boxLabel) { 
19064             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19065         }
19066         
19067         this.startValue = this.getValue();
19068         
19069         if(this.groupId){
19070             Roo.bootstrap.CheckBox.register(this);
19071         }
19072     },
19073     
19074     onClick : function()
19075     {   
19076         this.setChecked(!this.checked);
19077     },
19078     
19079     setChecked : function(state,suppressEvent)
19080     {
19081         this.startValue = this.getValue();
19082         
19083         if(this.inputType == 'radio'){
19084             
19085             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19086                 e.dom.checked = false;
19087             });
19088             
19089             this.inputEl().dom.checked = true;
19090             
19091             this.inputEl().dom.value = this.inputValue;
19092             
19093             if(suppressEvent !== true){
19094                 this.fireEvent('check', this, true);
19095             }
19096             
19097             this.validate();
19098             
19099             return;
19100         }
19101         
19102         this.checked = state;
19103         
19104         this.inputEl().dom.checked = state;
19105         
19106         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19107         
19108         if(suppressEvent !== true){
19109             this.fireEvent('check', this, state);
19110         }
19111         
19112         this.validate();
19113     },
19114     
19115     getValue : function()
19116     {
19117         if(this.inputType == 'radio'){
19118             return this.getGroupValue();
19119         }
19120         
19121         return this.inputEl().getValue();
19122         
19123     },
19124     
19125     getGroupValue : function()
19126     {
19127         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19128             return '';
19129         }
19130         
19131         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19132     },
19133     
19134     setValue : function(v,suppressEvent)
19135     {
19136         if(this.inputType == 'radio'){
19137             this.setGroupValue(v, suppressEvent);
19138             return;
19139         }
19140         
19141         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19142         
19143         this.validate();
19144     },
19145     
19146     setGroupValue : function(v, suppressEvent)
19147     {
19148         this.startValue = this.getValue();
19149         
19150         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19151             e.dom.checked = false;
19152             
19153             if(e.dom.value == v){
19154                 e.dom.checked = true;
19155             }
19156         });
19157         
19158         if(suppressEvent !== true){
19159             this.fireEvent('check', this, true);
19160         }
19161
19162         this.validate();
19163         
19164         return;
19165     },
19166     
19167     validate : function()
19168     {
19169         if(
19170                 this.disabled || 
19171                 (this.inputType == 'radio' && this.validateRadio()) ||
19172                 (this.inputType == 'checkbox' && this.validateCheckbox())
19173         ){
19174             this.markValid();
19175             return true;
19176         }
19177         
19178         this.markInvalid();
19179         return false;
19180     },
19181     
19182     validateRadio : function()
19183     {
19184         var valid = false;
19185         
19186         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19187             if(!e.dom.checked){
19188                 return;
19189             }
19190             
19191             valid = true;
19192             
19193             return false;
19194         });
19195         
19196         return valid;
19197     },
19198     
19199     validateCheckbox : function()
19200     {
19201         if(!this.groupId){
19202             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19203         }
19204         
19205         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19206         
19207         if(!group){
19208             return false;
19209         }
19210         
19211         var r = false;
19212         
19213         for(var i in group){
19214             if(r){
19215                 break;
19216             }
19217             
19218             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19219         }
19220         
19221         return r;
19222     },
19223     
19224     /**
19225      * Mark this field as valid
19226      */
19227     markValid : function()
19228     {
19229         if(this.allowBlank){
19230             return;
19231         }
19232         
19233         var _this = this;
19234         
19235         this.fireEvent('valid', this);
19236         
19237         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19238         
19239         if(this.groupId){
19240             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19241         }
19242         
19243         if(label){
19244             label.markValid();
19245         }
19246         
19247         if(this.inputType == 'radio'){
19248             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19249                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19250                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19251             });
19252             
19253             return;
19254         }
19255         
19256         if(!this.groupId){
19257             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19258             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19259             return;
19260         }
19261         
19262         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19263             
19264         if(!group){
19265             return;
19266         }
19267         
19268         for(var i in group){
19269             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19270             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19271         }
19272     },
19273     
19274      /**
19275      * Mark this field as invalid
19276      * @param {String} msg The validation message
19277      */
19278     markInvalid : function(msg)
19279     {
19280         if(this.allowBlank){
19281             return;
19282         }
19283         
19284         var _this = this;
19285         
19286         this.fireEvent('invalid', this, msg);
19287         
19288         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19289         
19290         if(this.groupId){
19291             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19292         }
19293         
19294         if(label){
19295             label.markInvalid();
19296         }
19297             
19298         if(this.inputType == 'radio'){
19299             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19300                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19301                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19302             });
19303             
19304             return;
19305         }
19306         
19307         if(!this.groupId){
19308             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19309             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19310             return;
19311         }
19312         
19313         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19314         
19315         if(!group){
19316             return;
19317         }
19318         
19319         for(var i in group){
19320             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19321             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19322         }
19323         
19324     }
19325     
19326 });
19327
19328 Roo.apply(Roo.bootstrap.CheckBox, {
19329     
19330     groups: {},
19331     
19332      /**
19333     * register a CheckBox Group
19334     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19335     */
19336     register : function(checkbox)
19337     {
19338         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19339             this.groups[checkbox.groupId] = {};
19340         }
19341         
19342         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19343             return;
19344         }
19345         
19346         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19347         
19348     },
19349     /**
19350     * fetch a CheckBox Group based on the group ID
19351     * @param {string} the group ID
19352     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19353     */
19354     get: function(groupId) {
19355         if (typeof(this.groups[groupId]) == 'undefined') {
19356             return false;
19357         }
19358         
19359         return this.groups[groupId] ;
19360     }
19361     
19362     
19363 });
19364 /*
19365  * - LGPL
19366  *
19367  * Radio
19368  *
19369  *
19370  * not inline
19371  *<div class="radio">
19372   <label>
19373     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19374     Option one is this and that&mdash;be sure to include why it's great
19375   </label>
19376 </div>
19377  *
19378  *
19379  *inline
19380  *<span>
19381  *<label class="radio-inline">fieldLabel</label>
19382  *<label class="radio-inline">
19383   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19384 </label>
19385 <span>
19386  * 
19387  * 
19388  */
19389
19390 /**
19391  * @class Roo.bootstrap.Radio
19392  * @extends Roo.bootstrap.CheckBox
19393  * Bootstrap Radio class
19394
19395  * @constructor
19396  * Create a new Radio
19397  * @param {Object} config The config object
19398  */
19399
19400 Roo.bootstrap.Radio = function(config){
19401     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19402    
19403 };
19404
19405 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19406     
19407     inputType: 'radio',
19408     inputValue: '',
19409     valueOff: '',
19410     
19411     getAutoCreate : function()
19412     {
19413         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19414         align = align || 'left'; // default...
19415         
19416         
19417         
19418         var id = Roo.id();
19419         
19420         var cfg = {
19421                 tag : this.inline ? 'span' : 'div',
19422                 cls : '',
19423                 cn : []
19424         };
19425         
19426         var inline = this.inline ? ' radio-inline' : '';
19427         
19428         var lbl = {
19429                 tag: 'label' ,
19430                 // does not need for, as we wrap the input with it..
19431                 'for' : id,
19432                 cls : 'control-label box-label' + inline,
19433                 cn : []
19434         };
19435         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19436         
19437         var fieldLabel = {
19438             tag: 'label' ,
19439             //cls : 'control-label' + inline,
19440             html : this.fieldLabel,
19441             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19442         };
19443         
19444  
19445         
19446         
19447         var input =  {
19448             tag: 'input',
19449             id : id,
19450             type : this.inputType,
19451             //value : (!this.checked) ? this.valueOff : this.inputValue,
19452             value : this.inputValue,
19453             cls : 'roo-radio',
19454             placeholder : this.placeholder || '' // ?? needed????
19455             
19456         };
19457         if (this.weight) { // Validity check?
19458             input.cls += " radio-" + this.weight;
19459         }
19460         if (this.disabled) {
19461             input.disabled=true;
19462         }
19463         
19464         if(this.checked){
19465             input.checked = this.checked;
19466         }
19467         
19468         if (this.name) {
19469             input.name = this.name;
19470         }
19471         
19472         if (this.size) {
19473             input.cls += ' input-' + this.size;
19474         }
19475         
19476         //?? can span's inline have a width??
19477         
19478         var settings=this;
19479         ['xs','sm','md','lg'].map(function(size){
19480             if (settings[size]) {
19481                 cfg.cls += ' col-' + size + '-' + settings[size];
19482             }
19483         });
19484         
19485         var inputblock = input;
19486         
19487         if (this.before || this.after) {
19488             
19489             inputblock = {
19490                 cls : 'input-group',
19491                 tag : 'span',
19492                 cn :  [] 
19493             };
19494             if (this.before) {
19495                 inputblock.cn.push({
19496                     tag :'span',
19497                     cls : 'input-group-addon',
19498                     html : this.before
19499                 });
19500             }
19501             inputblock.cn.push(input);
19502             if (this.after) {
19503                 inputblock.cn.push({
19504                     tag :'span',
19505                     cls : 'input-group-addon',
19506                     html : this.after
19507                 });
19508             }
19509             
19510         };
19511         
19512         
19513         if (this.fieldLabel && this.fieldLabel.length) {
19514             cfg.cn.push(fieldLabel);
19515         }
19516        
19517         // normal bootstrap puts the input inside the label.
19518         // however with our styled version - it has to go after the input.
19519        
19520         //lbl.cn.push(inputblock);
19521         
19522         var lblwrap =  {
19523             tag: 'span',
19524             cls: 'radio' + inline,
19525             cn: [
19526                 inputblock,
19527                 lbl
19528             ]
19529         };
19530         
19531         cfg.cn.push( lblwrap);
19532         
19533         if(this.boxLabel){
19534             lbl.cn.push({
19535                 tag: 'span',
19536                 html: this.boxLabel
19537             })
19538         }
19539          
19540         
19541         return cfg;
19542         
19543     },
19544     
19545     initEvents : function()
19546     {
19547 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19548         
19549         this.inputEl().on('click', this.onClick,  this);
19550         if (this.boxLabel) {
19551             //Roo.log('find label');
19552             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19553         }
19554         
19555     },
19556     
19557     inputEl: function ()
19558     {
19559         return this.el.select('input.roo-radio',true).first();
19560     },
19561     onClick : function()
19562     {   
19563         Roo.log("click");
19564         this.setChecked(true);
19565     },
19566     
19567     setChecked : function(state,suppressEvent)
19568     {
19569         if(state){
19570             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19571                 v.dom.checked = false;
19572             });
19573         }
19574         Roo.log(this.inputEl().dom);
19575         this.checked = state;
19576         this.inputEl().dom.checked = state;
19577         
19578         if(suppressEvent !== true){
19579             this.fireEvent('check', this, state);
19580         }
19581         
19582         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19583         
19584     },
19585     
19586     getGroupValue : function()
19587     {
19588         var value = '';
19589         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19590             if(v.dom.checked == true){
19591                 value = v.dom.value;
19592             }
19593         });
19594         
19595         return value;
19596     },
19597     
19598     /**
19599      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19600      * @return {Mixed} value The field value
19601      */
19602     getValue : function(){
19603         return this.getGroupValue();
19604     }
19605     
19606 });
19607
19608  
19609 //<script type="text/javascript">
19610
19611 /*
19612  * Based  Ext JS Library 1.1.1
19613  * Copyright(c) 2006-2007, Ext JS, LLC.
19614  * LGPL
19615  *
19616  */
19617  
19618 /**
19619  * @class Roo.HtmlEditorCore
19620  * @extends Roo.Component
19621  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19622  *
19623  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19624  */
19625
19626 Roo.HtmlEditorCore = function(config){
19627     
19628     
19629     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19630     
19631     
19632     this.addEvents({
19633         /**
19634          * @event initialize
19635          * Fires when the editor is fully initialized (including the iframe)
19636          * @param {Roo.HtmlEditorCore} this
19637          */
19638         initialize: true,
19639         /**
19640          * @event activate
19641          * Fires when the editor is first receives the focus. Any insertion must wait
19642          * until after this event.
19643          * @param {Roo.HtmlEditorCore} this
19644          */
19645         activate: true,
19646          /**
19647          * @event beforesync
19648          * Fires before the textarea is updated with content from the editor iframe. Return false
19649          * to cancel the sync.
19650          * @param {Roo.HtmlEditorCore} this
19651          * @param {String} html
19652          */
19653         beforesync: true,
19654          /**
19655          * @event beforepush
19656          * Fires before the iframe editor is updated with content from the textarea. Return false
19657          * to cancel the push.
19658          * @param {Roo.HtmlEditorCore} this
19659          * @param {String} html
19660          */
19661         beforepush: true,
19662          /**
19663          * @event sync
19664          * Fires when the textarea is updated with content from the editor iframe.
19665          * @param {Roo.HtmlEditorCore} this
19666          * @param {String} html
19667          */
19668         sync: true,
19669          /**
19670          * @event push
19671          * Fires when the iframe editor is updated with content from the textarea.
19672          * @param {Roo.HtmlEditorCore} this
19673          * @param {String} html
19674          */
19675         push: true,
19676         
19677         /**
19678          * @event editorevent
19679          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19680          * @param {Roo.HtmlEditorCore} this
19681          */
19682         editorevent: true
19683         
19684     });
19685     
19686     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19687     
19688     // defaults : white / black...
19689     this.applyBlacklists();
19690     
19691     
19692     
19693 };
19694
19695
19696 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19697
19698
19699      /**
19700      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19701      */
19702     
19703     owner : false,
19704     
19705      /**
19706      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19707      *                        Roo.resizable.
19708      */
19709     resizable : false,
19710      /**
19711      * @cfg {Number} height (in pixels)
19712      */   
19713     height: 300,
19714    /**
19715      * @cfg {Number} width (in pixels)
19716      */   
19717     width: 500,
19718     
19719     /**
19720      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19721      * 
19722      */
19723     stylesheets: false,
19724     
19725     // id of frame..
19726     frameId: false,
19727     
19728     // private properties
19729     validationEvent : false,
19730     deferHeight: true,
19731     initialized : false,
19732     activated : false,
19733     sourceEditMode : false,
19734     onFocus : Roo.emptyFn,
19735     iframePad:3,
19736     hideMode:'offsets',
19737     
19738     clearUp: true,
19739     
19740     // blacklist + whitelisted elements..
19741     black: false,
19742     white: false,
19743      
19744     
19745
19746     /**
19747      * Protected method that will not generally be called directly. It
19748      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19749      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19750      */
19751     getDocMarkup : function(){
19752         // body styles..
19753         var st = '';
19754         
19755         // inherit styels from page...?? 
19756         if (this.stylesheets === false) {
19757             
19758             Roo.get(document.head).select('style').each(function(node) {
19759                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19760             });
19761             
19762             Roo.get(document.head).select('link').each(function(node) { 
19763                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19764             });
19765             
19766         } else if (!this.stylesheets.length) {
19767                 // simple..
19768                 st = '<style type="text/css">' +
19769                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19770                    '</style>';
19771         } else { 
19772             
19773         }
19774         
19775         st +=  '<style type="text/css">' +
19776             'IMG { cursor: pointer } ' +
19777         '</style>';
19778
19779         
19780         return '<html><head>' + st  +
19781             //<style type="text/css">' +
19782             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19783             //'</style>' +
19784             ' </head><body class="roo-htmleditor-body"></body></html>';
19785     },
19786
19787     // private
19788     onRender : function(ct, position)
19789     {
19790         var _t = this;
19791         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19792         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19793         
19794         
19795         this.el.dom.style.border = '0 none';
19796         this.el.dom.setAttribute('tabIndex', -1);
19797         this.el.addClass('x-hidden hide');
19798         
19799         
19800         
19801         if(Roo.isIE){ // fix IE 1px bogus margin
19802             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19803         }
19804        
19805         
19806         this.frameId = Roo.id();
19807         
19808          
19809         
19810         var iframe = this.owner.wrap.createChild({
19811             tag: 'iframe',
19812             cls: 'form-control', // bootstrap..
19813             id: this.frameId,
19814             name: this.frameId,
19815             frameBorder : 'no',
19816             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19817         }, this.el
19818         );
19819         
19820         
19821         this.iframe = iframe.dom;
19822
19823          this.assignDocWin();
19824         
19825         this.doc.designMode = 'on';
19826        
19827         this.doc.open();
19828         this.doc.write(this.getDocMarkup());
19829         this.doc.close();
19830
19831         
19832         var task = { // must defer to wait for browser to be ready
19833             run : function(){
19834                 //console.log("run task?" + this.doc.readyState);
19835                 this.assignDocWin();
19836                 if(this.doc.body || this.doc.readyState == 'complete'){
19837                     try {
19838                         this.doc.designMode="on";
19839                     } catch (e) {
19840                         return;
19841                     }
19842                     Roo.TaskMgr.stop(task);
19843                     this.initEditor.defer(10, this);
19844                 }
19845             },
19846             interval : 10,
19847             duration: 10000,
19848             scope: this
19849         };
19850         Roo.TaskMgr.start(task);
19851
19852     },
19853
19854     // private
19855     onResize : function(w, h)
19856     {
19857          Roo.log('resize: ' +w + ',' + h );
19858         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19859         if(!this.iframe){
19860             return;
19861         }
19862         if(typeof w == 'number'){
19863             
19864             this.iframe.style.width = w + 'px';
19865         }
19866         if(typeof h == 'number'){
19867             
19868             this.iframe.style.height = h + 'px';
19869             if(this.doc){
19870                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19871             }
19872         }
19873         
19874     },
19875
19876     /**
19877      * Toggles the editor between standard and source edit mode.
19878      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19879      */
19880     toggleSourceEdit : function(sourceEditMode){
19881         
19882         this.sourceEditMode = sourceEditMode === true;
19883         
19884         if(this.sourceEditMode){
19885  
19886             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19887             
19888         }else{
19889             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19890             //this.iframe.className = '';
19891             this.deferFocus();
19892         }
19893         //this.setSize(this.owner.wrap.getSize());
19894         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19895     },
19896
19897     
19898   
19899
19900     /**
19901      * Protected method that will not generally be called directly. If you need/want
19902      * custom HTML cleanup, this is the method you should override.
19903      * @param {String} html The HTML to be cleaned
19904      * return {String} The cleaned HTML
19905      */
19906     cleanHtml : function(html){
19907         html = String(html);
19908         if(html.length > 5){
19909             if(Roo.isSafari){ // strip safari nonsense
19910                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19911             }
19912         }
19913         if(html == '&nbsp;'){
19914             html = '';
19915         }
19916         return html;
19917     },
19918
19919     /**
19920      * HTML Editor -> Textarea
19921      * Protected method that will not generally be called directly. Syncs the contents
19922      * of the editor iframe with the textarea.
19923      */
19924     syncValue : function(){
19925         if(this.initialized){
19926             var bd = (this.doc.body || this.doc.documentElement);
19927             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19928             var html = bd.innerHTML;
19929             if(Roo.isSafari){
19930                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19931                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19932                 if(m && m[1]){
19933                     html = '<div style="'+m[0]+'">' + html + '</div>';
19934                 }
19935             }
19936             html = this.cleanHtml(html);
19937             // fix up the special chars.. normaly like back quotes in word...
19938             // however we do not want to do this with chinese..
19939             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19940                 var cc = b.charCodeAt();
19941                 if (
19942                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19943                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19944                     (cc >= 0xf900 && cc < 0xfb00 )
19945                 ) {
19946                         return b;
19947                 }
19948                 return "&#"+cc+";" 
19949             });
19950             if(this.owner.fireEvent('beforesync', this, html) !== false){
19951                 this.el.dom.value = html;
19952                 this.owner.fireEvent('sync', this, html);
19953             }
19954         }
19955     },
19956
19957     /**
19958      * Protected method that will not generally be called directly. Pushes the value of the textarea
19959      * into the iframe editor.
19960      */
19961     pushValue : function(){
19962         if(this.initialized){
19963             var v = this.el.dom.value.trim();
19964             
19965 //            if(v.length < 1){
19966 //                v = '&#160;';
19967 //            }
19968             
19969             if(this.owner.fireEvent('beforepush', this, v) !== false){
19970                 var d = (this.doc.body || this.doc.documentElement);
19971                 d.innerHTML = v;
19972                 this.cleanUpPaste();
19973                 this.el.dom.value = d.innerHTML;
19974                 this.owner.fireEvent('push', this, v);
19975             }
19976         }
19977     },
19978
19979     // private
19980     deferFocus : function(){
19981         this.focus.defer(10, this);
19982     },
19983
19984     // doc'ed in Field
19985     focus : function(){
19986         if(this.win && !this.sourceEditMode){
19987             this.win.focus();
19988         }else{
19989             this.el.focus();
19990         }
19991     },
19992     
19993     assignDocWin: function()
19994     {
19995         var iframe = this.iframe;
19996         
19997          if(Roo.isIE){
19998             this.doc = iframe.contentWindow.document;
19999             this.win = iframe.contentWindow;
20000         } else {
20001 //            if (!Roo.get(this.frameId)) {
20002 //                return;
20003 //            }
20004 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20005 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20006             
20007             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20008                 return;
20009             }
20010             
20011             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20012             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20013         }
20014     },
20015     
20016     // private
20017     initEditor : function(){
20018         //console.log("INIT EDITOR");
20019         this.assignDocWin();
20020         
20021         
20022         
20023         this.doc.designMode="on";
20024         this.doc.open();
20025         this.doc.write(this.getDocMarkup());
20026         this.doc.close();
20027         
20028         var dbody = (this.doc.body || this.doc.documentElement);
20029         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20030         // this copies styles from the containing element into thsi one..
20031         // not sure why we need all of this..
20032         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20033         
20034         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20035         //ss['background-attachment'] = 'fixed'; // w3c
20036         dbody.bgProperties = 'fixed'; // ie
20037         //Roo.DomHelper.applyStyles(dbody, ss);
20038         Roo.EventManager.on(this.doc, {
20039             //'mousedown': this.onEditorEvent,
20040             'mouseup': this.onEditorEvent,
20041             'dblclick': this.onEditorEvent,
20042             'click': this.onEditorEvent,
20043             'keyup': this.onEditorEvent,
20044             buffer:100,
20045             scope: this
20046         });
20047         if(Roo.isGecko){
20048             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20049         }
20050         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20051             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20052         }
20053         this.initialized = true;
20054
20055         this.owner.fireEvent('initialize', this);
20056         this.pushValue();
20057     },
20058
20059     // private
20060     onDestroy : function(){
20061         
20062         
20063         
20064         if(this.rendered){
20065             
20066             //for (var i =0; i < this.toolbars.length;i++) {
20067             //    // fixme - ask toolbars for heights?
20068             //    this.toolbars[i].onDestroy();
20069            // }
20070             
20071             //this.wrap.dom.innerHTML = '';
20072             //this.wrap.remove();
20073         }
20074     },
20075
20076     // private
20077     onFirstFocus : function(){
20078         
20079         this.assignDocWin();
20080         
20081         
20082         this.activated = true;
20083          
20084     
20085         if(Roo.isGecko){ // prevent silly gecko errors
20086             this.win.focus();
20087             var s = this.win.getSelection();
20088             if(!s.focusNode || s.focusNode.nodeType != 3){
20089                 var r = s.getRangeAt(0);
20090                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20091                 r.collapse(true);
20092                 this.deferFocus();
20093             }
20094             try{
20095                 this.execCmd('useCSS', true);
20096                 this.execCmd('styleWithCSS', false);
20097             }catch(e){}
20098         }
20099         this.owner.fireEvent('activate', this);
20100     },
20101
20102     // private
20103     adjustFont: function(btn){
20104         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20105         //if(Roo.isSafari){ // safari
20106         //    adjust *= 2;
20107        // }
20108         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20109         if(Roo.isSafari){ // safari
20110             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20111             v =  (v < 10) ? 10 : v;
20112             v =  (v > 48) ? 48 : v;
20113             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20114             
20115         }
20116         
20117         
20118         v = Math.max(1, v+adjust);
20119         
20120         this.execCmd('FontSize', v  );
20121     },
20122
20123     onEditorEvent : function(e)
20124     {
20125         this.owner.fireEvent('editorevent', this, e);
20126       //  this.updateToolbar();
20127         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20128     },
20129
20130     insertTag : function(tg)
20131     {
20132         // could be a bit smarter... -> wrap the current selected tRoo..
20133         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20134             
20135             range = this.createRange(this.getSelection());
20136             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20137             wrappingNode.appendChild(range.extractContents());
20138             range.insertNode(wrappingNode);
20139
20140             return;
20141             
20142             
20143             
20144         }
20145         this.execCmd("formatblock",   tg);
20146         
20147     },
20148     
20149     insertText : function(txt)
20150     {
20151         
20152         
20153         var range = this.createRange();
20154         range.deleteContents();
20155                //alert(Sender.getAttribute('label'));
20156                
20157         range.insertNode(this.doc.createTextNode(txt));
20158     } ,
20159     
20160      
20161
20162     /**
20163      * Executes a Midas editor command on the editor document and performs necessary focus and
20164      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20165      * @param {String} cmd The Midas command
20166      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20167      */
20168     relayCmd : function(cmd, value){
20169         this.win.focus();
20170         this.execCmd(cmd, value);
20171         this.owner.fireEvent('editorevent', this);
20172         //this.updateToolbar();
20173         this.owner.deferFocus();
20174     },
20175
20176     /**
20177      * Executes a Midas editor command directly on the editor document.
20178      * For visual commands, you should use {@link #relayCmd} instead.
20179      * <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     execCmd : function(cmd, value){
20184         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20185         this.syncValue();
20186     },
20187  
20188  
20189    
20190     /**
20191      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20192      * to insert tRoo.
20193      * @param {String} text | dom node.. 
20194      */
20195     insertAtCursor : function(text)
20196     {
20197         
20198         
20199         
20200         if(!this.activated){
20201             return;
20202         }
20203         /*
20204         if(Roo.isIE){
20205             this.win.focus();
20206             var r = this.doc.selection.createRange();
20207             if(r){
20208                 r.collapse(true);
20209                 r.pasteHTML(text);
20210                 this.syncValue();
20211                 this.deferFocus();
20212             
20213             }
20214             return;
20215         }
20216         */
20217         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20218             this.win.focus();
20219             
20220             
20221             // from jquery ui (MIT licenced)
20222             var range, node;
20223             var win = this.win;
20224             
20225             if (win.getSelection && win.getSelection().getRangeAt) {
20226                 range = win.getSelection().getRangeAt(0);
20227                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20228                 range.insertNode(node);
20229             } else if (win.document.selection && win.document.selection.createRange) {
20230                 // no firefox support
20231                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20232                 win.document.selection.createRange().pasteHTML(txt);
20233             } else {
20234                 // no firefox support
20235                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20236                 this.execCmd('InsertHTML', txt);
20237             } 
20238             
20239             this.syncValue();
20240             
20241             this.deferFocus();
20242         }
20243     },
20244  // private
20245     mozKeyPress : function(e){
20246         if(e.ctrlKey){
20247             var c = e.getCharCode(), cmd;
20248           
20249             if(c > 0){
20250                 c = String.fromCharCode(c).toLowerCase();
20251                 switch(c){
20252                     case 'b':
20253                         cmd = 'bold';
20254                         break;
20255                     case 'i':
20256                         cmd = 'italic';
20257                         break;
20258                     
20259                     case 'u':
20260                         cmd = 'underline';
20261                         break;
20262                     
20263                     case 'v':
20264                         this.cleanUpPaste.defer(100, this);
20265                         return;
20266                         
20267                 }
20268                 if(cmd){
20269                     this.win.focus();
20270                     this.execCmd(cmd);
20271                     this.deferFocus();
20272                     e.preventDefault();
20273                 }
20274                 
20275             }
20276         }
20277     },
20278
20279     // private
20280     fixKeys : function(){ // load time branching for fastest keydown performance
20281         if(Roo.isIE){
20282             return function(e){
20283                 var k = e.getKey(), r;
20284                 if(k == e.TAB){
20285                     e.stopEvent();
20286                     r = this.doc.selection.createRange();
20287                     if(r){
20288                         r.collapse(true);
20289                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20290                         this.deferFocus();
20291                     }
20292                     return;
20293                 }
20294                 
20295                 if(k == e.ENTER){
20296                     r = this.doc.selection.createRange();
20297                     if(r){
20298                         var target = r.parentElement();
20299                         if(!target || target.tagName.toLowerCase() != 'li'){
20300                             e.stopEvent();
20301                             r.pasteHTML('<br />');
20302                             r.collapse(false);
20303                             r.select();
20304                         }
20305                     }
20306                 }
20307                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20308                     this.cleanUpPaste.defer(100, this);
20309                     return;
20310                 }
20311                 
20312                 
20313             };
20314         }else if(Roo.isOpera){
20315             return function(e){
20316                 var k = e.getKey();
20317                 if(k == e.TAB){
20318                     e.stopEvent();
20319                     this.win.focus();
20320                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20321                     this.deferFocus();
20322                 }
20323                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20324                     this.cleanUpPaste.defer(100, this);
20325                     return;
20326                 }
20327                 
20328             };
20329         }else if(Roo.isSafari){
20330             return function(e){
20331                 var k = e.getKey();
20332                 
20333                 if(k == e.TAB){
20334                     e.stopEvent();
20335                     this.execCmd('InsertText','\t');
20336                     this.deferFocus();
20337                     return;
20338                 }
20339                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20340                     this.cleanUpPaste.defer(100, this);
20341                     return;
20342                 }
20343                 
20344              };
20345         }
20346     }(),
20347     
20348     getAllAncestors: function()
20349     {
20350         var p = this.getSelectedNode();
20351         var a = [];
20352         if (!p) {
20353             a.push(p); // push blank onto stack..
20354             p = this.getParentElement();
20355         }
20356         
20357         
20358         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20359             a.push(p);
20360             p = p.parentNode;
20361         }
20362         a.push(this.doc.body);
20363         return a;
20364     },
20365     lastSel : false,
20366     lastSelNode : false,
20367     
20368     
20369     getSelection : function() 
20370     {
20371         this.assignDocWin();
20372         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20373     },
20374     
20375     getSelectedNode: function() 
20376     {
20377         // this may only work on Gecko!!!
20378         
20379         // should we cache this!!!!
20380         
20381         
20382         
20383          
20384         var range = this.createRange(this.getSelection()).cloneRange();
20385         
20386         if (Roo.isIE) {
20387             var parent = range.parentElement();
20388             while (true) {
20389                 var testRange = range.duplicate();
20390                 testRange.moveToElementText(parent);
20391                 if (testRange.inRange(range)) {
20392                     break;
20393                 }
20394                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20395                     break;
20396                 }
20397                 parent = parent.parentElement;
20398             }
20399             return parent;
20400         }
20401         
20402         // is ancestor a text element.
20403         var ac =  range.commonAncestorContainer;
20404         if (ac.nodeType == 3) {
20405             ac = ac.parentNode;
20406         }
20407         
20408         var ar = ac.childNodes;
20409          
20410         var nodes = [];
20411         var other_nodes = [];
20412         var has_other_nodes = false;
20413         for (var i=0;i<ar.length;i++) {
20414             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20415                 continue;
20416             }
20417             // fullly contained node.
20418             
20419             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20420                 nodes.push(ar[i]);
20421                 continue;
20422             }
20423             
20424             // probably selected..
20425             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20426                 other_nodes.push(ar[i]);
20427                 continue;
20428             }
20429             // outer..
20430             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20431                 continue;
20432             }
20433             
20434             
20435             has_other_nodes = true;
20436         }
20437         if (!nodes.length && other_nodes.length) {
20438             nodes= other_nodes;
20439         }
20440         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20441             return false;
20442         }
20443         
20444         return nodes[0];
20445     },
20446     createRange: function(sel)
20447     {
20448         // this has strange effects when using with 
20449         // top toolbar - not sure if it's a great idea.
20450         //this.editor.contentWindow.focus();
20451         if (typeof sel != "undefined") {
20452             try {
20453                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20454             } catch(e) {
20455                 return this.doc.createRange();
20456             }
20457         } else {
20458             return this.doc.createRange();
20459         }
20460     },
20461     getParentElement: function()
20462     {
20463         
20464         this.assignDocWin();
20465         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20466         
20467         var range = this.createRange(sel);
20468          
20469         try {
20470             var p = range.commonAncestorContainer;
20471             while (p.nodeType == 3) { // text node
20472                 p = p.parentNode;
20473             }
20474             return p;
20475         } catch (e) {
20476             return null;
20477         }
20478     
20479     },
20480     /***
20481      *
20482      * Range intersection.. the hard stuff...
20483      *  '-1' = before
20484      *  '0' = hits..
20485      *  '1' = after.
20486      *         [ -- selected range --- ]
20487      *   [fail]                        [fail]
20488      *
20489      *    basically..
20490      *      if end is before start or  hits it. fail.
20491      *      if start is after end or hits it fail.
20492      *
20493      *   if either hits (but other is outside. - then it's not 
20494      *   
20495      *    
20496      **/
20497     
20498     
20499     // @see http://www.thismuchiknow.co.uk/?p=64.
20500     rangeIntersectsNode : function(range, node)
20501     {
20502         var nodeRange = node.ownerDocument.createRange();
20503         try {
20504             nodeRange.selectNode(node);
20505         } catch (e) {
20506             nodeRange.selectNodeContents(node);
20507         }
20508     
20509         var rangeStartRange = range.cloneRange();
20510         rangeStartRange.collapse(true);
20511     
20512         var rangeEndRange = range.cloneRange();
20513         rangeEndRange.collapse(false);
20514     
20515         var nodeStartRange = nodeRange.cloneRange();
20516         nodeStartRange.collapse(true);
20517     
20518         var nodeEndRange = nodeRange.cloneRange();
20519         nodeEndRange.collapse(false);
20520     
20521         return rangeStartRange.compareBoundaryPoints(
20522                  Range.START_TO_START, nodeEndRange) == -1 &&
20523                rangeEndRange.compareBoundaryPoints(
20524                  Range.START_TO_START, nodeStartRange) == 1;
20525         
20526          
20527     },
20528     rangeCompareNode : function(range, node)
20529     {
20530         var nodeRange = node.ownerDocument.createRange();
20531         try {
20532             nodeRange.selectNode(node);
20533         } catch (e) {
20534             nodeRange.selectNodeContents(node);
20535         }
20536         
20537         
20538         range.collapse(true);
20539     
20540         nodeRange.collapse(true);
20541      
20542         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20543         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20544          
20545         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20546         
20547         var nodeIsBefore   =  ss == 1;
20548         var nodeIsAfter    = ee == -1;
20549         
20550         if (nodeIsBefore && nodeIsAfter) {
20551             return 0; // outer
20552         }
20553         if (!nodeIsBefore && nodeIsAfter) {
20554             return 1; //right trailed.
20555         }
20556         
20557         if (nodeIsBefore && !nodeIsAfter) {
20558             return 2;  // left trailed.
20559         }
20560         // fully contined.
20561         return 3;
20562     },
20563
20564     // private? - in a new class?
20565     cleanUpPaste :  function()
20566     {
20567         // cleans up the whole document..
20568         Roo.log('cleanuppaste');
20569         
20570         this.cleanUpChildren(this.doc.body);
20571         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20572         if (clean != this.doc.body.innerHTML) {
20573             this.doc.body.innerHTML = clean;
20574         }
20575         
20576     },
20577     
20578     cleanWordChars : function(input) {// change the chars to hex code
20579         var he = Roo.HtmlEditorCore;
20580         
20581         var output = input;
20582         Roo.each(he.swapCodes, function(sw) { 
20583             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20584             
20585             output = output.replace(swapper, sw[1]);
20586         });
20587         
20588         return output;
20589     },
20590     
20591     
20592     cleanUpChildren : function (n)
20593     {
20594         if (!n.childNodes.length) {
20595             return;
20596         }
20597         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20598            this.cleanUpChild(n.childNodes[i]);
20599         }
20600     },
20601     
20602     
20603         
20604     
20605     cleanUpChild : function (node)
20606     {
20607         var ed = this;
20608         //console.log(node);
20609         if (node.nodeName == "#text") {
20610             // clean up silly Windows -- stuff?
20611             return; 
20612         }
20613         if (node.nodeName == "#comment") {
20614             node.parentNode.removeChild(node);
20615             // clean up silly Windows -- stuff?
20616             return; 
20617         }
20618         var lcname = node.tagName.toLowerCase();
20619         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20620         // whitelist of tags..
20621         
20622         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20623             // remove node.
20624             node.parentNode.removeChild(node);
20625             return;
20626             
20627         }
20628         
20629         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20630         
20631         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20632         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20633         
20634         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20635         //    remove_keep_children = true;
20636         //}
20637         
20638         if (remove_keep_children) {
20639             this.cleanUpChildren(node);
20640             // inserts everything just before this node...
20641             while (node.childNodes.length) {
20642                 var cn = node.childNodes[0];
20643                 node.removeChild(cn);
20644                 node.parentNode.insertBefore(cn, node);
20645             }
20646             node.parentNode.removeChild(node);
20647             return;
20648         }
20649         
20650         if (!node.attributes || !node.attributes.length) {
20651             this.cleanUpChildren(node);
20652             return;
20653         }
20654         
20655         function cleanAttr(n,v)
20656         {
20657             
20658             if (v.match(/^\./) || v.match(/^\//)) {
20659                 return;
20660             }
20661             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20662                 return;
20663             }
20664             if (v.match(/^#/)) {
20665                 return;
20666             }
20667 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20668             node.removeAttribute(n);
20669             
20670         }
20671         
20672         var cwhite = this.cwhite;
20673         var cblack = this.cblack;
20674             
20675         function cleanStyle(n,v)
20676         {
20677             if (v.match(/expression/)) { //XSS?? should we even bother..
20678                 node.removeAttribute(n);
20679                 return;
20680             }
20681             
20682             var parts = v.split(/;/);
20683             var clean = [];
20684             
20685             Roo.each(parts, function(p) {
20686                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20687                 if (!p.length) {
20688                     return true;
20689                 }
20690                 var l = p.split(':').shift().replace(/\s+/g,'');
20691                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20692                 
20693                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20694 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20695                     //node.removeAttribute(n);
20696                     return true;
20697                 }
20698                 //Roo.log()
20699                 // only allow 'c whitelisted system attributes'
20700                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20701 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20702                     //node.removeAttribute(n);
20703                     return true;
20704                 }
20705                 
20706                 
20707                  
20708                 
20709                 clean.push(p);
20710                 return true;
20711             });
20712             if (clean.length) { 
20713                 node.setAttribute(n, clean.join(';'));
20714             } else {
20715                 node.removeAttribute(n);
20716             }
20717             
20718         }
20719         
20720         
20721         for (var i = node.attributes.length-1; i > -1 ; i--) {
20722             var a = node.attributes[i];
20723             //console.log(a);
20724             
20725             if (a.name.toLowerCase().substr(0,2)=='on')  {
20726                 node.removeAttribute(a.name);
20727                 continue;
20728             }
20729             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20730                 node.removeAttribute(a.name);
20731                 continue;
20732             }
20733             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20734                 cleanAttr(a.name,a.value); // fixme..
20735                 continue;
20736             }
20737             if (a.name == 'style') {
20738                 cleanStyle(a.name,a.value);
20739                 continue;
20740             }
20741             /// clean up MS crap..
20742             // tecnically this should be a list of valid class'es..
20743             
20744             
20745             if (a.name == 'class') {
20746                 if (a.value.match(/^Mso/)) {
20747                     node.className = '';
20748                 }
20749                 
20750                 if (a.value.match(/body/)) {
20751                     node.className = '';
20752                 }
20753                 continue;
20754             }
20755             
20756             // style cleanup!?
20757             // class cleanup?
20758             
20759         }
20760         
20761         
20762         this.cleanUpChildren(node);
20763         
20764         
20765     },
20766     
20767     /**
20768      * Clean up MS wordisms...
20769      */
20770     cleanWord : function(node)
20771     {
20772         
20773         
20774         if (!node) {
20775             this.cleanWord(this.doc.body);
20776             return;
20777         }
20778         if (node.nodeName == "#text") {
20779             // clean up silly Windows -- stuff?
20780             return; 
20781         }
20782         if (node.nodeName == "#comment") {
20783             node.parentNode.removeChild(node);
20784             // clean up silly Windows -- stuff?
20785             return; 
20786         }
20787         
20788         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20789             node.parentNode.removeChild(node);
20790             return;
20791         }
20792         
20793         // remove - but keep children..
20794         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20795             while (node.childNodes.length) {
20796                 var cn = node.childNodes[0];
20797                 node.removeChild(cn);
20798                 node.parentNode.insertBefore(cn, node);
20799             }
20800             node.parentNode.removeChild(node);
20801             this.iterateChildren(node, this.cleanWord);
20802             return;
20803         }
20804         // clean styles
20805         if (node.className.length) {
20806             
20807             var cn = node.className.split(/\W+/);
20808             var cna = [];
20809             Roo.each(cn, function(cls) {
20810                 if (cls.match(/Mso[a-zA-Z]+/)) {
20811                     return;
20812                 }
20813                 cna.push(cls);
20814             });
20815             node.className = cna.length ? cna.join(' ') : '';
20816             if (!cna.length) {
20817                 node.removeAttribute("class");
20818             }
20819         }
20820         
20821         if (node.hasAttribute("lang")) {
20822             node.removeAttribute("lang");
20823         }
20824         
20825         if (node.hasAttribute("style")) {
20826             
20827             var styles = node.getAttribute("style").split(";");
20828             var nstyle = [];
20829             Roo.each(styles, function(s) {
20830                 if (!s.match(/:/)) {
20831                     return;
20832                 }
20833                 var kv = s.split(":");
20834                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20835                     return;
20836                 }
20837                 // what ever is left... we allow.
20838                 nstyle.push(s);
20839             });
20840             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20841             if (!nstyle.length) {
20842                 node.removeAttribute('style');
20843             }
20844         }
20845         this.iterateChildren(node, this.cleanWord);
20846         
20847         
20848         
20849     },
20850     /**
20851      * iterateChildren of a Node, calling fn each time, using this as the scole..
20852      * @param {DomNode} node node to iterate children of.
20853      * @param {Function} fn method of this class to call on each item.
20854      */
20855     iterateChildren : function(node, fn)
20856     {
20857         if (!node.childNodes.length) {
20858                 return;
20859         }
20860         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20861            fn.call(this, node.childNodes[i])
20862         }
20863     },
20864     
20865     
20866     /**
20867      * cleanTableWidths.
20868      *
20869      * Quite often pasting from word etc.. results in tables with column and widths.
20870      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20871      *
20872      */
20873     cleanTableWidths : function(node)
20874     {
20875          
20876          
20877         if (!node) {
20878             this.cleanTableWidths(this.doc.body);
20879             return;
20880         }
20881         
20882         // ignore list...
20883         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20884             return; 
20885         }
20886         Roo.log(node.tagName);
20887         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20888             this.iterateChildren(node, this.cleanTableWidths);
20889             return;
20890         }
20891         if (node.hasAttribute('width')) {
20892             node.removeAttribute('width');
20893         }
20894         
20895          
20896         if (node.hasAttribute("style")) {
20897             // pretty basic...
20898             
20899             var styles = node.getAttribute("style").split(";");
20900             var nstyle = [];
20901             Roo.each(styles, function(s) {
20902                 if (!s.match(/:/)) {
20903                     return;
20904                 }
20905                 var kv = s.split(":");
20906                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20907                     return;
20908                 }
20909                 // what ever is left... we allow.
20910                 nstyle.push(s);
20911             });
20912             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20913             if (!nstyle.length) {
20914                 node.removeAttribute('style');
20915             }
20916         }
20917         
20918         this.iterateChildren(node, this.cleanTableWidths);
20919         
20920         
20921     },
20922     
20923     
20924     
20925     
20926     domToHTML : function(currentElement, depth, nopadtext) {
20927         
20928         depth = depth || 0;
20929         nopadtext = nopadtext || false;
20930     
20931         if (!currentElement) {
20932             return this.domToHTML(this.doc.body);
20933         }
20934         
20935         //Roo.log(currentElement);
20936         var j;
20937         var allText = false;
20938         var nodeName = currentElement.nodeName;
20939         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20940         
20941         if  (nodeName == '#text') {
20942             
20943             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20944         }
20945         
20946         
20947         var ret = '';
20948         if (nodeName != 'BODY') {
20949              
20950             var i = 0;
20951             // Prints the node tagName, such as <A>, <IMG>, etc
20952             if (tagName) {
20953                 var attr = [];
20954                 for(i = 0; i < currentElement.attributes.length;i++) {
20955                     // quoting?
20956                     var aname = currentElement.attributes.item(i).name;
20957                     if (!currentElement.attributes.item(i).value.length) {
20958                         continue;
20959                     }
20960                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20961                 }
20962                 
20963                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20964             } 
20965             else {
20966                 
20967                 // eack
20968             }
20969         } else {
20970             tagName = false;
20971         }
20972         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20973             return ret;
20974         }
20975         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20976             nopadtext = true;
20977         }
20978         
20979         
20980         // Traverse the tree
20981         i = 0;
20982         var currentElementChild = currentElement.childNodes.item(i);
20983         var allText = true;
20984         var innerHTML  = '';
20985         lastnode = '';
20986         while (currentElementChild) {
20987             // Formatting code (indent the tree so it looks nice on the screen)
20988             var nopad = nopadtext;
20989             if (lastnode == 'SPAN') {
20990                 nopad  = true;
20991             }
20992             // text
20993             if  (currentElementChild.nodeName == '#text') {
20994                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20995                 toadd = nopadtext ? toadd : toadd.trim();
20996                 if (!nopad && toadd.length > 80) {
20997                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20998                 }
20999                 innerHTML  += toadd;
21000                 
21001                 i++;
21002                 currentElementChild = currentElement.childNodes.item(i);
21003                 lastNode = '';
21004                 continue;
21005             }
21006             allText = false;
21007             
21008             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21009                 
21010             // Recursively traverse the tree structure of the child node
21011             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21012             lastnode = currentElementChild.nodeName;
21013             i++;
21014             currentElementChild=currentElement.childNodes.item(i);
21015         }
21016         
21017         ret += innerHTML;
21018         
21019         if (!allText) {
21020                 // The remaining code is mostly for formatting the tree
21021             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21022         }
21023         
21024         
21025         if (tagName) {
21026             ret+= "</"+tagName+">";
21027         }
21028         return ret;
21029         
21030     },
21031         
21032     applyBlacklists : function()
21033     {
21034         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21035         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21036         
21037         this.white = [];
21038         this.black = [];
21039         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21040             if (b.indexOf(tag) > -1) {
21041                 return;
21042             }
21043             this.white.push(tag);
21044             
21045         }, this);
21046         
21047         Roo.each(w, function(tag) {
21048             if (b.indexOf(tag) > -1) {
21049                 return;
21050             }
21051             if (this.white.indexOf(tag) > -1) {
21052                 return;
21053             }
21054             this.white.push(tag);
21055             
21056         }, this);
21057         
21058         
21059         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21060             if (w.indexOf(tag) > -1) {
21061                 return;
21062             }
21063             this.black.push(tag);
21064             
21065         }, this);
21066         
21067         Roo.each(b, function(tag) {
21068             if (w.indexOf(tag) > -1) {
21069                 return;
21070             }
21071             if (this.black.indexOf(tag) > -1) {
21072                 return;
21073             }
21074             this.black.push(tag);
21075             
21076         }, this);
21077         
21078         
21079         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21080         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21081         
21082         this.cwhite = [];
21083         this.cblack = [];
21084         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21085             if (b.indexOf(tag) > -1) {
21086                 return;
21087             }
21088             this.cwhite.push(tag);
21089             
21090         }, this);
21091         
21092         Roo.each(w, function(tag) {
21093             if (b.indexOf(tag) > -1) {
21094                 return;
21095             }
21096             if (this.cwhite.indexOf(tag) > -1) {
21097                 return;
21098             }
21099             this.cwhite.push(tag);
21100             
21101         }, this);
21102         
21103         
21104         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21105             if (w.indexOf(tag) > -1) {
21106                 return;
21107             }
21108             this.cblack.push(tag);
21109             
21110         }, this);
21111         
21112         Roo.each(b, function(tag) {
21113             if (w.indexOf(tag) > -1) {
21114                 return;
21115             }
21116             if (this.cblack.indexOf(tag) > -1) {
21117                 return;
21118             }
21119             this.cblack.push(tag);
21120             
21121         }, this);
21122     },
21123     
21124     setStylesheets : function(stylesheets)
21125     {
21126         if(typeof(stylesheets) == 'string'){
21127             Roo.get(this.iframe.contentDocument.head).createChild({
21128                 tag : 'link',
21129                 rel : 'stylesheet',
21130                 type : 'text/css',
21131                 href : stylesheets
21132             });
21133             
21134             return;
21135         }
21136         var _this = this;
21137      
21138         Roo.each(stylesheets, function(s) {
21139             if(!s.length){
21140                 return;
21141             }
21142             
21143             Roo.get(_this.iframe.contentDocument.head).createChild({
21144                 tag : 'link',
21145                 rel : 'stylesheet',
21146                 type : 'text/css',
21147                 href : s
21148             });
21149         });
21150
21151         
21152     },
21153     
21154     removeStylesheets : function()
21155     {
21156         var _this = this;
21157         
21158         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21159             s.remove();
21160         });
21161     }
21162     
21163     // hide stuff that is not compatible
21164     /**
21165      * @event blur
21166      * @hide
21167      */
21168     /**
21169      * @event change
21170      * @hide
21171      */
21172     /**
21173      * @event focus
21174      * @hide
21175      */
21176     /**
21177      * @event specialkey
21178      * @hide
21179      */
21180     /**
21181      * @cfg {String} fieldClass @hide
21182      */
21183     /**
21184      * @cfg {String} focusClass @hide
21185      */
21186     /**
21187      * @cfg {String} autoCreate @hide
21188      */
21189     /**
21190      * @cfg {String} inputType @hide
21191      */
21192     /**
21193      * @cfg {String} invalidClass @hide
21194      */
21195     /**
21196      * @cfg {String} invalidText @hide
21197      */
21198     /**
21199      * @cfg {String} msgFx @hide
21200      */
21201     /**
21202      * @cfg {String} validateOnBlur @hide
21203      */
21204 });
21205
21206 Roo.HtmlEditorCore.white = [
21207         'area', 'br', 'img', 'input', 'hr', 'wbr',
21208         
21209        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21210        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21211        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21212        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21213        'table',   'ul',         'xmp', 
21214        
21215        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21216       'thead',   'tr', 
21217      
21218       'dir', 'menu', 'ol', 'ul', 'dl',
21219        
21220       'embed',  'object'
21221 ];
21222
21223
21224 Roo.HtmlEditorCore.black = [
21225     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21226         'applet', // 
21227         'base',   'basefont', 'bgsound', 'blink',  'body', 
21228         'frame',  'frameset', 'head',    'html',   'ilayer', 
21229         'iframe', 'layer',  'link',     'meta',    'object',   
21230         'script', 'style' ,'title',  'xml' // clean later..
21231 ];
21232 Roo.HtmlEditorCore.clean = [
21233     'script', 'style', 'title', 'xml'
21234 ];
21235 Roo.HtmlEditorCore.remove = [
21236     'font'
21237 ];
21238 // attributes..
21239
21240 Roo.HtmlEditorCore.ablack = [
21241     'on'
21242 ];
21243     
21244 Roo.HtmlEditorCore.aclean = [ 
21245     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21246 ];
21247
21248 // protocols..
21249 Roo.HtmlEditorCore.pwhite= [
21250         'http',  'https',  'mailto'
21251 ];
21252
21253 // white listed style attributes.
21254 Roo.HtmlEditorCore.cwhite= [
21255       //  'text-align', /// default is to allow most things..
21256       
21257          
21258 //        'font-size'//??
21259 ];
21260
21261 // black listed style attributes.
21262 Roo.HtmlEditorCore.cblack= [
21263       //  'font-size' -- this can be set by the project 
21264 ];
21265
21266
21267 Roo.HtmlEditorCore.swapCodes   =[ 
21268     [    8211, "--" ], 
21269     [    8212, "--" ], 
21270     [    8216,  "'" ],  
21271     [    8217, "'" ],  
21272     [    8220, '"' ],  
21273     [    8221, '"' ],  
21274     [    8226, "*" ],  
21275     [    8230, "..." ]
21276 ]; 
21277
21278     /*
21279  * - LGPL
21280  *
21281  * HtmlEditor
21282  * 
21283  */
21284
21285 /**
21286  * @class Roo.bootstrap.HtmlEditor
21287  * @extends Roo.bootstrap.TextArea
21288  * Bootstrap HtmlEditor class
21289
21290  * @constructor
21291  * Create a new HtmlEditor
21292  * @param {Object} config The config object
21293  */
21294
21295 Roo.bootstrap.HtmlEditor = function(config){
21296     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21297     if (!this.toolbars) {
21298         this.toolbars = [];
21299     }
21300     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21301     this.addEvents({
21302             /**
21303              * @event initialize
21304              * Fires when the editor is fully initialized (including the iframe)
21305              * @param {HtmlEditor} this
21306              */
21307             initialize: true,
21308             /**
21309              * @event activate
21310              * Fires when the editor is first receives the focus. Any insertion must wait
21311              * until after this event.
21312              * @param {HtmlEditor} this
21313              */
21314             activate: true,
21315              /**
21316              * @event beforesync
21317              * Fires before the textarea is updated with content from the editor iframe. Return false
21318              * to cancel the sync.
21319              * @param {HtmlEditor} this
21320              * @param {String} html
21321              */
21322             beforesync: true,
21323              /**
21324              * @event beforepush
21325              * Fires before the iframe editor is updated with content from the textarea. Return false
21326              * to cancel the push.
21327              * @param {HtmlEditor} this
21328              * @param {String} html
21329              */
21330             beforepush: true,
21331              /**
21332              * @event sync
21333              * Fires when the textarea is updated with content from the editor iframe.
21334              * @param {HtmlEditor} this
21335              * @param {String} html
21336              */
21337             sync: true,
21338              /**
21339              * @event push
21340              * Fires when the iframe editor is updated with content from the textarea.
21341              * @param {HtmlEditor} this
21342              * @param {String} html
21343              */
21344             push: true,
21345              /**
21346              * @event editmodechange
21347              * Fires when the editor switches edit modes
21348              * @param {HtmlEditor} this
21349              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21350              */
21351             editmodechange: true,
21352             /**
21353              * @event editorevent
21354              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21355              * @param {HtmlEditor} this
21356              */
21357             editorevent: true,
21358             /**
21359              * @event firstfocus
21360              * Fires when on first focus - needed by toolbars..
21361              * @param {HtmlEditor} this
21362              */
21363             firstfocus: true,
21364             /**
21365              * @event autosave
21366              * Auto save the htmlEditor value as a file into Events
21367              * @param {HtmlEditor} this
21368              */
21369             autosave: true,
21370             /**
21371              * @event savedpreview
21372              * preview the saved version of htmlEditor
21373              * @param {HtmlEditor} this
21374              */
21375             savedpreview: true
21376         });
21377 };
21378
21379
21380 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21381     
21382     
21383       /**
21384      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21385      */
21386     toolbars : false,
21387    
21388      /**
21389      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21390      *                        Roo.resizable.
21391      */
21392     resizable : false,
21393      /**
21394      * @cfg {Number} height (in pixels)
21395      */   
21396     height: 300,
21397    /**
21398      * @cfg {Number} width (in pixels)
21399      */   
21400     width: false,
21401     
21402     /**
21403      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21404      * 
21405      */
21406     stylesheets: false,
21407     
21408     // id of frame..
21409     frameId: false,
21410     
21411     // private properties
21412     validationEvent : false,
21413     deferHeight: true,
21414     initialized : false,
21415     activated : false,
21416     
21417     onFocus : Roo.emptyFn,
21418     iframePad:3,
21419     hideMode:'offsets',
21420     
21421     
21422     tbContainer : false,
21423     
21424     toolbarContainer :function() {
21425         return this.wrap.select('.x-html-editor-tb',true).first();
21426     },
21427
21428     /**
21429      * Protected method that will not generally be called directly. It
21430      * is called when the editor creates its toolbar. Override this method if you need to
21431      * add custom toolbar buttons.
21432      * @param {HtmlEditor} editor
21433      */
21434     createToolbar : function(){
21435         
21436         Roo.log("create toolbars");
21437         
21438         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21439         this.toolbars[0].render(this.toolbarContainer());
21440         
21441         return;
21442         
21443 //        if (!editor.toolbars || !editor.toolbars.length) {
21444 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21445 //        }
21446 //        
21447 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21448 //            editor.toolbars[i] = Roo.factory(
21449 //                    typeof(editor.toolbars[i]) == 'string' ?
21450 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21451 //                Roo.bootstrap.HtmlEditor);
21452 //            editor.toolbars[i].init(editor);
21453 //        }
21454     },
21455
21456      
21457     // private
21458     onRender : function(ct, position)
21459     {
21460        // Roo.log("Call onRender: " + this.xtype);
21461         var _t = this;
21462         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21463       
21464         this.wrap = this.inputEl().wrap({
21465             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21466         });
21467         
21468         this.editorcore.onRender(ct, position);
21469          
21470         if (this.resizable) {
21471             this.resizeEl = new Roo.Resizable(this.wrap, {
21472                 pinned : true,
21473                 wrap: true,
21474                 dynamic : true,
21475                 minHeight : this.height,
21476                 height: this.height,
21477                 handles : this.resizable,
21478                 width: this.width,
21479                 listeners : {
21480                     resize : function(r, w, h) {
21481                         _t.onResize(w,h); // -something
21482                     }
21483                 }
21484             });
21485             
21486         }
21487         this.createToolbar(this);
21488        
21489         
21490         if(!this.width && this.resizable){
21491             this.setSize(this.wrap.getSize());
21492         }
21493         if (this.resizeEl) {
21494             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21495             // should trigger onReize..
21496         }
21497         
21498     },
21499
21500     // private
21501     onResize : function(w, h)
21502     {
21503         Roo.log('resize: ' +w + ',' + h );
21504         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21505         var ew = false;
21506         var eh = false;
21507         
21508         if(this.inputEl() ){
21509             if(typeof w == 'number'){
21510                 var aw = w - this.wrap.getFrameWidth('lr');
21511                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21512                 ew = aw;
21513             }
21514             if(typeof h == 'number'){
21515                  var tbh = -11;  // fixme it needs to tool bar size!
21516                 for (var i =0; i < this.toolbars.length;i++) {
21517                     // fixme - ask toolbars for heights?
21518                     tbh += this.toolbars[i].el.getHeight();
21519                     //if (this.toolbars[i].footer) {
21520                     //    tbh += this.toolbars[i].footer.el.getHeight();
21521                     //}
21522                 }
21523               
21524                 
21525                 
21526                 
21527                 
21528                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21529                 ah -= 5; // knock a few pixes off for look..
21530                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21531                 var eh = ah;
21532             }
21533         }
21534         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21535         this.editorcore.onResize(ew,eh);
21536         
21537     },
21538
21539     /**
21540      * Toggles the editor between standard and source edit mode.
21541      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21542      */
21543     toggleSourceEdit : function(sourceEditMode)
21544     {
21545         this.editorcore.toggleSourceEdit(sourceEditMode);
21546         
21547         if(this.editorcore.sourceEditMode){
21548             Roo.log('editor - showing textarea');
21549             
21550 //            Roo.log('in');
21551 //            Roo.log(this.syncValue());
21552             this.syncValue();
21553             this.inputEl().removeClass(['hide', 'x-hidden']);
21554             this.inputEl().dom.removeAttribute('tabIndex');
21555             this.inputEl().focus();
21556         }else{
21557             Roo.log('editor - hiding textarea');
21558 //            Roo.log('out')
21559 //            Roo.log(this.pushValue()); 
21560             this.pushValue();
21561             
21562             this.inputEl().addClass(['hide', 'x-hidden']);
21563             this.inputEl().dom.setAttribute('tabIndex', -1);
21564             //this.deferFocus();
21565         }
21566          
21567         if(this.resizable){
21568             this.setSize(this.wrap.getSize());
21569         }
21570         
21571         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21572     },
21573  
21574     // private (for BoxComponent)
21575     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21576
21577     // private (for BoxComponent)
21578     getResizeEl : function(){
21579         return this.wrap;
21580     },
21581
21582     // private (for BoxComponent)
21583     getPositionEl : function(){
21584         return this.wrap;
21585     },
21586
21587     // private
21588     initEvents : function(){
21589         this.originalValue = this.getValue();
21590     },
21591
21592 //    /**
21593 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21594 //     * @method
21595 //     */
21596 //    markInvalid : Roo.emptyFn,
21597 //    /**
21598 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21599 //     * @method
21600 //     */
21601 //    clearInvalid : Roo.emptyFn,
21602
21603     setValue : function(v){
21604         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21605         this.editorcore.pushValue();
21606     },
21607
21608      
21609     // private
21610     deferFocus : function(){
21611         this.focus.defer(10, this);
21612     },
21613
21614     // doc'ed in Field
21615     focus : function(){
21616         this.editorcore.focus();
21617         
21618     },
21619       
21620
21621     // private
21622     onDestroy : function(){
21623         
21624         
21625         
21626         if(this.rendered){
21627             
21628             for (var i =0; i < this.toolbars.length;i++) {
21629                 // fixme - ask toolbars for heights?
21630                 this.toolbars[i].onDestroy();
21631             }
21632             
21633             this.wrap.dom.innerHTML = '';
21634             this.wrap.remove();
21635         }
21636     },
21637
21638     // private
21639     onFirstFocus : function(){
21640         //Roo.log("onFirstFocus");
21641         this.editorcore.onFirstFocus();
21642          for (var i =0; i < this.toolbars.length;i++) {
21643             this.toolbars[i].onFirstFocus();
21644         }
21645         
21646     },
21647     
21648     // private
21649     syncValue : function()
21650     {   
21651         this.editorcore.syncValue();
21652     },
21653     
21654     pushValue : function()
21655     {   
21656         this.editorcore.pushValue();
21657     }
21658      
21659     
21660     // hide stuff that is not compatible
21661     /**
21662      * @event blur
21663      * @hide
21664      */
21665     /**
21666      * @event change
21667      * @hide
21668      */
21669     /**
21670      * @event focus
21671      * @hide
21672      */
21673     /**
21674      * @event specialkey
21675      * @hide
21676      */
21677     /**
21678      * @cfg {String} fieldClass @hide
21679      */
21680     /**
21681      * @cfg {String} focusClass @hide
21682      */
21683     /**
21684      * @cfg {String} autoCreate @hide
21685      */
21686     /**
21687      * @cfg {String} inputType @hide
21688      */
21689     /**
21690      * @cfg {String} invalidClass @hide
21691      */
21692     /**
21693      * @cfg {String} invalidText @hide
21694      */
21695     /**
21696      * @cfg {String} msgFx @hide
21697      */
21698     /**
21699      * @cfg {String} validateOnBlur @hide
21700      */
21701 });
21702  
21703     
21704    
21705    
21706    
21707       
21708 Roo.namespace('Roo.bootstrap.htmleditor');
21709 /**
21710  * @class Roo.bootstrap.HtmlEditorToolbar1
21711  * Basic Toolbar
21712  * 
21713  * Usage:
21714  *
21715  new Roo.bootstrap.HtmlEditor({
21716     ....
21717     toolbars : [
21718         new Roo.bootstrap.HtmlEditorToolbar1({
21719             disable : { fonts: 1 , format: 1, ..., ... , ...],
21720             btns : [ .... ]
21721         })
21722     }
21723      
21724  * 
21725  * @cfg {Object} disable List of elements to disable..
21726  * @cfg {Array} btns List of additional buttons.
21727  * 
21728  * 
21729  * NEEDS Extra CSS? 
21730  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21731  */
21732  
21733 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21734 {
21735     
21736     Roo.apply(this, config);
21737     
21738     // default disabled, based on 'good practice'..
21739     this.disable = this.disable || {};
21740     Roo.applyIf(this.disable, {
21741         fontSize : true,
21742         colors : true,
21743         specialElements : true
21744     });
21745     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21746     
21747     this.editor = config.editor;
21748     this.editorcore = config.editor.editorcore;
21749     
21750     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21751     
21752     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21753     // dont call parent... till later.
21754 }
21755 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21756      
21757     bar : true,
21758     
21759     editor : false,
21760     editorcore : false,
21761     
21762     
21763     formats : [
21764         "p" ,  
21765         "h1","h2","h3","h4","h5","h6", 
21766         "pre", "code", 
21767         "abbr", "acronym", "address", "cite", "samp", "var",
21768         'div','span'
21769     ],
21770     
21771     onRender : function(ct, position)
21772     {
21773        // Roo.log("Call onRender: " + this.xtype);
21774         
21775        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21776        Roo.log(this.el);
21777        this.el.dom.style.marginBottom = '0';
21778        var _this = this;
21779        var editorcore = this.editorcore;
21780        var editor= this.editor;
21781        
21782        var children = [];
21783        var btn = function(id,cmd , toggle, handler){
21784        
21785             var  event = toggle ? 'toggle' : 'click';
21786        
21787             var a = {
21788                 size : 'sm',
21789                 xtype: 'Button',
21790                 xns: Roo.bootstrap,
21791                 glyphicon : id,
21792                 cmd : id || cmd,
21793                 enableToggle:toggle !== false,
21794                 //html : 'submit'
21795                 pressed : toggle ? false : null,
21796                 listeners : {}
21797             };
21798             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21799                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21800             };
21801             children.push(a);
21802             return a;
21803        }
21804         
21805         var style = {
21806                 xtype: 'Button',
21807                 size : 'sm',
21808                 xns: Roo.bootstrap,
21809                 glyphicon : 'font',
21810                 //html : 'submit'
21811                 menu : {
21812                     xtype: 'Menu',
21813                     xns: Roo.bootstrap,
21814                     items:  []
21815                 }
21816         };
21817         Roo.each(this.formats, function(f) {
21818             style.menu.items.push({
21819                 xtype :'MenuItem',
21820                 xns: Roo.bootstrap,
21821                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21822                 tagname : f,
21823                 listeners : {
21824                     click : function()
21825                     {
21826                         editorcore.insertTag(this.tagname);
21827                         editor.focus();
21828                     }
21829                 }
21830                 
21831             });
21832         });
21833          children.push(style);   
21834             
21835             
21836         btn('bold',false,true);
21837         btn('italic',false,true);
21838         btn('align-left', 'justifyleft',true);
21839         btn('align-center', 'justifycenter',true);
21840         btn('align-right' , 'justifyright',true);
21841         btn('link', false, false, function(btn) {
21842             //Roo.log("create link?");
21843             var url = prompt(this.createLinkText, this.defaultLinkValue);
21844             if(url && url != 'http:/'+'/'){
21845                 this.editorcore.relayCmd('createlink', url);
21846             }
21847         }),
21848         btn('list','insertunorderedlist',true);
21849         btn('pencil', false,true, function(btn){
21850                 Roo.log(this);
21851                 
21852                 this.toggleSourceEdit(btn.pressed);
21853         });
21854         /*
21855         var cog = {
21856                 xtype: 'Button',
21857                 size : 'sm',
21858                 xns: Roo.bootstrap,
21859                 glyphicon : 'cog',
21860                 //html : 'submit'
21861                 menu : {
21862                     xtype: 'Menu',
21863                     xns: Roo.bootstrap,
21864                     items:  []
21865                 }
21866         };
21867         
21868         cog.menu.items.push({
21869             xtype :'MenuItem',
21870             xns: Roo.bootstrap,
21871             html : Clean styles,
21872             tagname : f,
21873             listeners : {
21874                 click : function()
21875                 {
21876                     editorcore.insertTag(this.tagname);
21877                     editor.focus();
21878                 }
21879             }
21880             
21881         });
21882        */
21883         
21884          
21885        this.xtype = 'NavSimplebar';
21886         
21887         for(var i=0;i< children.length;i++) {
21888             
21889             this.buttons.add(this.addxtypeChild(children[i]));
21890             
21891         }
21892         
21893         editor.on('editorevent', this.updateToolbar, this);
21894     },
21895     onBtnClick : function(id)
21896     {
21897        this.editorcore.relayCmd(id);
21898        this.editorcore.focus();
21899     },
21900     
21901     /**
21902      * Protected method that will not generally be called directly. It triggers
21903      * a toolbar update by reading the markup state of the current selection in the editor.
21904      */
21905     updateToolbar: function(){
21906
21907         if(!this.editorcore.activated){
21908             this.editor.onFirstFocus(); // is this neeed?
21909             return;
21910         }
21911
21912         var btns = this.buttons; 
21913         var doc = this.editorcore.doc;
21914         btns.get('bold').setActive(doc.queryCommandState('bold'));
21915         btns.get('italic').setActive(doc.queryCommandState('italic'));
21916         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21917         
21918         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21919         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21920         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21921         
21922         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21923         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21924          /*
21925         
21926         var ans = this.editorcore.getAllAncestors();
21927         if (this.formatCombo) {
21928             
21929             
21930             var store = this.formatCombo.store;
21931             this.formatCombo.setValue("");
21932             for (var i =0; i < ans.length;i++) {
21933                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21934                     // select it..
21935                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21936                     break;
21937                 }
21938             }
21939         }
21940         
21941         
21942         
21943         // hides menus... - so this cant be on a menu...
21944         Roo.bootstrap.MenuMgr.hideAll();
21945         */
21946         Roo.bootstrap.MenuMgr.hideAll();
21947         //this.editorsyncValue();
21948     },
21949     onFirstFocus: function() {
21950         this.buttons.each(function(item){
21951            item.enable();
21952         });
21953     },
21954     toggleSourceEdit : function(sourceEditMode){
21955         
21956           
21957         if(sourceEditMode){
21958             Roo.log("disabling buttons");
21959            this.buttons.each( function(item){
21960                 if(item.cmd != 'pencil'){
21961                     item.disable();
21962                 }
21963             });
21964           
21965         }else{
21966             Roo.log("enabling buttons");
21967             if(this.editorcore.initialized){
21968                 this.buttons.each( function(item){
21969                     item.enable();
21970                 });
21971             }
21972             
21973         }
21974         Roo.log("calling toggole on editor");
21975         // tell the editor that it's been pressed..
21976         this.editor.toggleSourceEdit(sourceEditMode);
21977        
21978     }
21979 });
21980
21981
21982
21983
21984
21985 /**
21986  * @class Roo.bootstrap.Table.AbstractSelectionModel
21987  * @extends Roo.util.Observable
21988  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21989  * implemented by descendant classes.  This class should not be directly instantiated.
21990  * @constructor
21991  */
21992 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21993     this.locked = false;
21994     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21995 };
21996
21997
21998 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21999     /** @ignore Called by the grid automatically. Do not call directly. */
22000     init : function(grid){
22001         this.grid = grid;
22002         this.initEvents();
22003     },
22004
22005     /**
22006      * Locks the selections.
22007      */
22008     lock : function(){
22009         this.locked = true;
22010     },
22011
22012     /**
22013      * Unlocks the selections.
22014      */
22015     unlock : function(){
22016         this.locked = false;
22017     },
22018
22019     /**
22020      * Returns true if the selections are locked.
22021      * @return {Boolean}
22022      */
22023     isLocked : function(){
22024         return this.locked;
22025     }
22026 });
22027 /**
22028  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22029  * @class Roo.bootstrap.Table.RowSelectionModel
22030  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22031  * It supports multiple selections and keyboard selection/navigation. 
22032  * @constructor
22033  * @param {Object} config
22034  */
22035
22036 Roo.bootstrap.Table.RowSelectionModel = function(config){
22037     Roo.apply(this, config);
22038     this.selections = new Roo.util.MixedCollection(false, function(o){
22039         return o.id;
22040     });
22041
22042     this.last = false;
22043     this.lastActive = false;
22044
22045     this.addEvents({
22046         /**
22047              * @event selectionchange
22048              * Fires when the selection changes
22049              * @param {SelectionModel} this
22050              */
22051             "selectionchange" : true,
22052         /**
22053              * @event afterselectionchange
22054              * Fires after the selection changes (eg. by key press or clicking)
22055              * @param {SelectionModel} this
22056              */
22057             "afterselectionchange" : true,
22058         /**
22059              * @event beforerowselect
22060              * Fires when a row is selected being selected, return false to cancel.
22061              * @param {SelectionModel} this
22062              * @param {Number} rowIndex The selected index
22063              * @param {Boolean} keepExisting False if other selections will be cleared
22064              */
22065             "beforerowselect" : true,
22066         /**
22067              * @event rowselect
22068              * Fires when a row is selected.
22069              * @param {SelectionModel} this
22070              * @param {Number} rowIndex The selected index
22071              * @param {Roo.data.Record} r The record
22072              */
22073             "rowselect" : true,
22074         /**
22075              * @event rowdeselect
22076              * Fires when a row is deselected.
22077              * @param {SelectionModel} this
22078              * @param {Number} rowIndex The selected index
22079              */
22080         "rowdeselect" : true
22081     });
22082     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22083     this.locked = false;
22084 };
22085
22086 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22087     /**
22088      * @cfg {Boolean} singleSelect
22089      * True to allow selection of only one row at a time (defaults to false)
22090      */
22091     singleSelect : false,
22092
22093     // private
22094     initEvents : function(){
22095
22096         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22097             this.grid.on("mousedown", this.handleMouseDown, this);
22098         }else{ // allow click to work like normal
22099             this.grid.on("rowclick", this.handleDragableRowClick, this);
22100         }
22101
22102         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22103             "up" : function(e){
22104                 if(!e.shiftKey){
22105                     this.selectPrevious(e.shiftKey);
22106                 }else if(this.last !== false && this.lastActive !== false){
22107                     var last = this.last;
22108                     this.selectRange(this.last,  this.lastActive-1);
22109                     this.grid.getView().focusRow(this.lastActive);
22110                     if(last !== false){
22111                         this.last = last;
22112                     }
22113                 }else{
22114                     this.selectFirstRow();
22115                 }
22116                 this.fireEvent("afterselectionchange", this);
22117             },
22118             "down" : function(e){
22119                 if(!e.shiftKey){
22120                     this.selectNext(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             scope: this
22134         });
22135
22136         var view = this.grid.view;
22137         view.on("refresh", this.onRefresh, this);
22138         view.on("rowupdated", this.onRowUpdated, this);
22139         view.on("rowremoved", this.onRemove, this);
22140     },
22141
22142     // private
22143     onRefresh : function(){
22144         var ds = this.grid.dataSource, i, v = this.grid.view;
22145         var s = this.selections;
22146         s.each(function(r){
22147             if((i = ds.indexOfId(r.id)) != -1){
22148                 v.onRowSelect(i);
22149             }else{
22150                 s.remove(r);
22151             }
22152         });
22153     },
22154
22155     // private
22156     onRemove : function(v, index, r){
22157         this.selections.remove(r);
22158     },
22159
22160     // private
22161     onRowUpdated : function(v, index, r){
22162         if(this.isSelected(r)){
22163             v.onRowSelect(index);
22164         }
22165     },
22166
22167     /**
22168      * Select records.
22169      * @param {Array} records The records to select
22170      * @param {Boolean} keepExisting (optional) True to keep existing selections
22171      */
22172     selectRecords : function(records, keepExisting){
22173         if(!keepExisting){
22174             this.clearSelections();
22175         }
22176         var ds = this.grid.dataSource;
22177         for(var i = 0, len = records.length; i < len; i++){
22178             this.selectRow(ds.indexOf(records[i]), true);
22179         }
22180     },
22181
22182     /**
22183      * Gets the number of selected rows.
22184      * @return {Number}
22185      */
22186     getCount : function(){
22187         return this.selections.length;
22188     },
22189
22190     /**
22191      * Selects the first row in the grid.
22192      */
22193     selectFirstRow : function(){
22194         this.selectRow(0);
22195     },
22196
22197     /**
22198      * Select the last row.
22199      * @param {Boolean} keepExisting (optional) True to keep existing selections
22200      */
22201     selectLastRow : function(keepExisting){
22202         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22203     },
22204
22205     /**
22206      * Selects the row immediately following the last selected row.
22207      * @param {Boolean} keepExisting (optional) True to keep existing selections
22208      */
22209     selectNext : function(keepExisting){
22210         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22211             this.selectRow(this.last+1, keepExisting);
22212             this.grid.getView().focusRow(this.last);
22213         }
22214     },
22215
22216     /**
22217      * Selects the row that precedes the last selected row.
22218      * @param {Boolean} keepExisting (optional) True to keep existing selections
22219      */
22220     selectPrevious : function(keepExisting){
22221         if(this.last){
22222             this.selectRow(this.last-1, keepExisting);
22223             this.grid.getView().focusRow(this.last);
22224         }
22225     },
22226
22227     /**
22228      * Returns the selected records
22229      * @return {Array} Array of selected records
22230      */
22231     getSelections : function(){
22232         return [].concat(this.selections.items);
22233     },
22234
22235     /**
22236      * Returns the first selected record.
22237      * @return {Record}
22238      */
22239     getSelected : function(){
22240         return this.selections.itemAt(0);
22241     },
22242
22243
22244     /**
22245      * Clears all selections.
22246      */
22247     clearSelections : function(fast){
22248         if(this.locked) {
22249             return;
22250         }
22251         if(fast !== true){
22252             var ds = this.grid.dataSource;
22253             var s = this.selections;
22254             s.each(function(r){
22255                 this.deselectRow(ds.indexOfId(r.id));
22256             }, this);
22257             s.clear();
22258         }else{
22259             this.selections.clear();
22260         }
22261         this.last = false;
22262     },
22263
22264
22265     /**
22266      * Selects all rows.
22267      */
22268     selectAll : function(){
22269         if(this.locked) {
22270             return;
22271         }
22272         this.selections.clear();
22273         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22274             this.selectRow(i, true);
22275         }
22276     },
22277
22278     /**
22279      * Returns True if there is a selection.
22280      * @return {Boolean}
22281      */
22282     hasSelection : function(){
22283         return this.selections.length > 0;
22284     },
22285
22286     /**
22287      * Returns True if the specified row is selected.
22288      * @param {Number/Record} record The record or index of the record to check
22289      * @return {Boolean}
22290      */
22291     isSelected : function(index){
22292         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22293         return (r && this.selections.key(r.id) ? true : false);
22294     },
22295
22296     /**
22297      * Returns True if the specified record id is selected.
22298      * @param {String} id The id of record to check
22299      * @return {Boolean}
22300      */
22301     isIdSelected : function(id){
22302         return (this.selections.key(id) ? true : false);
22303     },
22304
22305     // private
22306     handleMouseDown : function(e, t){
22307         var view = this.grid.getView(), rowIndex;
22308         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22309             return;
22310         };
22311         if(e.shiftKey && this.last !== false){
22312             var last = this.last;
22313             this.selectRange(last, rowIndex, e.ctrlKey);
22314             this.last = last; // reset the last
22315             view.focusRow(rowIndex);
22316         }else{
22317             var isSelected = this.isSelected(rowIndex);
22318             if(e.button !== 0 && isSelected){
22319                 view.focusRow(rowIndex);
22320             }else if(e.ctrlKey && isSelected){
22321                 this.deselectRow(rowIndex);
22322             }else if(!isSelected){
22323                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22324                 view.focusRow(rowIndex);
22325             }
22326         }
22327         this.fireEvent("afterselectionchange", this);
22328     },
22329     // private
22330     handleDragableRowClick :  function(grid, rowIndex, e) 
22331     {
22332         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22333             this.selectRow(rowIndex, false);
22334             grid.view.focusRow(rowIndex);
22335              this.fireEvent("afterselectionchange", this);
22336         }
22337     },
22338     
22339     /**
22340      * Selects multiple rows.
22341      * @param {Array} rows Array of the indexes of the row to select
22342      * @param {Boolean} keepExisting (optional) True to keep existing selections
22343      */
22344     selectRows : function(rows, keepExisting){
22345         if(!keepExisting){
22346             this.clearSelections();
22347         }
22348         for(var i = 0, len = rows.length; i < len; i++){
22349             this.selectRow(rows[i], true);
22350         }
22351     },
22352
22353     /**
22354      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22355      * @param {Number} startRow The index of the first row in the range
22356      * @param {Number} endRow The index of the last row in the range
22357      * @param {Boolean} keepExisting (optional) True to retain existing selections
22358      */
22359     selectRange : function(startRow, endRow, keepExisting){
22360         if(this.locked) {
22361             return;
22362         }
22363         if(!keepExisting){
22364             this.clearSelections();
22365         }
22366         if(startRow <= endRow){
22367             for(var i = startRow; i <= endRow; i++){
22368                 this.selectRow(i, true);
22369             }
22370         }else{
22371             for(var i = startRow; i >= endRow; i--){
22372                 this.selectRow(i, true);
22373             }
22374         }
22375     },
22376
22377     /**
22378      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22379      * @param {Number} startRow The index of the first row in the range
22380      * @param {Number} endRow The index of the last row in the range
22381      */
22382     deselectRange : function(startRow, endRow, preventViewNotify){
22383         if(this.locked) {
22384             return;
22385         }
22386         for(var i = startRow; i <= endRow; i++){
22387             this.deselectRow(i, preventViewNotify);
22388         }
22389     },
22390
22391     /**
22392      * Selects a row.
22393      * @param {Number} row The index of the row to select
22394      * @param {Boolean} keepExisting (optional) True to keep existing selections
22395      */
22396     selectRow : function(index, keepExisting, preventViewNotify){
22397         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22398             return;
22399         }
22400         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22401             if(!keepExisting || this.singleSelect){
22402                 this.clearSelections();
22403             }
22404             var r = this.grid.dataSource.getAt(index);
22405             this.selections.add(r);
22406             this.last = this.lastActive = index;
22407             if(!preventViewNotify){
22408                 this.grid.getView().onRowSelect(index);
22409             }
22410             this.fireEvent("rowselect", this, index, r);
22411             this.fireEvent("selectionchange", this);
22412         }
22413     },
22414
22415     /**
22416      * Deselects a row.
22417      * @param {Number} row The index of the row to deselect
22418      */
22419     deselectRow : function(index, preventViewNotify){
22420         if(this.locked) {
22421             return;
22422         }
22423         if(this.last == index){
22424             this.last = false;
22425         }
22426         if(this.lastActive == index){
22427             this.lastActive = false;
22428         }
22429         var r = this.grid.dataSource.getAt(index);
22430         this.selections.remove(r);
22431         if(!preventViewNotify){
22432             this.grid.getView().onRowDeselect(index);
22433         }
22434         this.fireEvent("rowdeselect", this, index);
22435         this.fireEvent("selectionchange", this);
22436     },
22437
22438     // private
22439     restoreLast : function(){
22440         if(this._last){
22441             this.last = this._last;
22442         }
22443     },
22444
22445     // private
22446     acceptsNav : function(row, col, cm){
22447         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22448     },
22449
22450     // private
22451     onEditorKey : function(field, e){
22452         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22453         if(k == e.TAB){
22454             e.stopEvent();
22455             ed.completeEdit();
22456             if(e.shiftKey){
22457                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22458             }else{
22459                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22460             }
22461         }else if(k == e.ENTER && !e.ctrlKey){
22462             e.stopEvent();
22463             ed.completeEdit();
22464             if(e.shiftKey){
22465                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22466             }else{
22467                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22468             }
22469         }else if(k == e.ESC){
22470             ed.cancelEdit();
22471         }
22472         if(newCell){
22473             g.startEditing(newCell[0], newCell[1]);
22474         }
22475     }
22476 });/*
22477  * Based on:
22478  * Ext JS Library 1.1.1
22479  * Copyright(c) 2006-2007, Ext JS, LLC.
22480  *
22481  * Originally Released Under LGPL - original licence link has changed is not relivant.
22482  *
22483  * Fork - LGPL
22484  * <script type="text/javascript">
22485  */
22486  
22487 /**
22488  * @class Roo.bootstrap.PagingToolbar
22489  * @extends Roo.bootstrap.NavSimplebar
22490  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22491  * @constructor
22492  * Create a new PagingToolbar
22493  * @param {Object} config The config object
22494  * @param {Roo.data.Store} store
22495  */
22496 Roo.bootstrap.PagingToolbar = function(config)
22497 {
22498     // old args format still supported... - xtype is prefered..
22499         // created from xtype...
22500     
22501     this.ds = config.dataSource;
22502     
22503     if (config.store && !this.ds) {
22504         this.store= Roo.factory(config.store, Roo.data);
22505         this.ds = this.store;
22506         this.ds.xmodule = this.xmodule || false;
22507     }
22508     
22509     this.toolbarItems = [];
22510     if (config.items) {
22511         this.toolbarItems = config.items;
22512     }
22513     
22514     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22515     
22516     this.cursor = 0;
22517     
22518     if (this.ds) { 
22519         this.bind(this.ds);
22520     }
22521     
22522     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22523     
22524 };
22525
22526 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22527     /**
22528      * @cfg {Roo.data.Store} dataSource
22529      * The underlying data store providing the paged data
22530      */
22531     /**
22532      * @cfg {String/HTMLElement/Element} container
22533      * container The id or element that will contain the toolbar
22534      */
22535     /**
22536      * @cfg {Boolean} displayInfo
22537      * True to display the displayMsg (defaults to false)
22538      */
22539     /**
22540      * @cfg {Number} pageSize
22541      * The number of records to display per page (defaults to 20)
22542      */
22543     pageSize: 20,
22544     /**
22545      * @cfg {String} displayMsg
22546      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22547      */
22548     displayMsg : 'Displaying {0} - {1} of {2}',
22549     /**
22550      * @cfg {String} emptyMsg
22551      * The message to display when no records are found (defaults to "No data to display")
22552      */
22553     emptyMsg : 'No data to display',
22554     /**
22555      * Customizable piece of the default paging text (defaults to "Page")
22556      * @type String
22557      */
22558     beforePageText : "Page",
22559     /**
22560      * Customizable piece of the default paging text (defaults to "of %0")
22561      * @type String
22562      */
22563     afterPageText : "of {0}",
22564     /**
22565      * Customizable piece of the default paging text (defaults to "First Page")
22566      * @type String
22567      */
22568     firstText : "First Page",
22569     /**
22570      * Customizable piece of the default paging text (defaults to "Previous Page")
22571      * @type String
22572      */
22573     prevText : "Previous Page",
22574     /**
22575      * Customizable piece of the default paging text (defaults to "Next Page")
22576      * @type String
22577      */
22578     nextText : "Next Page",
22579     /**
22580      * Customizable piece of the default paging text (defaults to "Last Page")
22581      * @type String
22582      */
22583     lastText : "Last Page",
22584     /**
22585      * Customizable piece of the default paging text (defaults to "Refresh")
22586      * @type String
22587      */
22588     refreshText : "Refresh",
22589
22590     buttons : false,
22591     // private
22592     onRender : function(ct, position) 
22593     {
22594         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22595         this.navgroup.parentId = this.id;
22596         this.navgroup.onRender(this.el, null);
22597         // add the buttons to the navgroup
22598         
22599         if(this.displayInfo){
22600             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22601             this.displayEl = this.el.select('.x-paging-info', true).first();
22602 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22603 //            this.displayEl = navel.el.select('span',true).first();
22604         }
22605         
22606         var _this = this;
22607         
22608         if(this.buttons){
22609             Roo.each(_this.buttons, function(e){ // this might need to use render????
22610                Roo.factory(e).onRender(_this.el, null);
22611             });
22612         }
22613             
22614         Roo.each(_this.toolbarItems, function(e) {
22615             _this.navgroup.addItem(e);
22616         });
22617         
22618         
22619         this.first = this.navgroup.addItem({
22620             tooltip: this.firstText,
22621             cls: "prev",
22622             icon : 'fa fa-backward',
22623             disabled: true,
22624             preventDefault: true,
22625             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22626         });
22627         
22628         this.prev =  this.navgroup.addItem({
22629             tooltip: this.prevText,
22630             cls: "prev",
22631             icon : 'fa fa-step-backward',
22632             disabled: true,
22633             preventDefault: true,
22634             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22635         });
22636     //this.addSeparator();
22637         
22638         
22639         var field = this.navgroup.addItem( {
22640             tagtype : 'span',
22641             cls : 'x-paging-position',
22642             
22643             html : this.beforePageText  +
22644                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22645                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22646          } ); //?? escaped?
22647         
22648         this.field = field.el.select('input', true).first();
22649         this.field.on("keydown", this.onPagingKeydown, this);
22650         this.field.on("focus", function(){this.dom.select();});
22651     
22652     
22653         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22654         //this.field.setHeight(18);
22655         //this.addSeparator();
22656         this.next = this.navgroup.addItem({
22657             tooltip: this.nextText,
22658             cls: "next",
22659             html : ' <i class="fa fa-step-forward">',
22660             disabled: true,
22661             preventDefault: true,
22662             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22663         });
22664         this.last = this.navgroup.addItem({
22665             tooltip: this.lastText,
22666             icon : 'fa fa-forward',
22667             cls: "next",
22668             disabled: true,
22669             preventDefault: true,
22670             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22671         });
22672     //this.addSeparator();
22673         this.loading = this.navgroup.addItem({
22674             tooltip: this.refreshText,
22675             icon: 'fa fa-refresh',
22676             preventDefault: true,
22677             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22678         });
22679         
22680     },
22681
22682     // private
22683     updateInfo : function(){
22684         if(this.displayEl){
22685             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22686             var msg = count == 0 ?
22687                 this.emptyMsg :
22688                 String.format(
22689                     this.displayMsg,
22690                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22691                 );
22692             this.displayEl.update(msg);
22693         }
22694     },
22695
22696     // private
22697     onLoad : function(ds, r, o){
22698        this.cursor = o.params ? o.params.start : 0;
22699        var d = this.getPageData(),
22700             ap = d.activePage,
22701             ps = d.pages;
22702         
22703        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22704        this.field.dom.value = ap;
22705        this.first.setDisabled(ap == 1);
22706        this.prev.setDisabled(ap == 1);
22707        this.next.setDisabled(ap == ps);
22708        this.last.setDisabled(ap == ps);
22709        this.loading.enable();
22710        this.updateInfo();
22711     },
22712
22713     // private
22714     getPageData : function(){
22715         var total = this.ds.getTotalCount();
22716         return {
22717             total : total,
22718             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22719             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22720         };
22721     },
22722
22723     // private
22724     onLoadError : function(){
22725         this.loading.enable();
22726     },
22727
22728     // private
22729     onPagingKeydown : function(e){
22730         var k = e.getKey();
22731         var d = this.getPageData();
22732         if(k == e.RETURN){
22733             var v = this.field.dom.value, pageNum;
22734             if(!v || isNaN(pageNum = parseInt(v, 10))){
22735                 this.field.dom.value = d.activePage;
22736                 return;
22737             }
22738             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22739             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22740             e.stopEvent();
22741         }
22742         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))
22743         {
22744           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22745           this.field.dom.value = pageNum;
22746           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22747           e.stopEvent();
22748         }
22749         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22750         {
22751           var v = this.field.dom.value, pageNum; 
22752           var increment = (e.shiftKey) ? 10 : 1;
22753           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22754                 increment *= -1;
22755           }
22756           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22757             this.field.dom.value = d.activePage;
22758             return;
22759           }
22760           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22761           {
22762             this.field.dom.value = parseInt(v, 10) + increment;
22763             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22764             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22765           }
22766           e.stopEvent();
22767         }
22768     },
22769
22770     // private
22771     beforeLoad : function(){
22772         if(this.loading){
22773             this.loading.disable();
22774         }
22775     },
22776
22777     // private
22778     onClick : function(which){
22779         
22780         var ds = this.ds;
22781         if (!ds) {
22782             return;
22783         }
22784         
22785         switch(which){
22786             case "first":
22787                 ds.load({params:{start: 0, limit: this.pageSize}});
22788             break;
22789             case "prev":
22790                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22791             break;
22792             case "next":
22793                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22794             break;
22795             case "last":
22796                 var total = ds.getTotalCount();
22797                 var extra = total % this.pageSize;
22798                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22799                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22800             break;
22801             case "refresh":
22802                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22803             break;
22804         }
22805     },
22806
22807     /**
22808      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22809      * @param {Roo.data.Store} store The data store to unbind
22810      */
22811     unbind : function(ds){
22812         ds.un("beforeload", this.beforeLoad, this);
22813         ds.un("load", this.onLoad, this);
22814         ds.un("loadexception", this.onLoadError, this);
22815         ds.un("remove", this.updateInfo, this);
22816         ds.un("add", this.updateInfo, this);
22817         this.ds = undefined;
22818     },
22819
22820     /**
22821      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22822      * @param {Roo.data.Store} store The data store to bind
22823      */
22824     bind : function(ds){
22825         ds.on("beforeload", this.beforeLoad, this);
22826         ds.on("load", this.onLoad, this);
22827         ds.on("loadexception", this.onLoadError, this);
22828         ds.on("remove", this.updateInfo, this);
22829         ds.on("add", this.updateInfo, this);
22830         this.ds = ds;
22831     }
22832 });/*
22833  * - LGPL
22834  *
22835  * element
22836  * 
22837  */
22838
22839 /**
22840  * @class Roo.bootstrap.MessageBar
22841  * @extends Roo.bootstrap.Component
22842  * Bootstrap MessageBar class
22843  * @cfg {String} html contents of the MessageBar
22844  * @cfg {String} weight (info | success | warning | danger) default info
22845  * @cfg {String} beforeClass insert the bar before the given class
22846  * @cfg {Boolean} closable (true | false) default false
22847  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22848  * 
22849  * @constructor
22850  * Create a new Element
22851  * @param {Object} config The config object
22852  */
22853
22854 Roo.bootstrap.MessageBar = function(config){
22855     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22856 };
22857
22858 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22859     
22860     html: '',
22861     weight: 'info',
22862     closable: false,
22863     fixed: false,
22864     beforeClass: 'bootstrap-sticky-wrap',
22865     
22866     getAutoCreate : function(){
22867         
22868         var cfg = {
22869             tag: 'div',
22870             cls: 'alert alert-dismissable alert-' + this.weight,
22871             cn: [
22872                 {
22873                     tag: 'span',
22874                     cls: 'message',
22875                     html: this.html || ''
22876                 }
22877             ]
22878         };
22879         
22880         if(this.fixed){
22881             cfg.cls += ' alert-messages-fixed';
22882         }
22883         
22884         if(this.closable){
22885             cfg.cn.push({
22886                 tag: 'button',
22887                 cls: 'close',
22888                 html: 'x'
22889             });
22890         }
22891         
22892         return cfg;
22893     },
22894     
22895     onRender : function(ct, position)
22896     {
22897         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22898         
22899         if(!this.el){
22900             var cfg = Roo.apply({},  this.getAutoCreate());
22901             cfg.id = Roo.id();
22902             
22903             if (this.cls) {
22904                 cfg.cls += ' ' + this.cls;
22905             }
22906             if (this.style) {
22907                 cfg.style = this.style;
22908             }
22909             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22910             
22911             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22912         }
22913         
22914         this.el.select('>button.close').on('click', this.hide, this);
22915         
22916     },
22917     
22918     show : function()
22919     {
22920         if (!this.rendered) {
22921             this.render();
22922         }
22923         
22924         this.el.show();
22925         
22926         this.fireEvent('show', this);
22927         
22928     },
22929     
22930     hide : function()
22931     {
22932         if (!this.rendered) {
22933             this.render();
22934         }
22935         
22936         this.el.hide();
22937         
22938         this.fireEvent('hide', this);
22939     },
22940     
22941     update : function()
22942     {
22943 //        var e = this.el.dom.firstChild;
22944 //        
22945 //        if(this.closable){
22946 //            e = e.nextSibling;
22947 //        }
22948 //        
22949 //        e.data = this.html || '';
22950
22951         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22952     }
22953    
22954 });
22955
22956  
22957
22958      /*
22959  * - LGPL
22960  *
22961  * Graph
22962  * 
22963  */
22964
22965
22966 /**
22967  * @class Roo.bootstrap.Graph
22968  * @extends Roo.bootstrap.Component
22969  * Bootstrap Graph class
22970 > Prameters
22971  -sm {number} sm 4
22972  -md {number} md 5
22973  @cfg {String} graphtype  bar | vbar | pie
22974  @cfg {number} g_x coodinator | centre x (pie)
22975  @cfg {number} g_y coodinator | centre y (pie)
22976  @cfg {number} g_r radius (pie)
22977  @cfg {number} g_height height of the chart (respected by all elements in the set)
22978  @cfg {number} g_width width of the chart (respected by all elements in the set)
22979  @cfg {Object} title The title of the chart
22980     
22981  -{Array}  values
22982  -opts (object) options for the chart 
22983      o {
22984      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22985      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22986      o vgutter (number)
22987      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.
22988      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22989      o to
22990      o stretch (boolean)
22991      o }
22992  -opts (object) options for the pie
22993      o{
22994      o cut
22995      o startAngle (number)
22996      o endAngle (number)
22997      } 
22998  *
22999  * @constructor
23000  * Create a new Input
23001  * @param {Object} config The config object
23002  */
23003
23004 Roo.bootstrap.Graph = function(config){
23005     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23006     
23007     this.addEvents({
23008         // img events
23009         /**
23010          * @event click
23011          * The img click event for the img.
23012          * @param {Roo.EventObject} e
23013          */
23014         "click" : true
23015     });
23016 };
23017
23018 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23019     
23020     sm: 4,
23021     md: 5,
23022     graphtype: 'bar',
23023     g_height: 250,
23024     g_width: 400,
23025     g_x: 50,
23026     g_y: 50,
23027     g_r: 30,
23028     opts:{
23029         //g_colors: this.colors,
23030         g_type: 'soft',
23031         g_gutter: '20%'
23032
23033     },
23034     title : false,
23035
23036     getAutoCreate : function(){
23037         
23038         var cfg = {
23039             tag: 'div',
23040             html : null
23041         };
23042         
23043         
23044         return  cfg;
23045     },
23046
23047     onRender : function(ct,position){
23048         
23049         
23050         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23051         
23052         if (typeof(Raphael) == 'undefined') {
23053             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23054             return;
23055         }
23056         
23057         this.raphael = Raphael(this.el.dom);
23058         
23059                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23060                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23061                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23062                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23063                 /*
23064                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23065                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23066                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23067                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23068                 
23069                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23070                 r.barchart(330, 10, 300, 220, data1);
23071                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23072                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23073                 */
23074                 
23075                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23076                 // r.barchart(30, 30, 560, 250,  xdata, {
23077                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23078                 //     axis : "0 0 1 1",
23079                 //     axisxlabels :  xdata
23080                 //     //yvalues : cols,
23081                    
23082                 // });
23083 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23084 //        
23085 //        this.load(null,xdata,{
23086 //                axis : "0 0 1 1",
23087 //                axisxlabels :  xdata
23088 //                });
23089
23090     },
23091
23092     load : function(graphtype,xdata,opts)
23093     {
23094         this.raphael.clear();
23095         if(!graphtype) {
23096             graphtype = this.graphtype;
23097         }
23098         if(!opts){
23099             opts = this.opts;
23100         }
23101         var r = this.raphael,
23102             fin = function () {
23103                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23104             },
23105             fout = function () {
23106                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23107             },
23108             pfin = function() {
23109                 this.sector.stop();
23110                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23111
23112                 if (this.label) {
23113                     this.label[0].stop();
23114                     this.label[0].attr({ r: 7.5 });
23115                     this.label[1].attr({ "font-weight": 800 });
23116                 }
23117             },
23118             pfout = function() {
23119                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23120
23121                 if (this.label) {
23122                     this.label[0].animate({ r: 5 }, 500, "bounce");
23123                     this.label[1].attr({ "font-weight": 400 });
23124                 }
23125             };
23126
23127         switch(graphtype){
23128             case 'bar':
23129                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23130                 break;
23131             case 'hbar':
23132                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23133                 break;
23134             case 'pie':
23135 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23136 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23137 //            
23138                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23139                 
23140                 break;
23141
23142         }
23143         
23144         if(this.title){
23145             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23146         }
23147         
23148     },
23149     
23150     setTitle: function(o)
23151     {
23152         this.title = o;
23153     },
23154     
23155     initEvents: function() {
23156         
23157         if(!this.href){
23158             this.el.on('click', this.onClick, this);
23159         }
23160     },
23161     
23162     onClick : function(e)
23163     {
23164         Roo.log('img onclick');
23165         this.fireEvent('click', this, e);
23166     }
23167    
23168 });
23169
23170  
23171 /*
23172  * - LGPL
23173  *
23174  * numberBox
23175  * 
23176  */
23177 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23178
23179 /**
23180  * @class Roo.bootstrap.dash.NumberBox
23181  * @extends Roo.bootstrap.Component
23182  * Bootstrap NumberBox class
23183  * @cfg {String} headline Box headline
23184  * @cfg {String} content Box content
23185  * @cfg {String} icon Box icon
23186  * @cfg {String} footer Footer text
23187  * @cfg {String} fhref Footer href
23188  * 
23189  * @constructor
23190  * Create a new NumberBox
23191  * @param {Object} config The config object
23192  */
23193
23194
23195 Roo.bootstrap.dash.NumberBox = function(config){
23196     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23197     
23198 };
23199
23200 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23201     
23202     headline : '',
23203     content : '',
23204     icon : '',
23205     footer : '',
23206     fhref : '',
23207     ficon : '',
23208     
23209     getAutoCreate : function(){
23210         
23211         var cfg = {
23212             tag : 'div',
23213             cls : 'small-box ',
23214             cn : [
23215                 {
23216                     tag : 'div',
23217                     cls : 'inner',
23218                     cn :[
23219                         {
23220                             tag : 'h3',
23221                             cls : 'roo-headline',
23222                             html : this.headline
23223                         },
23224                         {
23225                             tag : 'p',
23226                             cls : 'roo-content',
23227                             html : this.content
23228                         }
23229                     ]
23230                 }
23231             ]
23232         };
23233         
23234         if(this.icon){
23235             cfg.cn.push({
23236                 tag : 'div',
23237                 cls : 'icon',
23238                 cn :[
23239                     {
23240                         tag : 'i',
23241                         cls : 'ion ' + this.icon
23242                     }
23243                 ]
23244             });
23245         }
23246         
23247         if(this.footer){
23248             var footer = {
23249                 tag : 'a',
23250                 cls : 'small-box-footer',
23251                 href : this.fhref || '#',
23252                 html : this.footer
23253             };
23254             
23255             cfg.cn.push(footer);
23256             
23257         }
23258         
23259         return  cfg;
23260     },
23261
23262     onRender : function(ct,position){
23263         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23264
23265
23266        
23267                 
23268     },
23269
23270     setHeadline: function (value)
23271     {
23272         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23273     },
23274     
23275     setFooter: function (value, href)
23276     {
23277         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23278         
23279         if(href){
23280             this.el.select('a.small-box-footer',true).first().attr('href', href);
23281         }
23282         
23283     },
23284
23285     setContent: function (value)
23286     {
23287         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23288     },
23289
23290     initEvents: function() 
23291     {   
23292         
23293     }
23294     
23295 });
23296
23297  
23298 /*
23299  * - LGPL
23300  *
23301  * TabBox
23302  * 
23303  */
23304 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23305
23306 /**
23307  * @class Roo.bootstrap.dash.TabBox
23308  * @extends Roo.bootstrap.Component
23309  * Bootstrap TabBox class
23310  * @cfg {String} title Title of the TabBox
23311  * @cfg {String} icon Icon of the TabBox
23312  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23313  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23314  * 
23315  * @constructor
23316  * Create a new TabBox
23317  * @param {Object} config The config object
23318  */
23319
23320
23321 Roo.bootstrap.dash.TabBox = function(config){
23322     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23323     this.addEvents({
23324         // raw events
23325         /**
23326          * @event addpane
23327          * When a pane is added
23328          * @param {Roo.bootstrap.dash.TabPane} pane
23329          */
23330         "addpane" : true,
23331         /**
23332          * @event activatepane
23333          * When a pane is activated
23334          * @param {Roo.bootstrap.dash.TabPane} pane
23335          */
23336         "activatepane" : true
23337         
23338          
23339     });
23340     
23341     this.panes = [];
23342 };
23343
23344 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23345
23346     title : '',
23347     icon : false,
23348     showtabs : true,
23349     tabScrollable : false,
23350     
23351     getChildContainer : function()
23352     {
23353         return this.el.select('.tab-content', true).first();
23354     },
23355     
23356     getAutoCreate : function(){
23357         
23358         var header = {
23359             tag: 'li',
23360             cls: 'pull-left header',
23361             html: this.title,
23362             cn : []
23363         };
23364         
23365         if(this.icon){
23366             header.cn.push({
23367                 tag: 'i',
23368                 cls: 'fa ' + this.icon
23369             });
23370         }
23371         
23372         var h = {
23373             tag: 'ul',
23374             cls: 'nav nav-tabs pull-right',
23375             cn: [
23376                 header
23377             ]
23378         };
23379         
23380         if(this.tabScrollable){
23381             h = {
23382                 tag: 'div',
23383                 cls: 'tab-header',
23384                 cn: [
23385                     {
23386                         tag: 'ul',
23387                         cls: 'nav nav-tabs pull-right',
23388                         cn: [
23389                             header
23390                         ]
23391                     }
23392                 ]
23393             };
23394         }
23395         
23396         var cfg = {
23397             tag: 'div',
23398             cls: 'nav-tabs-custom',
23399             cn: [
23400                 h,
23401                 {
23402                     tag: 'div',
23403                     cls: 'tab-content no-padding',
23404                     cn: []
23405                 }
23406             ]
23407         };
23408
23409         return  cfg;
23410     },
23411     initEvents : function()
23412     {
23413         //Roo.log('add add pane handler');
23414         this.on('addpane', this.onAddPane, this);
23415     },
23416      /**
23417      * Updates the box title
23418      * @param {String} html to set the title to.
23419      */
23420     setTitle : function(value)
23421     {
23422         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23423     },
23424     onAddPane : function(pane)
23425     {
23426         this.panes.push(pane);
23427         //Roo.log('addpane');
23428         //Roo.log(pane);
23429         // tabs are rendere left to right..
23430         if(!this.showtabs){
23431             return;
23432         }
23433         
23434         var ctr = this.el.select('.nav-tabs', true).first();
23435          
23436          
23437         var existing = ctr.select('.nav-tab',true);
23438         var qty = existing.getCount();;
23439         
23440         
23441         var tab = ctr.createChild({
23442             tag : 'li',
23443             cls : 'nav-tab' + (qty ? '' : ' active'),
23444             cn : [
23445                 {
23446                     tag : 'a',
23447                     href:'#',
23448                     html : pane.title
23449                 }
23450             ]
23451         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23452         pane.tab = tab;
23453         
23454         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23455         if (!qty) {
23456             pane.el.addClass('active');
23457         }
23458         
23459                 
23460     },
23461     onTabClick : function(ev,un,ob,pane)
23462     {
23463         //Roo.log('tab - prev default');
23464         ev.preventDefault();
23465         
23466         
23467         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23468         pane.tab.addClass('active');
23469         //Roo.log(pane.title);
23470         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23471         // technically we should have a deactivate event.. but maybe add later.
23472         // and it should not de-activate the selected tab...
23473         this.fireEvent('activatepane', pane);
23474         pane.el.addClass('active');
23475         pane.fireEvent('activate');
23476         
23477         
23478     },
23479     
23480     getActivePane : function()
23481     {
23482         var r = false;
23483         Roo.each(this.panes, function(p) {
23484             if(p.el.hasClass('active')){
23485                 r = p;
23486                 return false;
23487             }
23488             
23489             return;
23490         });
23491         
23492         return r;
23493     }
23494     
23495     
23496 });
23497
23498  
23499 /*
23500  * - LGPL
23501  *
23502  * Tab pane
23503  * 
23504  */
23505 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23506 /**
23507  * @class Roo.bootstrap.TabPane
23508  * @extends Roo.bootstrap.Component
23509  * Bootstrap TabPane class
23510  * @cfg {Boolean} active (false | true) Default false
23511  * @cfg {String} title title of panel
23512
23513  * 
23514  * @constructor
23515  * Create a new TabPane
23516  * @param {Object} config The config object
23517  */
23518
23519 Roo.bootstrap.dash.TabPane = function(config){
23520     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23521     
23522     this.addEvents({
23523         // raw events
23524         /**
23525          * @event activate
23526          * When a pane is activated
23527          * @param {Roo.bootstrap.dash.TabPane} pane
23528          */
23529         "activate" : true
23530          
23531     });
23532 };
23533
23534 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23535     
23536     active : false,
23537     title : '',
23538     
23539     // the tabBox that this is attached to.
23540     tab : false,
23541      
23542     getAutoCreate : function() 
23543     {
23544         var cfg = {
23545             tag: 'div',
23546             cls: 'tab-pane'
23547         };
23548         
23549         if(this.active){
23550             cfg.cls += ' active';
23551         }
23552         
23553         return cfg;
23554     },
23555     initEvents  : function()
23556     {
23557         //Roo.log('trigger add pane handler');
23558         this.parent().fireEvent('addpane', this)
23559     },
23560     
23561      /**
23562      * Updates the tab title 
23563      * @param {String} html to set the title to.
23564      */
23565     setTitle: function(str)
23566     {
23567         if (!this.tab) {
23568             return;
23569         }
23570         this.title = str;
23571         this.tab.select('a', true).first().dom.innerHTML = str;
23572         
23573     }
23574     
23575     
23576     
23577 });
23578
23579  
23580
23581
23582  /*
23583  * - LGPL
23584  *
23585  * menu
23586  * 
23587  */
23588 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23589
23590 /**
23591  * @class Roo.bootstrap.menu.Menu
23592  * @extends Roo.bootstrap.Component
23593  * Bootstrap Menu class - container for Menu
23594  * @cfg {String} html Text of the menu
23595  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23596  * @cfg {String} icon Font awesome icon
23597  * @cfg {String} pos Menu align to (top | bottom) default bottom
23598  * 
23599  * 
23600  * @constructor
23601  * Create a new Menu
23602  * @param {Object} config The config object
23603  */
23604
23605
23606 Roo.bootstrap.menu.Menu = function(config){
23607     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23608     
23609     this.addEvents({
23610         /**
23611          * @event beforeshow
23612          * Fires before this menu is displayed
23613          * @param {Roo.bootstrap.menu.Menu} this
23614          */
23615         beforeshow : true,
23616         /**
23617          * @event beforehide
23618          * Fires before this menu is hidden
23619          * @param {Roo.bootstrap.menu.Menu} this
23620          */
23621         beforehide : true,
23622         /**
23623          * @event show
23624          * Fires after this menu is displayed
23625          * @param {Roo.bootstrap.menu.Menu} this
23626          */
23627         show : true,
23628         /**
23629          * @event hide
23630          * Fires after this menu is hidden
23631          * @param {Roo.bootstrap.menu.Menu} this
23632          */
23633         hide : true,
23634         /**
23635          * @event click
23636          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23637          * @param {Roo.bootstrap.menu.Menu} this
23638          * @param {Roo.EventObject} e
23639          */
23640         click : true
23641     });
23642     
23643 };
23644
23645 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23646     
23647     submenu : false,
23648     html : '',
23649     weight : 'default',
23650     icon : false,
23651     pos : 'bottom',
23652     
23653     
23654     getChildContainer : function() {
23655         if(this.isSubMenu){
23656             return this.el;
23657         }
23658         
23659         return this.el.select('ul.dropdown-menu', true).first();  
23660     },
23661     
23662     getAutoCreate : function()
23663     {
23664         var text = [
23665             {
23666                 tag : 'span',
23667                 cls : 'roo-menu-text',
23668                 html : this.html
23669             }
23670         ];
23671         
23672         if(this.icon){
23673             text.unshift({
23674                 tag : 'i',
23675                 cls : 'fa ' + this.icon
23676             })
23677         }
23678         
23679         
23680         var cfg = {
23681             tag : 'div',
23682             cls : 'btn-group',
23683             cn : [
23684                 {
23685                     tag : 'button',
23686                     cls : 'dropdown-button btn btn-' + this.weight,
23687                     cn : text
23688                 },
23689                 {
23690                     tag : 'button',
23691                     cls : 'dropdown-toggle btn btn-' + this.weight,
23692                     cn : [
23693                         {
23694                             tag : 'span',
23695                             cls : 'caret'
23696                         }
23697                     ]
23698                 },
23699                 {
23700                     tag : 'ul',
23701                     cls : 'dropdown-menu'
23702                 }
23703             ]
23704             
23705         };
23706         
23707         if(this.pos == 'top'){
23708             cfg.cls += ' dropup';
23709         }
23710         
23711         if(this.isSubMenu){
23712             cfg = {
23713                 tag : 'ul',
23714                 cls : 'dropdown-menu'
23715             }
23716         }
23717         
23718         return cfg;
23719     },
23720     
23721     onRender : function(ct, position)
23722     {
23723         this.isSubMenu = ct.hasClass('dropdown-submenu');
23724         
23725         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23726     },
23727     
23728     initEvents : function() 
23729     {
23730         if(this.isSubMenu){
23731             return;
23732         }
23733         
23734         this.hidden = true;
23735         
23736         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23737         this.triggerEl.on('click', this.onTriggerPress, this);
23738         
23739         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23740         this.buttonEl.on('click', this.onClick, this);
23741         
23742     },
23743     
23744     list : function()
23745     {
23746         if(this.isSubMenu){
23747             return this.el;
23748         }
23749         
23750         return this.el.select('ul.dropdown-menu', true).first();
23751     },
23752     
23753     onClick : function(e)
23754     {
23755         this.fireEvent("click", this, e);
23756     },
23757     
23758     onTriggerPress  : function(e)
23759     {   
23760         if (this.isVisible()) {
23761             this.hide();
23762         } else {
23763             this.show();
23764         }
23765     },
23766     
23767     isVisible : function(){
23768         return !this.hidden;
23769     },
23770     
23771     show : function()
23772     {
23773         this.fireEvent("beforeshow", this);
23774         
23775         this.hidden = false;
23776         this.el.addClass('open');
23777         
23778         Roo.get(document).on("mouseup", this.onMouseUp, this);
23779         
23780         this.fireEvent("show", this);
23781         
23782         
23783     },
23784     
23785     hide : function()
23786     {
23787         this.fireEvent("beforehide", this);
23788         
23789         this.hidden = true;
23790         this.el.removeClass('open');
23791         
23792         Roo.get(document).un("mouseup", this.onMouseUp);
23793         
23794         this.fireEvent("hide", this);
23795     },
23796     
23797     onMouseUp : function()
23798     {
23799         this.hide();
23800     }
23801     
23802 });
23803
23804  
23805  /*
23806  * - LGPL
23807  *
23808  * menu item
23809  * 
23810  */
23811 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23812
23813 /**
23814  * @class Roo.bootstrap.menu.Item
23815  * @extends Roo.bootstrap.Component
23816  * Bootstrap MenuItem class
23817  * @cfg {Boolean} submenu (true | false) default false
23818  * @cfg {String} html text of the item
23819  * @cfg {String} href the link
23820  * @cfg {Boolean} disable (true | false) default false
23821  * @cfg {Boolean} preventDefault (true | false) default true
23822  * @cfg {String} icon Font awesome icon
23823  * @cfg {String} pos Submenu align to (left | right) default right 
23824  * 
23825  * 
23826  * @constructor
23827  * Create a new Item
23828  * @param {Object} config The config object
23829  */
23830
23831
23832 Roo.bootstrap.menu.Item = function(config){
23833     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23834     this.addEvents({
23835         /**
23836          * @event mouseover
23837          * Fires when the mouse is hovering over this menu
23838          * @param {Roo.bootstrap.menu.Item} this
23839          * @param {Roo.EventObject} e
23840          */
23841         mouseover : true,
23842         /**
23843          * @event mouseout
23844          * Fires when the mouse exits this menu
23845          * @param {Roo.bootstrap.menu.Item} this
23846          * @param {Roo.EventObject} e
23847          */
23848         mouseout : true,
23849         // raw events
23850         /**
23851          * @event click
23852          * The raw click event for the entire grid.
23853          * @param {Roo.EventObject} e
23854          */
23855         click : true
23856     });
23857 };
23858
23859 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23860     
23861     submenu : false,
23862     href : '',
23863     html : '',
23864     preventDefault: true,
23865     disable : false,
23866     icon : false,
23867     pos : 'right',
23868     
23869     getAutoCreate : function()
23870     {
23871         var text = [
23872             {
23873                 tag : 'span',
23874                 cls : 'roo-menu-item-text',
23875                 html : this.html
23876             }
23877         ];
23878         
23879         if(this.icon){
23880             text.unshift({
23881                 tag : 'i',
23882                 cls : 'fa ' + this.icon
23883             })
23884         }
23885         
23886         var cfg = {
23887             tag : 'li',
23888             cn : [
23889                 {
23890                     tag : 'a',
23891                     href : this.href || '#',
23892                     cn : text
23893                 }
23894             ]
23895         };
23896         
23897         if(this.disable){
23898             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23899         }
23900         
23901         if(this.submenu){
23902             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23903             
23904             if(this.pos == 'left'){
23905                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23906             }
23907         }
23908         
23909         return cfg;
23910     },
23911     
23912     initEvents : function() 
23913     {
23914         this.el.on('mouseover', this.onMouseOver, this);
23915         this.el.on('mouseout', this.onMouseOut, this);
23916         
23917         this.el.select('a', true).first().on('click', this.onClick, this);
23918         
23919     },
23920     
23921     onClick : function(e)
23922     {
23923         if(this.preventDefault){
23924             e.preventDefault();
23925         }
23926         
23927         this.fireEvent("click", this, e);
23928     },
23929     
23930     onMouseOver : function(e)
23931     {
23932         if(this.submenu && this.pos == 'left'){
23933             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23934         }
23935         
23936         this.fireEvent("mouseover", this, e);
23937     },
23938     
23939     onMouseOut : function(e)
23940     {
23941         this.fireEvent("mouseout", this, e);
23942     }
23943 });
23944
23945  
23946
23947  /*
23948  * - LGPL
23949  *
23950  * menu separator
23951  * 
23952  */
23953 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23954
23955 /**
23956  * @class Roo.bootstrap.menu.Separator
23957  * @extends Roo.bootstrap.Component
23958  * Bootstrap Separator class
23959  * 
23960  * @constructor
23961  * Create a new Separator
23962  * @param {Object} config The config object
23963  */
23964
23965
23966 Roo.bootstrap.menu.Separator = function(config){
23967     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23968 };
23969
23970 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23971     
23972     getAutoCreate : function(){
23973         var cfg = {
23974             tag : 'li',
23975             cls: 'divider'
23976         };
23977         
23978         return cfg;
23979     }
23980    
23981 });
23982
23983  
23984
23985  /*
23986  * - LGPL
23987  *
23988  * Tooltip
23989  * 
23990  */
23991
23992 /**
23993  * @class Roo.bootstrap.Tooltip
23994  * Bootstrap Tooltip class
23995  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23996  * to determine which dom element triggers the tooltip.
23997  * 
23998  * It needs to add support for additional attributes like tooltip-position
23999  * 
24000  * @constructor
24001  * Create a new Toolti
24002  * @param {Object} config The config object
24003  */
24004
24005 Roo.bootstrap.Tooltip = function(config){
24006     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24007 };
24008
24009 Roo.apply(Roo.bootstrap.Tooltip, {
24010     /**
24011      * @function init initialize tooltip monitoring.
24012      * @static
24013      */
24014     currentEl : false,
24015     currentTip : false,
24016     currentRegion : false,
24017     
24018     //  init : delay?
24019     
24020     init : function()
24021     {
24022         Roo.get(document).on('mouseover', this.enter ,this);
24023         Roo.get(document).on('mouseout', this.leave, this);
24024          
24025         
24026         this.currentTip = new Roo.bootstrap.Tooltip();
24027     },
24028     
24029     enter : function(ev)
24030     {
24031         var dom = ev.getTarget();
24032         
24033         //Roo.log(['enter',dom]);
24034         var el = Roo.fly(dom);
24035         if (this.currentEl) {
24036             //Roo.log(dom);
24037             //Roo.log(this.currentEl);
24038             //Roo.log(this.currentEl.contains(dom));
24039             if (this.currentEl == el) {
24040                 return;
24041             }
24042             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24043                 return;
24044             }
24045
24046         }
24047         
24048         if (this.currentTip.el) {
24049             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24050         }    
24051         //Roo.log(ev);
24052         var bindEl = el;
24053         
24054         // you can not look for children, as if el is the body.. then everythign is the child..
24055         if (!el.attr('tooltip')) { //
24056             if (!el.select("[tooltip]").elements.length) {
24057                 return;
24058             }
24059             // is the mouse over this child...?
24060             bindEl = el.select("[tooltip]").first();
24061             var xy = ev.getXY();
24062             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24063                 //Roo.log("not in region.");
24064                 return;
24065             }
24066             //Roo.log("child element over..");
24067             
24068         }
24069         this.currentEl = bindEl;
24070         this.currentTip.bind(bindEl);
24071         this.currentRegion = Roo.lib.Region.getRegion(dom);
24072         this.currentTip.enter();
24073         
24074     },
24075     leave : function(ev)
24076     {
24077         var dom = ev.getTarget();
24078         //Roo.log(['leave',dom]);
24079         if (!this.currentEl) {
24080             return;
24081         }
24082         
24083         
24084         if (dom != this.currentEl.dom) {
24085             return;
24086         }
24087         var xy = ev.getXY();
24088         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24089             return;
24090         }
24091         // only activate leave if mouse cursor is outside... bounding box..
24092         
24093         
24094         
24095         
24096         if (this.currentTip) {
24097             this.currentTip.leave();
24098         }
24099         //Roo.log('clear currentEl');
24100         this.currentEl = false;
24101         
24102         
24103     },
24104     alignment : {
24105         'left' : ['r-l', [-2,0], 'right'],
24106         'right' : ['l-r', [2,0], 'left'],
24107         'bottom' : ['t-b', [0,2], 'top'],
24108         'top' : [ 'b-t', [0,-2], 'bottom']
24109     }
24110     
24111 });
24112
24113
24114 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24115     
24116     
24117     bindEl : false,
24118     
24119     delay : null, // can be { show : 300 , hide: 500}
24120     
24121     timeout : null,
24122     
24123     hoverState : null, //???
24124     
24125     placement : 'bottom', 
24126     
24127     getAutoCreate : function(){
24128     
24129         var cfg = {
24130            cls : 'tooltip',
24131            role : 'tooltip',
24132            cn : [
24133                 {
24134                     cls : 'tooltip-arrow'
24135                 },
24136                 {
24137                     cls : 'tooltip-inner'
24138                 }
24139            ]
24140         };
24141         
24142         return cfg;
24143     },
24144     bind : function(el)
24145     {
24146         this.bindEl = el;
24147     },
24148       
24149     
24150     enter : function () {
24151        
24152         if (this.timeout != null) {
24153             clearTimeout(this.timeout);
24154         }
24155         
24156         this.hoverState = 'in';
24157          //Roo.log("enter - show");
24158         if (!this.delay || !this.delay.show) {
24159             this.show();
24160             return;
24161         }
24162         var _t = this;
24163         this.timeout = setTimeout(function () {
24164             if (_t.hoverState == 'in') {
24165                 _t.show();
24166             }
24167         }, this.delay.show);
24168     },
24169     leave : function()
24170     {
24171         clearTimeout(this.timeout);
24172     
24173         this.hoverState = 'out';
24174          if (!this.delay || !this.delay.hide) {
24175             this.hide();
24176             return;
24177         }
24178        
24179         var _t = this;
24180         this.timeout = setTimeout(function () {
24181             //Roo.log("leave - timeout");
24182             
24183             if (_t.hoverState == 'out') {
24184                 _t.hide();
24185                 Roo.bootstrap.Tooltip.currentEl = false;
24186             }
24187         }, delay);
24188     },
24189     
24190     show : function ()
24191     {
24192         if (!this.el) {
24193             this.render(document.body);
24194         }
24195         // set content.
24196         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24197         
24198         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24199         
24200         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24201         
24202         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24203         
24204         var placement = typeof this.placement == 'function' ?
24205             this.placement.call(this, this.el, on_el) :
24206             this.placement;
24207             
24208         var autoToken = /\s?auto?\s?/i;
24209         var autoPlace = autoToken.test(placement);
24210         if (autoPlace) {
24211             placement = placement.replace(autoToken, '') || 'top';
24212         }
24213         
24214         //this.el.detach()
24215         //this.el.setXY([0,0]);
24216         this.el.show();
24217         //this.el.dom.style.display='block';
24218         
24219         //this.el.appendTo(on_el);
24220         
24221         var p = this.getPosition();
24222         var box = this.el.getBox();
24223         
24224         if (autoPlace) {
24225             // fixme..
24226         }
24227         
24228         var align = Roo.bootstrap.Tooltip.alignment[placement];
24229         
24230         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24231         
24232         if(placement == 'top' || placement == 'bottom'){
24233             if(xy[0] < 0){
24234                 placement = 'right';
24235             }
24236             
24237             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24238                 placement = 'left';
24239             }
24240         }
24241         
24242         align = Roo.bootstrap.Tooltip.alignment[placement];
24243         
24244         this.el.alignTo(this.bindEl, align[0],align[1]);
24245         //var arrow = this.el.select('.arrow',true).first();
24246         //arrow.set(align[2], 
24247         
24248         this.el.addClass(placement);
24249         
24250         this.el.addClass('in fade');
24251         
24252         this.hoverState = null;
24253         
24254         if (this.el.hasClass('fade')) {
24255             // fade it?
24256         }
24257         
24258     },
24259     hide : function()
24260     {
24261          
24262         if (!this.el) {
24263             return;
24264         }
24265         //this.el.setXY([0,0]);
24266         this.el.removeClass('in');
24267         //this.el.hide();
24268         
24269     }
24270     
24271 });
24272  
24273
24274  /*
24275  * - LGPL
24276  *
24277  * Location Picker
24278  * 
24279  */
24280
24281 /**
24282  * @class Roo.bootstrap.LocationPicker
24283  * @extends Roo.bootstrap.Component
24284  * Bootstrap LocationPicker class
24285  * @cfg {Number} latitude Position when init default 0
24286  * @cfg {Number} longitude Position when init default 0
24287  * @cfg {Number} zoom default 15
24288  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24289  * @cfg {Boolean} mapTypeControl default false
24290  * @cfg {Boolean} disableDoubleClickZoom default false
24291  * @cfg {Boolean} scrollwheel default true
24292  * @cfg {Boolean} streetViewControl default false
24293  * @cfg {Number} radius default 0
24294  * @cfg {String} locationName
24295  * @cfg {Boolean} draggable default true
24296  * @cfg {Boolean} enableAutocomplete default false
24297  * @cfg {Boolean} enableReverseGeocode default true
24298  * @cfg {String} markerTitle
24299  * 
24300  * @constructor
24301  * Create a new LocationPicker
24302  * @param {Object} config The config object
24303  */
24304
24305
24306 Roo.bootstrap.LocationPicker = function(config){
24307     
24308     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24309     
24310     this.addEvents({
24311         /**
24312          * @event initial
24313          * Fires when the picker initialized.
24314          * @param {Roo.bootstrap.LocationPicker} this
24315          * @param {Google Location} location
24316          */
24317         initial : true,
24318         /**
24319          * @event positionchanged
24320          * Fires when the picker position changed.
24321          * @param {Roo.bootstrap.LocationPicker} this
24322          * @param {Google Location} location
24323          */
24324         positionchanged : true,
24325         /**
24326          * @event resize
24327          * Fires when the map resize.
24328          * @param {Roo.bootstrap.LocationPicker} this
24329          */
24330         resize : true,
24331         /**
24332          * @event show
24333          * Fires when the map show.
24334          * @param {Roo.bootstrap.LocationPicker} this
24335          */
24336         show : true,
24337         /**
24338          * @event hide
24339          * Fires when the map hide.
24340          * @param {Roo.bootstrap.LocationPicker} this
24341          */
24342         hide : true,
24343         /**
24344          * @event mapClick
24345          * Fires when click the map.
24346          * @param {Roo.bootstrap.LocationPicker} this
24347          * @param {Map event} e
24348          */
24349         mapClick : true,
24350         /**
24351          * @event mapRightClick
24352          * Fires when right click the map.
24353          * @param {Roo.bootstrap.LocationPicker} this
24354          * @param {Map event} e
24355          */
24356         mapRightClick : true,
24357         /**
24358          * @event markerClick
24359          * Fires when click the marker.
24360          * @param {Roo.bootstrap.LocationPicker} this
24361          * @param {Map event} e
24362          */
24363         markerClick : true,
24364         /**
24365          * @event markerRightClick
24366          * Fires when right click the marker.
24367          * @param {Roo.bootstrap.LocationPicker} this
24368          * @param {Map event} e
24369          */
24370         markerRightClick : true,
24371         /**
24372          * @event OverlayViewDraw
24373          * Fires when OverlayView Draw
24374          * @param {Roo.bootstrap.LocationPicker} this
24375          */
24376         OverlayViewDraw : true,
24377         /**
24378          * @event OverlayViewOnAdd
24379          * Fires when OverlayView Draw
24380          * @param {Roo.bootstrap.LocationPicker} this
24381          */
24382         OverlayViewOnAdd : true,
24383         /**
24384          * @event OverlayViewOnRemove
24385          * Fires when OverlayView Draw
24386          * @param {Roo.bootstrap.LocationPicker} this
24387          */
24388         OverlayViewOnRemove : true,
24389         /**
24390          * @event OverlayViewShow
24391          * Fires when OverlayView Draw
24392          * @param {Roo.bootstrap.LocationPicker} this
24393          * @param {Pixel} cpx
24394          */
24395         OverlayViewShow : true,
24396         /**
24397          * @event OverlayViewHide
24398          * Fires when OverlayView Draw
24399          * @param {Roo.bootstrap.LocationPicker} this
24400          */
24401         OverlayViewHide : true,
24402         /**
24403          * @event loadexception
24404          * Fires when load google lib failed.
24405          * @param {Roo.bootstrap.LocationPicker} this
24406          */
24407         loadexception : true
24408     });
24409         
24410 };
24411
24412 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24413     
24414     gMapContext: false,
24415     
24416     latitude: 0,
24417     longitude: 0,
24418     zoom: 15,
24419     mapTypeId: false,
24420     mapTypeControl: false,
24421     disableDoubleClickZoom: false,
24422     scrollwheel: true,
24423     streetViewControl: false,
24424     radius: 0,
24425     locationName: '',
24426     draggable: true,
24427     enableAutocomplete: false,
24428     enableReverseGeocode: true,
24429     markerTitle: '',
24430     
24431     getAutoCreate: function()
24432     {
24433
24434         var cfg = {
24435             tag: 'div',
24436             cls: 'roo-location-picker'
24437         };
24438         
24439         return cfg
24440     },
24441     
24442     initEvents: function(ct, position)
24443     {       
24444         if(!this.el.getWidth() || this.isApplied()){
24445             return;
24446         }
24447         
24448         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24449         
24450         this.initial();
24451     },
24452     
24453     initial: function()
24454     {
24455         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24456             this.fireEvent('loadexception', this);
24457             return;
24458         }
24459         
24460         if(!this.mapTypeId){
24461             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24462         }
24463         
24464         this.gMapContext = this.GMapContext();
24465         
24466         this.initOverlayView();
24467         
24468         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24469         
24470         var _this = this;
24471                 
24472         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24473             _this.setPosition(_this.gMapContext.marker.position);
24474         });
24475         
24476         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24477             _this.fireEvent('mapClick', this, event);
24478             
24479         });
24480
24481         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24482             _this.fireEvent('mapRightClick', this, event);
24483             
24484         });
24485         
24486         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24487             _this.fireEvent('markerClick', this, event);
24488             
24489         });
24490
24491         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24492             _this.fireEvent('markerRightClick', this, event);
24493             
24494         });
24495         
24496         this.setPosition(this.gMapContext.location);
24497         
24498         this.fireEvent('initial', this, this.gMapContext.location);
24499     },
24500     
24501     initOverlayView: function()
24502     {
24503         var _this = this;
24504         
24505         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24506             
24507             draw: function()
24508             {
24509                 _this.fireEvent('OverlayViewDraw', _this);
24510             },
24511             
24512             onAdd: function()
24513             {
24514                 _this.fireEvent('OverlayViewOnAdd', _this);
24515             },
24516             
24517             onRemove: function()
24518             {
24519                 _this.fireEvent('OverlayViewOnRemove', _this);
24520             },
24521             
24522             show: function(cpx)
24523             {
24524                 _this.fireEvent('OverlayViewShow', _this, cpx);
24525             },
24526             
24527             hide: function()
24528             {
24529                 _this.fireEvent('OverlayViewHide', _this);
24530             }
24531             
24532         });
24533     },
24534     
24535     fromLatLngToContainerPixel: function(event)
24536     {
24537         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24538     },
24539     
24540     isApplied: function() 
24541     {
24542         return this.getGmapContext() == false ? false : true;
24543     },
24544     
24545     getGmapContext: function() 
24546     {
24547         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24548     },
24549     
24550     GMapContext: function() 
24551     {
24552         var position = new google.maps.LatLng(this.latitude, this.longitude);
24553         
24554         var _map = new google.maps.Map(this.el.dom, {
24555             center: position,
24556             zoom: this.zoom,
24557             mapTypeId: this.mapTypeId,
24558             mapTypeControl: this.mapTypeControl,
24559             disableDoubleClickZoom: this.disableDoubleClickZoom,
24560             scrollwheel: this.scrollwheel,
24561             streetViewControl: this.streetViewControl,
24562             locationName: this.locationName,
24563             draggable: this.draggable,
24564             enableAutocomplete: this.enableAutocomplete,
24565             enableReverseGeocode: this.enableReverseGeocode
24566         });
24567         
24568         var _marker = new google.maps.Marker({
24569             position: position,
24570             map: _map,
24571             title: this.markerTitle,
24572             draggable: this.draggable
24573         });
24574         
24575         return {
24576             map: _map,
24577             marker: _marker,
24578             circle: null,
24579             location: position,
24580             radius: this.radius,
24581             locationName: this.locationName,
24582             addressComponents: {
24583                 formatted_address: null,
24584                 addressLine1: null,
24585                 addressLine2: null,
24586                 streetName: null,
24587                 streetNumber: null,
24588                 city: null,
24589                 district: null,
24590                 state: null,
24591                 stateOrProvince: null
24592             },
24593             settings: this,
24594             domContainer: this.el.dom,
24595             geodecoder: new google.maps.Geocoder()
24596         };
24597     },
24598     
24599     drawCircle: function(center, radius, options) 
24600     {
24601         if (this.gMapContext.circle != null) {
24602             this.gMapContext.circle.setMap(null);
24603         }
24604         if (radius > 0) {
24605             radius *= 1;
24606             options = Roo.apply({}, options, {
24607                 strokeColor: "#0000FF",
24608                 strokeOpacity: .35,
24609                 strokeWeight: 2,
24610                 fillColor: "#0000FF",
24611                 fillOpacity: .2
24612             });
24613             
24614             options.map = this.gMapContext.map;
24615             options.radius = radius;
24616             options.center = center;
24617             this.gMapContext.circle = new google.maps.Circle(options);
24618             return this.gMapContext.circle;
24619         }
24620         
24621         return null;
24622     },
24623     
24624     setPosition: function(location) 
24625     {
24626         this.gMapContext.location = location;
24627         this.gMapContext.marker.setPosition(location);
24628         this.gMapContext.map.panTo(location);
24629         this.drawCircle(location, this.gMapContext.radius, {});
24630         
24631         var _this = this;
24632         
24633         if (this.gMapContext.settings.enableReverseGeocode) {
24634             this.gMapContext.geodecoder.geocode({
24635                 latLng: this.gMapContext.location
24636             }, function(results, status) {
24637                 
24638                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24639                     _this.gMapContext.locationName = results[0].formatted_address;
24640                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24641                     
24642                     _this.fireEvent('positionchanged', this, location);
24643                 }
24644             });
24645             
24646             return;
24647         }
24648         
24649         this.fireEvent('positionchanged', this, location);
24650     },
24651     
24652     resize: function()
24653     {
24654         google.maps.event.trigger(this.gMapContext.map, "resize");
24655         
24656         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24657         
24658         this.fireEvent('resize', this);
24659     },
24660     
24661     setPositionByLatLng: function(latitude, longitude)
24662     {
24663         this.setPosition(new google.maps.LatLng(latitude, longitude));
24664     },
24665     
24666     getCurrentPosition: function() 
24667     {
24668         return {
24669             latitude: this.gMapContext.location.lat(),
24670             longitude: this.gMapContext.location.lng()
24671         };
24672     },
24673     
24674     getAddressName: function() 
24675     {
24676         return this.gMapContext.locationName;
24677     },
24678     
24679     getAddressComponents: function() 
24680     {
24681         return this.gMapContext.addressComponents;
24682     },
24683     
24684     address_component_from_google_geocode: function(address_components) 
24685     {
24686         var result = {};
24687         
24688         for (var i = 0; i < address_components.length; i++) {
24689             var component = address_components[i];
24690             if (component.types.indexOf("postal_code") >= 0) {
24691                 result.postalCode = component.short_name;
24692             } else if (component.types.indexOf("street_number") >= 0) {
24693                 result.streetNumber = component.short_name;
24694             } else if (component.types.indexOf("route") >= 0) {
24695                 result.streetName = component.short_name;
24696             } else if (component.types.indexOf("neighborhood") >= 0) {
24697                 result.city = component.short_name;
24698             } else if (component.types.indexOf("locality") >= 0) {
24699                 result.city = component.short_name;
24700             } else if (component.types.indexOf("sublocality") >= 0) {
24701                 result.district = component.short_name;
24702             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24703                 result.stateOrProvince = component.short_name;
24704             } else if (component.types.indexOf("country") >= 0) {
24705                 result.country = component.short_name;
24706             }
24707         }
24708         
24709         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24710         result.addressLine2 = "";
24711         return result;
24712     },
24713     
24714     setZoomLevel: function(zoom)
24715     {
24716         this.gMapContext.map.setZoom(zoom);
24717     },
24718     
24719     show: function()
24720     {
24721         if(!this.el){
24722             return;
24723         }
24724         
24725         this.el.show();
24726         
24727         this.resize();
24728         
24729         this.fireEvent('show', this);
24730     },
24731     
24732     hide: function()
24733     {
24734         if(!this.el){
24735             return;
24736         }
24737         
24738         this.el.hide();
24739         
24740         this.fireEvent('hide', this);
24741     }
24742     
24743 });
24744
24745 Roo.apply(Roo.bootstrap.LocationPicker, {
24746     
24747     OverlayView : function(map, options)
24748     {
24749         options = options || {};
24750         
24751         this.setMap(map);
24752     }
24753     
24754     
24755 });/*
24756  * - LGPL
24757  *
24758  * Alert
24759  * 
24760  */
24761
24762 /**
24763  * @class Roo.bootstrap.Alert
24764  * @extends Roo.bootstrap.Component
24765  * Bootstrap Alert class
24766  * @cfg {String} title The title of alert
24767  * @cfg {String} html The content of alert
24768  * @cfg {String} weight (  success | info | warning | danger )
24769  * @cfg {String} faicon font-awesomeicon
24770  * 
24771  * @constructor
24772  * Create a new alert
24773  * @param {Object} config The config object
24774  */
24775
24776
24777 Roo.bootstrap.Alert = function(config){
24778     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24779     
24780 };
24781
24782 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24783     
24784     title: '',
24785     html: '',
24786     weight: false,
24787     faicon: false,
24788     
24789     getAutoCreate : function()
24790     {
24791         
24792         var cfg = {
24793             tag : 'div',
24794             cls : 'alert',
24795             cn : [
24796                 {
24797                     tag : 'i',
24798                     cls : 'roo-alert-icon'
24799                     
24800                 },
24801                 {
24802                     tag : 'b',
24803                     cls : 'roo-alert-title',
24804                     html : this.title
24805                 },
24806                 {
24807                     tag : 'span',
24808                     cls : 'roo-alert-text',
24809                     html : this.html
24810                 }
24811             ]
24812         };
24813         
24814         if(this.faicon){
24815             cfg.cn[0].cls += ' fa ' + this.faicon;
24816         }
24817         
24818         if(this.weight){
24819             cfg.cls += ' alert-' + this.weight;
24820         }
24821         
24822         return cfg;
24823     },
24824     
24825     initEvents: function() 
24826     {
24827         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24828     },
24829     
24830     setTitle : function(str)
24831     {
24832         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24833     },
24834     
24835     setText : function(str)
24836     {
24837         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24838     },
24839     
24840     setWeight : function(weight)
24841     {
24842         if(this.weight){
24843             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24844         }
24845         
24846         this.weight = weight;
24847         
24848         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24849     },
24850     
24851     setIcon : function(icon)
24852     {
24853         if(this.faicon){
24854             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24855         }
24856         
24857         this.faicon = icon;
24858         
24859         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24860     },
24861     
24862     hide: function() 
24863     {
24864         this.el.hide();   
24865     },
24866     
24867     show: function() 
24868     {  
24869         this.el.show();   
24870     }
24871     
24872 });
24873
24874  
24875 /*
24876 * Licence: LGPL
24877 */
24878
24879 /**
24880  * @class Roo.bootstrap.UploadCropbox
24881  * @extends Roo.bootstrap.Component
24882  * Bootstrap UploadCropbox class
24883  * @cfg {String} emptyText show when image has been loaded
24884  * @cfg {String} rotateNotify show when image too small to rotate
24885  * @cfg {Number} errorTimeout default 3000
24886  * @cfg {Number} minWidth default 300
24887  * @cfg {Number} minHeight default 300
24888  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24889  * @cfg {Boolean} isDocument (true|false) default false
24890  * @cfg {String} url action url
24891  * @cfg {String} paramName default 'imageUpload'
24892  * @cfg {String} method default POST
24893  * @cfg {Boolean} loadMask (true|false) default true
24894  * @cfg {Boolean} loadingText default 'Loading...'
24895  * 
24896  * @constructor
24897  * Create a new UploadCropbox
24898  * @param {Object} config The config object
24899  */
24900
24901 Roo.bootstrap.UploadCropbox = function(config){
24902     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24903     
24904     this.addEvents({
24905         /**
24906          * @event beforeselectfile
24907          * Fire before select file
24908          * @param {Roo.bootstrap.UploadCropbox} this
24909          */
24910         "beforeselectfile" : true,
24911         /**
24912          * @event initial
24913          * Fire after initEvent
24914          * @param {Roo.bootstrap.UploadCropbox} this
24915          */
24916         "initial" : true,
24917         /**
24918          * @event crop
24919          * Fire after initEvent
24920          * @param {Roo.bootstrap.UploadCropbox} this
24921          * @param {String} data
24922          */
24923         "crop" : true,
24924         /**
24925          * @event prepare
24926          * Fire when preparing the file data
24927          * @param {Roo.bootstrap.UploadCropbox} this
24928          * @param {Object} file
24929          */
24930         "prepare" : true,
24931         /**
24932          * @event exception
24933          * Fire when get exception
24934          * @param {Roo.bootstrap.UploadCropbox} this
24935          * @param {XMLHttpRequest} xhr
24936          */
24937         "exception" : true,
24938         /**
24939          * @event beforeloadcanvas
24940          * Fire before load the canvas
24941          * @param {Roo.bootstrap.UploadCropbox} this
24942          * @param {String} src
24943          */
24944         "beforeloadcanvas" : true,
24945         /**
24946          * @event trash
24947          * Fire when trash image
24948          * @param {Roo.bootstrap.UploadCropbox} this
24949          */
24950         "trash" : true,
24951         /**
24952          * @event download
24953          * Fire when download the image
24954          * @param {Roo.bootstrap.UploadCropbox} this
24955          */
24956         "download" : true,
24957         /**
24958          * @event footerbuttonclick
24959          * Fire when footerbuttonclick
24960          * @param {Roo.bootstrap.UploadCropbox} this
24961          * @param {String} type
24962          */
24963         "footerbuttonclick" : true,
24964         /**
24965          * @event resize
24966          * Fire when resize
24967          * @param {Roo.bootstrap.UploadCropbox} this
24968          */
24969         "resize" : true,
24970         /**
24971          * @event rotate
24972          * Fire when rotate the image
24973          * @param {Roo.bootstrap.UploadCropbox} this
24974          * @param {String} pos
24975          */
24976         "rotate" : true,
24977         /**
24978          * @event inspect
24979          * Fire when inspect the file
24980          * @param {Roo.bootstrap.UploadCropbox} this
24981          * @param {Object} file
24982          */
24983         "inspect" : true,
24984         /**
24985          * @event upload
24986          * Fire when xhr upload the file
24987          * @param {Roo.bootstrap.UploadCropbox} this
24988          * @param {Object} data
24989          */
24990         "upload" : true,
24991         /**
24992          * @event arrange
24993          * Fire when arrange the file data
24994          * @param {Roo.bootstrap.UploadCropbox} this
24995          * @param {Object} formData
24996          */
24997         "arrange" : true
24998     });
24999     
25000     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25001 };
25002
25003 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25004     
25005     emptyText : 'Click to upload image',
25006     rotateNotify : 'Image is too small to rotate',
25007     errorTimeout : 3000,
25008     scale : 0,
25009     baseScale : 1,
25010     rotate : 0,
25011     dragable : false,
25012     pinching : false,
25013     mouseX : 0,
25014     mouseY : 0,
25015     cropData : false,
25016     minWidth : 300,
25017     minHeight : 300,
25018     file : false,
25019     exif : {},
25020     baseRotate : 1,
25021     cropType : 'image/jpeg',
25022     buttons : false,
25023     canvasLoaded : false,
25024     isDocument : false,
25025     method : 'POST',
25026     paramName : 'imageUpload',
25027     loadMask : true,
25028     loadingText : 'Loading...',
25029     maskEl : false,
25030     
25031     getAutoCreate : function()
25032     {
25033         var cfg = {
25034             tag : 'div',
25035             cls : 'roo-upload-cropbox',
25036             cn : [
25037                 {
25038                     tag : 'input',
25039                     cls : 'roo-upload-cropbox-selector',
25040                     type : 'file'
25041                 },
25042                 {
25043                     tag : 'div',
25044                     cls : 'roo-upload-cropbox-body',
25045                     style : 'cursor:pointer',
25046                     cn : [
25047                         {
25048                             tag : 'div',
25049                             cls : 'roo-upload-cropbox-preview'
25050                         },
25051                         {
25052                             tag : 'div',
25053                             cls : 'roo-upload-cropbox-thumb'
25054                         },
25055                         {
25056                             tag : 'div',
25057                             cls : 'roo-upload-cropbox-empty-notify',
25058                             html : this.emptyText
25059                         },
25060                         {
25061                             tag : 'div',
25062                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25063                             html : this.rotateNotify
25064                         }
25065                     ]
25066                 },
25067                 {
25068                     tag : 'div',
25069                     cls : 'roo-upload-cropbox-footer',
25070                     cn : {
25071                         tag : 'div',
25072                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25073                         cn : []
25074                     }
25075                 }
25076             ]
25077         };
25078         
25079         return cfg;
25080     },
25081     
25082     onRender : function(ct, position)
25083     {
25084         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25085         
25086         if (this.buttons.length) {
25087             
25088             Roo.each(this.buttons, function(bb) {
25089                 
25090                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25091                 
25092                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25093                 
25094             }, this);
25095         }
25096         
25097         if(this.loadMask){
25098             this.maskEl = this.el;
25099         }
25100     },
25101     
25102     initEvents : function()
25103     {
25104         this.urlAPI = (window.createObjectURL && window) || 
25105                                 (window.URL && URL.revokeObjectURL && URL) || 
25106                                 (window.webkitURL && webkitURL);
25107                         
25108         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25109         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25110         
25111         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25112         this.selectorEl.hide();
25113         
25114         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25115         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25116         
25117         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25118         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25119         this.thumbEl.hide();
25120         
25121         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25122         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25123         
25124         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25125         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25126         this.errorEl.hide();
25127         
25128         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25129         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25130         this.footerEl.hide();
25131         
25132         this.setThumbBoxSize();
25133         
25134         this.bind();
25135         
25136         this.resize();
25137         
25138         this.fireEvent('initial', this);
25139     },
25140
25141     bind : function()
25142     {
25143         var _this = this;
25144         
25145         window.addEventListener("resize", function() { _this.resize(); } );
25146         
25147         this.bodyEl.on('click', this.beforeSelectFile, this);
25148         
25149         if(Roo.isTouch){
25150             this.bodyEl.on('touchstart', this.onTouchStart, this);
25151             this.bodyEl.on('touchmove', this.onTouchMove, this);
25152             this.bodyEl.on('touchend', this.onTouchEnd, this);
25153         }
25154         
25155         if(!Roo.isTouch){
25156             this.bodyEl.on('mousedown', this.onMouseDown, this);
25157             this.bodyEl.on('mousemove', this.onMouseMove, this);
25158             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25159             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25160             Roo.get(document).on('mouseup', this.onMouseUp, this);
25161         }
25162         
25163         this.selectorEl.on('change', this.onFileSelected, this);
25164     },
25165     
25166     reset : function()
25167     {    
25168         this.scale = 0;
25169         this.baseScale = 1;
25170         this.rotate = 0;
25171         this.baseRotate = 1;
25172         this.dragable = false;
25173         this.pinching = false;
25174         this.mouseX = 0;
25175         this.mouseY = 0;
25176         this.cropData = false;
25177         this.notifyEl.dom.innerHTML = this.emptyText;
25178         
25179         this.selectorEl.dom.value = '';
25180         
25181     },
25182     
25183     resize : function()
25184     {
25185         if(this.fireEvent('resize', this) != false){
25186             this.setThumbBoxPosition();
25187             this.setCanvasPosition();
25188         }
25189     },
25190     
25191     onFooterButtonClick : function(e, el, o, type)
25192     {
25193         switch (type) {
25194             case 'rotate-left' :
25195                 this.onRotateLeft(e);
25196                 break;
25197             case 'rotate-right' :
25198                 this.onRotateRight(e);
25199                 break;
25200             case 'picture' :
25201                 this.beforeSelectFile(e);
25202                 break;
25203             case 'trash' :
25204                 this.trash(e);
25205                 break;
25206             case 'crop' :
25207                 this.crop(e);
25208                 break;
25209             case 'download' :
25210                 this.download(e);
25211                 break;
25212             default :
25213                 break;
25214         }
25215         
25216         this.fireEvent('footerbuttonclick', this, type);
25217     },
25218     
25219     beforeSelectFile : function(e)
25220     {
25221         e.preventDefault();
25222         
25223         if(this.fireEvent('beforeselectfile', this) != false){
25224             this.selectorEl.dom.click();
25225         }
25226     },
25227     
25228     onFileSelected : function(e)
25229     {
25230         e.preventDefault();
25231         
25232         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25233             return;
25234         }
25235         
25236         var file = this.selectorEl.dom.files[0];
25237         
25238         if(this.fireEvent('inspect', this, file) != false){
25239             this.prepare(file);
25240         }
25241         
25242     },
25243     
25244     trash : function(e)
25245     {
25246         this.fireEvent('trash', this);
25247     },
25248     
25249     download : function(e)
25250     {
25251         this.fireEvent('download', this);
25252     },
25253     
25254     loadCanvas : function(src)
25255     {   
25256         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25257             
25258             this.reset();
25259             
25260             this.imageEl = document.createElement('img');
25261             
25262             var _this = this;
25263             
25264             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25265             
25266             this.imageEl.src = src;
25267         }
25268     },
25269     
25270     onLoadCanvas : function()
25271     {   
25272         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25273         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25274         
25275         this.bodyEl.un('click', this.beforeSelectFile, this);
25276         
25277         this.notifyEl.hide();
25278         this.thumbEl.show();
25279         this.footerEl.show();
25280         
25281         this.baseRotateLevel();
25282         
25283         if(this.isDocument){
25284             this.setThumbBoxSize();
25285         }
25286         
25287         this.setThumbBoxPosition();
25288         
25289         this.baseScaleLevel();
25290         
25291         this.draw();
25292         
25293         this.resize();
25294         
25295         this.canvasLoaded = true;
25296         
25297         if(this.loadMask){
25298             this.maskEl.unmask();
25299         }
25300         
25301     },
25302     
25303     setCanvasPosition : function()
25304     {   
25305         if(!this.canvasEl){
25306             return;
25307         }
25308         
25309         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25310         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25311         
25312         this.previewEl.setLeft(pw);
25313         this.previewEl.setTop(ph);
25314         
25315     },
25316     
25317     onMouseDown : function(e)
25318     {   
25319         e.stopEvent();
25320         
25321         this.dragable = true;
25322         this.pinching = false;
25323         
25324         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25325             this.dragable = false;
25326             return;
25327         }
25328         
25329         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25330         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25331         
25332     },
25333     
25334     onMouseMove : function(e)
25335     {   
25336         e.stopEvent();
25337         
25338         if(!this.canvasLoaded){
25339             return;
25340         }
25341         
25342         if (!this.dragable){
25343             return;
25344         }
25345         
25346         var minX = Math.ceil(this.thumbEl.getLeft(true));
25347         var minY = Math.ceil(this.thumbEl.getTop(true));
25348         
25349         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25350         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25351         
25352         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25353         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25354         
25355         x = x - this.mouseX;
25356         y = y - this.mouseY;
25357         
25358         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25359         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25360         
25361         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25362         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25363         
25364         this.previewEl.setLeft(bgX);
25365         this.previewEl.setTop(bgY);
25366         
25367         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25368         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25369     },
25370     
25371     onMouseUp : function(e)
25372     {   
25373         e.stopEvent();
25374         
25375         this.dragable = false;
25376     },
25377     
25378     onMouseWheel : function(e)
25379     {   
25380         e.stopEvent();
25381         
25382         this.startScale = this.scale;
25383         
25384         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25385         
25386         if(!this.zoomable()){
25387             this.scale = this.startScale;
25388             return;
25389         }
25390         
25391         this.draw();
25392         
25393         return;
25394     },
25395     
25396     zoomable : function()
25397     {
25398         var minScale = this.thumbEl.getWidth() / this.minWidth;
25399         
25400         if(this.minWidth < this.minHeight){
25401             minScale = this.thumbEl.getHeight() / this.minHeight;
25402         }
25403         
25404         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25405         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25406         
25407         if(
25408                 this.isDocument &&
25409                 (this.rotate == 0 || this.rotate == 180) && 
25410                 (
25411                     width > this.imageEl.OriginWidth || 
25412                     height > this.imageEl.OriginHeight ||
25413                     (width < this.minWidth && height < this.minHeight)
25414                 )
25415         ){
25416             return false;
25417         }
25418         
25419         if(
25420                 this.isDocument &&
25421                 (this.rotate == 90 || this.rotate == 270) && 
25422                 (
25423                     width > this.imageEl.OriginWidth || 
25424                     height > this.imageEl.OriginHeight ||
25425                     (width < this.minHeight && height < this.minWidth)
25426                 )
25427         ){
25428             return false;
25429         }
25430         
25431         if(
25432                 !this.isDocument &&
25433                 (this.rotate == 0 || this.rotate == 180) && 
25434                 (
25435                     width < this.minWidth || 
25436                     width > this.imageEl.OriginWidth || 
25437                     height < this.minHeight || 
25438                     height > this.imageEl.OriginHeight
25439                 )
25440         ){
25441             return false;
25442         }
25443         
25444         if(
25445                 !this.isDocument &&
25446                 (this.rotate == 90 || this.rotate == 270) && 
25447                 (
25448                     width < this.minHeight || 
25449                     width > this.imageEl.OriginWidth || 
25450                     height < this.minWidth || 
25451                     height > this.imageEl.OriginHeight
25452                 )
25453         ){
25454             return false;
25455         }
25456         
25457         return true;
25458         
25459     },
25460     
25461     onRotateLeft : function(e)
25462     {   
25463         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25464             
25465             var minScale = this.thumbEl.getWidth() / this.minWidth;
25466             
25467             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25468             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25469             
25470             this.startScale = this.scale;
25471             
25472             while (this.getScaleLevel() < minScale){
25473             
25474                 this.scale = this.scale + 1;
25475                 
25476                 if(!this.zoomable()){
25477                     break;
25478                 }
25479                 
25480                 if(
25481                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25482                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25483                 ){
25484                     continue;
25485                 }
25486                 
25487                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25488
25489                 this.draw();
25490                 
25491                 return;
25492             }
25493             
25494             this.scale = this.startScale;
25495             
25496             this.onRotateFail();
25497             
25498             return false;
25499         }
25500         
25501         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25502
25503         if(this.isDocument){
25504             this.setThumbBoxSize();
25505             this.setThumbBoxPosition();
25506             this.setCanvasPosition();
25507         }
25508         
25509         this.draw();
25510         
25511         this.fireEvent('rotate', this, 'left');
25512         
25513     },
25514     
25515     onRotateRight : function(e)
25516     {
25517         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25518             
25519             var minScale = this.thumbEl.getWidth() / this.minWidth;
25520         
25521             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25522             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25523             
25524             this.startScale = this.scale;
25525             
25526             while (this.getScaleLevel() < minScale){
25527             
25528                 this.scale = this.scale + 1;
25529                 
25530                 if(!this.zoomable()){
25531                     break;
25532                 }
25533                 
25534                 if(
25535                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25536                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25537                 ){
25538                     continue;
25539                 }
25540                 
25541                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25542
25543                 this.draw();
25544                 
25545                 return;
25546             }
25547             
25548             this.scale = this.startScale;
25549             
25550             this.onRotateFail();
25551             
25552             return false;
25553         }
25554         
25555         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25556
25557         if(this.isDocument){
25558             this.setThumbBoxSize();
25559             this.setThumbBoxPosition();
25560             this.setCanvasPosition();
25561         }
25562         
25563         this.draw();
25564         
25565         this.fireEvent('rotate', this, 'right');
25566     },
25567     
25568     onRotateFail : function()
25569     {
25570         this.errorEl.show(true);
25571         
25572         var _this = this;
25573         
25574         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25575     },
25576     
25577     draw : function()
25578     {
25579         this.previewEl.dom.innerHTML = '';
25580         
25581         var canvasEl = document.createElement("canvas");
25582         
25583         var contextEl = canvasEl.getContext("2d");
25584         
25585         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25586         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25587         var center = this.imageEl.OriginWidth / 2;
25588         
25589         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25590             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25591             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25592             center = this.imageEl.OriginHeight / 2;
25593         }
25594         
25595         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25596         
25597         contextEl.translate(center, center);
25598         contextEl.rotate(this.rotate * Math.PI / 180);
25599
25600         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25601         
25602         this.canvasEl = document.createElement("canvas");
25603         
25604         this.contextEl = this.canvasEl.getContext("2d");
25605         
25606         switch (this.rotate) {
25607             case 0 :
25608                 
25609                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25610                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25611                 
25612                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25613                 
25614                 break;
25615             case 90 : 
25616                 
25617                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25618                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25619                 
25620                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25621                     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);
25622                     break;
25623                 }
25624                 
25625                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25626                 
25627                 break;
25628             case 180 :
25629                 
25630                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25631                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25632                 
25633                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25634                     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);
25635                     break;
25636                 }
25637                 
25638                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25639                 
25640                 break;
25641             case 270 :
25642                 
25643                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25644                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25645         
25646                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25647                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25648                     break;
25649                 }
25650                 
25651                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25652                 
25653                 break;
25654             default : 
25655                 break;
25656         }
25657         
25658         this.previewEl.appendChild(this.canvasEl);
25659         
25660         this.setCanvasPosition();
25661     },
25662     
25663     crop : function()
25664     {
25665         if(!this.canvasLoaded){
25666             return;
25667         }
25668         
25669         var imageCanvas = document.createElement("canvas");
25670         
25671         var imageContext = imageCanvas.getContext("2d");
25672         
25673         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25674         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25675         
25676         var center = imageCanvas.width / 2;
25677         
25678         imageContext.translate(center, center);
25679         
25680         imageContext.rotate(this.rotate * Math.PI / 180);
25681         
25682         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25683         
25684         var canvas = document.createElement("canvas");
25685         
25686         var context = canvas.getContext("2d");
25687                 
25688         canvas.width = this.minWidth;
25689         canvas.height = this.minHeight;
25690
25691         switch (this.rotate) {
25692             case 0 :
25693                 
25694                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25695                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25696                 
25697                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25698                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25699                 
25700                 var targetWidth = this.minWidth - 2 * x;
25701                 var targetHeight = this.minHeight - 2 * y;
25702                 
25703                 var scale = 1;
25704                 
25705                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25706                     scale = targetWidth / width;
25707                 }
25708                 
25709                 if(x > 0 && y == 0){
25710                     scale = targetHeight / height;
25711                 }
25712                 
25713                 if(x > 0 && y > 0){
25714                     scale = targetWidth / width;
25715                     
25716                     if(width < height){
25717                         scale = targetHeight / height;
25718                     }
25719                 }
25720                 
25721                 context.scale(scale, scale);
25722                 
25723                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25724                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25725
25726                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25727                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25728
25729                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25730                 
25731                 break;
25732             case 90 : 
25733                 
25734                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25735                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25736                 
25737                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25738                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25739                 
25740                 var targetWidth = this.minWidth - 2 * x;
25741                 var targetHeight = this.minHeight - 2 * y;
25742                 
25743                 var scale = 1;
25744                 
25745                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25746                     scale = targetWidth / width;
25747                 }
25748                 
25749                 if(x > 0 && y == 0){
25750                     scale = targetHeight / height;
25751                 }
25752                 
25753                 if(x > 0 && y > 0){
25754                     scale = targetWidth / width;
25755                     
25756                     if(width < height){
25757                         scale = targetHeight / height;
25758                     }
25759                 }
25760                 
25761                 context.scale(scale, scale);
25762                 
25763                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25764                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25765
25766                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25767                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25768                 
25769                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25770                 
25771                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25772                 
25773                 break;
25774             case 180 :
25775                 
25776                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25777                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25778                 
25779                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25780                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25781                 
25782                 var targetWidth = this.minWidth - 2 * x;
25783                 var targetHeight = this.minHeight - 2 * y;
25784                 
25785                 var scale = 1;
25786                 
25787                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25788                     scale = targetWidth / width;
25789                 }
25790                 
25791                 if(x > 0 && y == 0){
25792                     scale = targetHeight / height;
25793                 }
25794                 
25795                 if(x > 0 && y > 0){
25796                     scale = targetWidth / width;
25797                     
25798                     if(width < height){
25799                         scale = targetHeight / height;
25800                     }
25801                 }
25802                 
25803                 context.scale(scale, scale);
25804                 
25805                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25806                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25807
25808                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25809                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25810
25811                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25812                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25813                 
25814                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25815                 
25816                 break;
25817             case 270 :
25818                 
25819                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25820                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25821                 
25822                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25823                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25824                 
25825                 var targetWidth = this.minWidth - 2 * x;
25826                 var targetHeight = this.minHeight - 2 * y;
25827                 
25828                 var scale = 1;
25829                 
25830                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25831                     scale = targetWidth / width;
25832                 }
25833                 
25834                 if(x > 0 && y == 0){
25835                     scale = targetHeight / height;
25836                 }
25837                 
25838                 if(x > 0 && y > 0){
25839                     scale = targetWidth / width;
25840                     
25841                     if(width < height){
25842                         scale = targetHeight / height;
25843                     }
25844                 }
25845                 
25846                 context.scale(scale, scale);
25847                 
25848                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25849                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25850
25851                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25852                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25853                 
25854                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25855                 
25856                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25857                 
25858                 break;
25859             default : 
25860                 break;
25861         }
25862         
25863         this.cropData = canvas.toDataURL(this.cropType);
25864         
25865         if(this.fireEvent('crop', this, this.cropData) !== false){
25866             this.process(this.file, this.cropData);
25867         }
25868         
25869         return;
25870         
25871     },
25872     
25873     setThumbBoxSize : function()
25874     {
25875         var width, height;
25876         
25877         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25878             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25879             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25880             
25881             this.minWidth = width;
25882             this.minHeight = height;
25883             
25884             if(this.rotate == 90 || this.rotate == 270){
25885                 this.minWidth = height;
25886                 this.minHeight = width;
25887             }
25888         }
25889         
25890         height = 300;
25891         width = Math.ceil(this.minWidth * height / this.minHeight);
25892         
25893         if(this.minWidth > this.minHeight){
25894             width = 300;
25895             height = Math.ceil(this.minHeight * width / this.minWidth);
25896         }
25897         
25898         this.thumbEl.setStyle({
25899             width : width + 'px',
25900             height : height + 'px'
25901         });
25902
25903         return;
25904             
25905     },
25906     
25907     setThumbBoxPosition : function()
25908     {
25909         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25910         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25911         
25912         this.thumbEl.setLeft(x);
25913         this.thumbEl.setTop(y);
25914         
25915     },
25916     
25917     baseRotateLevel : function()
25918     {
25919         this.baseRotate = 1;
25920         
25921         if(
25922                 typeof(this.exif) != 'undefined' &&
25923                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25924                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25925         ){
25926             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25927         }
25928         
25929         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25930         
25931     },
25932     
25933     baseScaleLevel : function()
25934     {
25935         var width, height;
25936         
25937         if(this.isDocument){
25938             
25939             if(this.baseRotate == 6 || this.baseRotate == 8){
25940             
25941                 height = this.thumbEl.getHeight();
25942                 this.baseScale = height / this.imageEl.OriginWidth;
25943
25944                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25945                     width = this.thumbEl.getWidth();
25946                     this.baseScale = width / this.imageEl.OriginHeight;
25947                 }
25948
25949                 return;
25950             }
25951
25952             height = this.thumbEl.getHeight();
25953             this.baseScale = height / this.imageEl.OriginHeight;
25954
25955             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25956                 width = this.thumbEl.getWidth();
25957                 this.baseScale = width / this.imageEl.OriginWidth;
25958             }
25959
25960             return;
25961         }
25962         
25963         if(this.baseRotate == 6 || this.baseRotate == 8){
25964             
25965             width = this.thumbEl.getHeight();
25966             this.baseScale = width / this.imageEl.OriginHeight;
25967             
25968             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25969                 height = this.thumbEl.getWidth();
25970                 this.baseScale = height / this.imageEl.OriginHeight;
25971             }
25972             
25973             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25974                 height = this.thumbEl.getWidth();
25975                 this.baseScale = height / this.imageEl.OriginHeight;
25976                 
25977                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25978                     width = this.thumbEl.getHeight();
25979                     this.baseScale = width / this.imageEl.OriginWidth;
25980                 }
25981             }
25982             
25983             return;
25984         }
25985         
25986         width = this.thumbEl.getWidth();
25987         this.baseScale = width / this.imageEl.OriginWidth;
25988         
25989         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25990             height = this.thumbEl.getHeight();
25991             this.baseScale = height / this.imageEl.OriginHeight;
25992         }
25993         
25994         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25995             
25996             height = this.thumbEl.getHeight();
25997             this.baseScale = height / this.imageEl.OriginHeight;
25998             
25999             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26000                 width = this.thumbEl.getWidth();
26001                 this.baseScale = width / this.imageEl.OriginWidth;
26002             }
26003             
26004         }
26005         
26006         return;
26007     },
26008     
26009     getScaleLevel : function()
26010     {
26011         return this.baseScale * Math.pow(1.1, this.scale);
26012     },
26013     
26014     onTouchStart : function(e)
26015     {
26016         if(!this.canvasLoaded){
26017             this.beforeSelectFile(e);
26018             return;
26019         }
26020         
26021         var touches = e.browserEvent.touches;
26022         
26023         if(!touches){
26024             return;
26025         }
26026         
26027         if(touches.length == 1){
26028             this.onMouseDown(e);
26029             return;
26030         }
26031         
26032         if(touches.length != 2){
26033             return;
26034         }
26035         
26036         var coords = [];
26037         
26038         for(var i = 0, finger; finger = touches[i]; i++){
26039             coords.push(finger.pageX, finger.pageY);
26040         }
26041         
26042         var x = Math.pow(coords[0] - coords[2], 2);
26043         var y = Math.pow(coords[1] - coords[3], 2);
26044         
26045         this.startDistance = Math.sqrt(x + y);
26046         
26047         this.startScale = this.scale;
26048         
26049         this.pinching = true;
26050         this.dragable = false;
26051         
26052     },
26053     
26054     onTouchMove : function(e)
26055     {
26056         if(!this.pinching && !this.dragable){
26057             return;
26058         }
26059         
26060         var touches = e.browserEvent.touches;
26061         
26062         if(!touches){
26063             return;
26064         }
26065         
26066         if(this.dragable){
26067             this.onMouseMove(e);
26068             return;
26069         }
26070         
26071         var coords = [];
26072         
26073         for(var i = 0, finger; finger = touches[i]; i++){
26074             coords.push(finger.pageX, finger.pageY);
26075         }
26076         
26077         var x = Math.pow(coords[0] - coords[2], 2);
26078         var y = Math.pow(coords[1] - coords[3], 2);
26079         
26080         this.endDistance = Math.sqrt(x + y);
26081         
26082         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26083         
26084         if(!this.zoomable()){
26085             this.scale = this.startScale;
26086             return;
26087         }
26088         
26089         this.draw();
26090         
26091     },
26092     
26093     onTouchEnd : function(e)
26094     {
26095         this.pinching = false;
26096         this.dragable = false;
26097         
26098     },
26099     
26100     process : function(file, crop)
26101     {
26102         if(this.loadMask){
26103             this.maskEl.mask(this.loadingText);
26104         }
26105         
26106         this.xhr = new XMLHttpRequest();
26107         
26108         file.xhr = this.xhr;
26109
26110         this.xhr.open(this.method, this.url, true);
26111         
26112         var headers = {
26113             "Accept": "application/json",
26114             "Cache-Control": "no-cache",
26115             "X-Requested-With": "XMLHttpRequest"
26116         };
26117         
26118         for (var headerName in headers) {
26119             var headerValue = headers[headerName];
26120             if (headerValue) {
26121                 this.xhr.setRequestHeader(headerName, headerValue);
26122             }
26123         }
26124         
26125         var _this = this;
26126         
26127         this.xhr.onload = function()
26128         {
26129             _this.xhrOnLoad(_this.xhr);
26130         }
26131         
26132         this.xhr.onerror = function()
26133         {
26134             _this.xhrOnError(_this.xhr);
26135         }
26136         
26137         var formData = new FormData();
26138
26139         formData.append('returnHTML', 'NO');
26140         
26141         if(crop){
26142             formData.append('crop', crop);
26143         }
26144         
26145         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26146             formData.append(this.paramName, file, file.name);
26147         }
26148         
26149         if(typeof(file.filename) != 'undefined'){
26150             formData.append('filename', file.filename);
26151         }
26152         
26153         if(typeof(file.mimetype) != 'undefined'){
26154             formData.append('mimetype', file.mimetype);
26155         }
26156         
26157         if(this.fireEvent('arrange', this, formData) != false){
26158             this.xhr.send(formData);
26159         };
26160     },
26161     
26162     xhrOnLoad : function(xhr)
26163     {
26164         if(this.loadMask){
26165             this.maskEl.unmask();
26166         }
26167         
26168         if (xhr.readyState !== 4) {
26169             this.fireEvent('exception', this, xhr);
26170             return;
26171         }
26172
26173         var response = Roo.decode(xhr.responseText);
26174         
26175         if(!response.success){
26176             this.fireEvent('exception', this, xhr);
26177             return;
26178         }
26179         
26180         var response = Roo.decode(xhr.responseText);
26181         
26182         this.fireEvent('upload', this, response);
26183         
26184     },
26185     
26186     xhrOnError : function()
26187     {
26188         if(this.loadMask){
26189             this.maskEl.unmask();
26190         }
26191         
26192         Roo.log('xhr on error');
26193         
26194         var response = Roo.decode(xhr.responseText);
26195           
26196         Roo.log(response);
26197         
26198     },
26199     
26200     prepare : function(file)
26201     {   
26202         if(this.loadMask){
26203             this.maskEl.mask(this.loadingText);
26204         }
26205         
26206         this.file = false;
26207         this.exif = {};
26208         
26209         if(typeof(file) === 'string'){
26210             this.loadCanvas(file);
26211             return;
26212         }
26213         
26214         if(!file || !this.urlAPI){
26215             return;
26216         }
26217         
26218         this.file = file;
26219         this.cropType = file.type;
26220         
26221         var _this = this;
26222         
26223         if(this.fireEvent('prepare', this, this.file) != false){
26224             
26225             var reader = new FileReader();
26226             
26227             reader.onload = function (e) {
26228                 if (e.target.error) {
26229                     Roo.log(e.target.error);
26230                     return;
26231                 }
26232                 
26233                 var buffer = e.target.result,
26234                     dataView = new DataView(buffer),
26235                     offset = 2,
26236                     maxOffset = dataView.byteLength - 4,
26237                     markerBytes,
26238                     markerLength;
26239                 
26240                 if (dataView.getUint16(0) === 0xffd8) {
26241                     while (offset < maxOffset) {
26242                         markerBytes = dataView.getUint16(offset);
26243                         
26244                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26245                             markerLength = dataView.getUint16(offset + 2) + 2;
26246                             if (offset + markerLength > dataView.byteLength) {
26247                                 Roo.log('Invalid meta data: Invalid segment size.');
26248                                 break;
26249                             }
26250                             
26251                             if(markerBytes == 0xffe1){
26252                                 _this.parseExifData(
26253                                     dataView,
26254                                     offset,
26255                                     markerLength
26256                                 );
26257                             }
26258                             
26259                             offset += markerLength;
26260                             
26261                             continue;
26262                         }
26263                         
26264                         break;
26265                     }
26266                     
26267                 }
26268                 
26269                 var url = _this.urlAPI.createObjectURL(_this.file);
26270                 
26271                 _this.loadCanvas(url);
26272                 
26273                 return;
26274             }
26275             
26276             reader.readAsArrayBuffer(this.file);
26277             
26278         }
26279         
26280     },
26281     
26282     parseExifData : function(dataView, offset, length)
26283     {
26284         var tiffOffset = offset + 10,
26285             littleEndian,
26286             dirOffset;
26287     
26288         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26289             // No Exif data, might be XMP data instead
26290             return;
26291         }
26292         
26293         // Check for the ASCII code for "Exif" (0x45786966):
26294         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26295             // No Exif data, might be XMP data instead
26296             return;
26297         }
26298         if (tiffOffset + 8 > dataView.byteLength) {
26299             Roo.log('Invalid Exif data: Invalid segment size.');
26300             return;
26301         }
26302         // Check for the two null bytes:
26303         if (dataView.getUint16(offset + 8) !== 0x0000) {
26304             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26305             return;
26306         }
26307         // Check the byte alignment:
26308         switch (dataView.getUint16(tiffOffset)) {
26309         case 0x4949:
26310             littleEndian = true;
26311             break;
26312         case 0x4D4D:
26313             littleEndian = false;
26314             break;
26315         default:
26316             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26317             return;
26318         }
26319         // Check for the TIFF tag marker (0x002A):
26320         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26321             Roo.log('Invalid Exif data: Missing TIFF marker.');
26322             return;
26323         }
26324         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26325         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26326         
26327         this.parseExifTags(
26328             dataView,
26329             tiffOffset,
26330             tiffOffset + dirOffset,
26331             littleEndian
26332         );
26333     },
26334     
26335     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26336     {
26337         var tagsNumber,
26338             dirEndOffset,
26339             i;
26340         if (dirOffset + 6 > dataView.byteLength) {
26341             Roo.log('Invalid Exif data: Invalid directory offset.');
26342             return;
26343         }
26344         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26345         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26346         if (dirEndOffset + 4 > dataView.byteLength) {
26347             Roo.log('Invalid Exif data: Invalid directory size.');
26348             return;
26349         }
26350         for (i = 0; i < tagsNumber; i += 1) {
26351             this.parseExifTag(
26352                 dataView,
26353                 tiffOffset,
26354                 dirOffset + 2 + 12 * i, // tag offset
26355                 littleEndian
26356             );
26357         }
26358         // Return the offset to the next directory:
26359         return dataView.getUint32(dirEndOffset, littleEndian);
26360     },
26361     
26362     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26363     {
26364         var tag = dataView.getUint16(offset, littleEndian);
26365         
26366         this.exif[tag] = this.getExifValue(
26367             dataView,
26368             tiffOffset,
26369             offset,
26370             dataView.getUint16(offset + 2, littleEndian), // tag type
26371             dataView.getUint32(offset + 4, littleEndian), // tag length
26372             littleEndian
26373         );
26374     },
26375     
26376     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26377     {
26378         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26379             tagSize,
26380             dataOffset,
26381             values,
26382             i,
26383             str,
26384             c;
26385     
26386         if (!tagType) {
26387             Roo.log('Invalid Exif data: Invalid tag type.');
26388             return;
26389         }
26390         
26391         tagSize = tagType.size * length;
26392         // Determine if the value is contained in the dataOffset bytes,
26393         // or if the value at the dataOffset is a pointer to the actual data:
26394         dataOffset = tagSize > 4 ?
26395                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26396         if (dataOffset + tagSize > dataView.byteLength) {
26397             Roo.log('Invalid Exif data: Invalid data offset.');
26398             return;
26399         }
26400         if (length === 1) {
26401             return tagType.getValue(dataView, dataOffset, littleEndian);
26402         }
26403         values = [];
26404         for (i = 0; i < length; i += 1) {
26405             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26406         }
26407         
26408         if (tagType.ascii) {
26409             str = '';
26410             // Concatenate the chars:
26411             for (i = 0; i < values.length; i += 1) {
26412                 c = values[i];
26413                 // Ignore the terminating NULL byte(s):
26414                 if (c === '\u0000') {
26415                     break;
26416                 }
26417                 str += c;
26418             }
26419             return str;
26420         }
26421         return values;
26422     }
26423     
26424 });
26425
26426 Roo.apply(Roo.bootstrap.UploadCropbox, {
26427     tags : {
26428         'Orientation': 0x0112
26429     },
26430     
26431     Orientation: {
26432             1: 0, //'top-left',
26433 //            2: 'top-right',
26434             3: 180, //'bottom-right',
26435 //            4: 'bottom-left',
26436 //            5: 'left-top',
26437             6: 90, //'right-top',
26438 //            7: 'right-bottom',
26439             8: 270 //'left-bottom'
26440     },
26441     
26442     exifTagTypes : {
26443         // byte, 8-bit unsigned int:
26444         1: {
26445             getValue: function (dataView, dataOffset) {
26446                 return dataView.getUint8(dataOffset);
26447             },
26448             size: 1
26449         },
26450         // ascii, 8-bit byte:
26451         2: {
26452             getValue: function (dataView, dataOffset) {
26453                 return String.fromCharCode(dataView.getUint8(dataOffset));
26454             },
26455             size: 1,
26456             ascii: true
26457         },
26458         // short, 16 bit int:
26459         3: {
26460             getValue: function (dataView, dataOffset, littleEndian) {
26461                 return dataView.getUint16(dataOffset, littleEndian);
26462             },
26463             size: 2
26464         },
26465         // long, 32 bit int:
26466         4: {
26467             getValue: function (dataView, dataOffset, littleEndian) {
26468                 return dataView.getUint32(dataOffset, littleEndian);
26469             },
26470             size: 4
26471         },
26472         // rational = two long values, first is numerator, second is denominator:
26473         5: {
26474             getValue: function (dataView, dataOffset, littleEndian) {
26475                 return dataView.getUint32(dataOffset, littleEndian) /
26476                     dataView.getUint32(dataOffset + 4, littleEndian);
26477             },
26478             size: 8
26479         },
26480         // slong, 32 bit signed int:
26481         9: {
26482             getValue: function (dataView, dataOffset, littleEndian) {
26483                 return dataView.getInt32(dataOffset, littleEndian);
26484             },
26485             size: 4
26486         },
26487         // srational, two slongs, first is numerator, second is denominator:
26488         10: {
26489             getValue: function (dataView, dataOffset, littleEndian) {
26490                 return dataView.getInt32(dataOffset, littleEndian) /
26491                     dataView.getInt32(dataOffset + 4, littleEndian);
26492             },
26493             size: 8
26494         }
26495     },
26496     
26497     footer : {
26498         STANDARD : [
26499             {
26500                 tag : 'div',
26501                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26502                 action : 'rotate-left',
26503                 cn : [
26504                     {
26505                         tag : 'button',
26506                         cls : 'btn btn-default',
26507                         html : '<i class="fa fa-undo"></i>'
26508                     }
26509                 ]
26510             },
26511             {
26512                 tag : 'div',
26513                 cls : 'btn-group roo-upload-cropbox-picture',
26514                 action : 'picture',
26515                 cn : [
26516                     {
26517                         tag : 'button',
26518                         cls : 'btn btn-default',
26519                         html : '<i class="fa fa-picture-o"></i>'
26520                     }
26521                 ]
26522             },
26523             {
26524                 tag : 'div',
26525                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26526                 action : 'rotate-right',
26527                 cn : [
26528                     {
26529                         tag : 'button',
26530                         cls : 'btn btn-default',
26531                         html : '<i class="fa fa-repeat"></i>'
26532                     }
26533                 ]
26534             }
26535         ],
26536         DOCUMENT : [
26537             {
26538                 tag : 'div',
26539                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26540                 action : 'rotate-left',
26541                 cn : [
26542                     {
26543                         tag : 'button',
26544                         cls : 'btn btn-default',
26545                         html : '<i class="fa fa-undo"></i>'
26546                     }
26547                 ]
26548             },
26549             {
26550                 tag : 'div',
26551                 cls : 'btn-group roo-upload-cropbox-download',
26552                 action : 'download',
26553                 cn : [
26554                     {
26555                         tag : 'button',
26556                         cls : 'btn btn-default',
26557                         html : '<i class="fa fa-download"></i>'
26558                     }
26559                 ]
26560             },
26561             {
26562                 tag : 'div',
26563                 cls : 'btn-group roo-upload-cropbox-crop',
26564                 action : 'crop',
26565                 cn : [
26566                     {
26567                         tag : 'button',
26568                         cls : 'btn btn-default',
26569                         html : '<i class="fa fa-crop"></i>'
26570                     }
26571                 ]
26572             },
26573             {
26574                 tag : 'div',
26575                 cls : 'btn-group roo-upload-cropbox-trash',
26576                 action : 'trash',
26577                 cn : [
26578                     {
26579                         tag : 'button',
26580                         cls : 'btn btn-default',
26581                         html : '<i class="fa fa-trash"></i>'
26582                     }
26583                 ]
26584             },
26585             {
26586                 tag : 'div',
26587                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26588                 action : 'rotate-right',
26589                 cn : [
26590                     {
26591                         tag : 'button',
26592                         cls : 'btn btn-default',
26593                         html : '<i class="fa fa-repeat"></i>'
26594                     }
26595                 ]
26596             }
26597         ],
26598         ROTATOR : [
26599             {
26600                 tag : 'div',
26601                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26602                 action : 'rotate-left',
26603                 cn : [
26604                     {
26605                         tag : 'button',
26606                         cls : 'btn btn-default',
26607                         html : '<i class="fa fa-undo"></i>'
26608                     }
26609                 ]
26610             },
26611             {
26612                 tag : 'div',
26613                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26614                 action : 'rotate-right',
26615                 cn : [
26616                     {
26617                         tag : 'button',
26618                         cls : 'btn btn-default',
26619                         html : '<i class="fa fa-repeat"></i>'
26620                     }
26621                 ]
26622             }
26623         ]
26624     }
26625 });
26626
26627 /*
26628 * Licence: LGPL
26629 */
26630
26631 /**
26632  * @class Roo.bootstrap.DocumentManager
26633  * @extends Roo.bootstrap.Component
26634  * Bootstrap DocumentManager class
26635  * @cfg {String} paramName default 'imageUpload'
26636  * @cfg {String} method default POST
26637  * @cfg {String} url action url
26638  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26639  * @cfg {Boolean} multiple multiple upload default true
26640  * @cfg {Number} thumbSize default 300
26641  * @cfg {String} fieldLabel
26642  * @cfg {Number} labelWidth default 4
26643  * @cfg {String} labelAlign (left|top) default left
26644  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26645  * 
26646  * @constructor
26647  * Create a new DocumentManager
26648  * @param {Object} config The config object
26649  */
26650
26651 Roo.bootstrap.DocumentManager = function(config){
26652     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26653     
26654     this.addEvents({
26655         /**
26656          * @event initial
26657          * Fire when initial the DocumentManager
26658          * @param {Roo.bootstrap.DocumentManager} this
26659          */
26660         "initial" : true,
26661         /**
26662          * @event inspect
26663          * inspect selected file
26664          * @param {Roo.bootstrap.DocumentManager} this
26665          * @param {File} file
26666          */
26667         "inspect" : true,
26668         /**
26669          * @event exception
26670          * Fire when xhr load exception
26671          * @param {Roo.bootstrap.DocumentManager} this
26672          * @param {XMLHttpRequest} xhr
26673          */
26674         "exception" : true,
26675         /**
26676          * @event prepare
26677          * prepare the form data
26678          * @param {Roo.bootstrap.DocumentManager} this
26679          * @param {Object} formData
26680          */
26681         "prepare" : true,
26682         /**
26683          * @event remove
26684          * Fire when remove the file
26685          * @param {Roo.bootstrap.DocumentManager} this
26686          * @param {Object} file
26687          */
26688         "remove" : true,
26689         /**
26690          * @event refresh
26691          * Fire after refresh the file
26692          * @param {Roo.bootstrap.DocumentManager} this
26693          */
26694         "refresh" : true,
26695         /**
26696          * @event click
26697          * Fire after click the image
26698          * @param {Roo.bootstrap.DocumentManager} this
26699          * @param {Object} file
26700          */
26701         "click" : true,
26702         /**
26703          * @event edit
26704          * Fire when upload a image and editable set to true
26705          * @param {Roo.bootstrap.DocumentManager} this
26706          * @param {Object} file
26707          */
26708         "edit" : true,
26709         /**
26710          * @event beforeselectfile
26711          * Fire before select file
26712          * @param {Roo.bootstrap.DocumentManager} this
26713          */
26714         "beforeselectfile" : true,
26715         /**
26716          * @event process
26717          * Fire before process file
26718          * @param {Roo.bootstrap.DocumentManager} this
26719          * @param {Object} file
26720          */
26721         "process" : true
26722         
26723     });
26724 };
26725
26726 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26727     
26728     boxes : 0,
26729     inputName : '',
26730     thumbSize : 300,
26731     multiple : true,
26732     files : [],
26733     method : 'POST',
26734     url : '',
26735     paramName : 'imageUpload',
26736     fieldLabel : '',
26737     labelWidth : 4,
26738     labelAlign : 'left',
26739     editable : true,
26740     delegates : [],
26741     
26742     
26743     xhr : false, 
26744     
26745     getAutoCreate : function()
26746     {   
26747         var managerWidget = {
26748             tag : 'div',
26749             cls : 'roo-document-manager',
26750             cn : [
26751                 {
26752                     tag : 'input',
26753                     cls : 'roo-document-manager-selector',
26754                     type : 'file'
26755                 },
26756                 {
26757                     tag : 'div',
26758                     cls : 'roo-document-manager-uploader',
26759                     cn : [
26760                         {
26761                             tag : 'div',
26762                             cls : 'roo-document-manager-upload-btn',
26763                             html : '<i class="fa fa-plus"></i>'
26764                         }
26765                     ]
26766                     
26767                 }
26768             ]
26769         };
26770         
26771         var content = [
26772             {
26773                 tag : 'div',
26774                 cls : 'column col-md-12',
26775                 cn : managerWidget
26776             }
26777         ];
26778         
26779         if(this.fieldLabel.length){
26780             
26781             content = [
26782                 {
26783                     tag : 'div',
26784                     cls : 'column col-md-12',
26785                     html : this.fieldLabel
26786                 },
26787                 {
26788                     tag : 'div',
26789                     cls : 'column col-md-12',
26790                     cn : managerWidget
26791                 }
26792             ];
26793
26794             if(this.labelAlign == 'left'){
26795                 content = [
26796                     {
26797                         tag : 'div',
26798                         cls : 'column col-md-' + this.labelWidth,
26799                         html : this.fieldLabel
26800                     },
26801                     {
26802                         tag : 'div',
26803                         cls : 'column col-md-' + (12 - this.labelWidth),
26804                         cn : managerWidget
26805                     }
26806                 ];
26807                 
26808             }
26809         }
26810         
26811         var cfg = {
26812             tag : 'div',
26813             cls : 'row clearfix',
26814             cn : content
26815         };
26816         
26817         return cfg;
26818         
26819     },
26820     
26821     initEvents : function()
26822     {
26823         this.managerEl = this.el.select('.roo-document-manager', true).first();
26824         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26825         
26826         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26827         this.selectorEl.hide();
26828         
26829         if(this.multiple){
26830             this.selectorEl.attr('multiple', 'multiple');
26831         }
26832         
26833         this.selectorEl.on('change', this.onFileSelected, this);
26834         
26835         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26836         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26837         
26838         this.uploader.on('click', this.onUploaderClick, this);
26839         
26840         this.renderProgressDialog();
26841         
26842         var _this = this;
26843         
26844         window.addEventListener("resize", function() { _this.refresh(); } );
26845         
26846         this.fireEvent('initial', this);
26847     },
26848     
26849     renderProgressDialog : function()
26850     {
26851         var _this = this;
26852         
26853         this.progressDialog = new Roo.bootstrap.Modal({
26854             cls : 'roo-document-manager-progress-dialog',
26855             allow_close : false,
26856             title : '',
26857             buttons : [
26858                 {
26859                     name  :'cancel',
26860                     weight : 'danger',
26861                     html : 'Cancel'
26862                 }
26863             ], 
26864             listeners : { 
26865                 btnclick : function() {
26866                     _this.uploadCancel();
26867                     this.hide();
26868                 }
26869             }
26870         });
26871          
26872         this.progressDialog.render(Roo.get(document.body));
26873          
26874         this.progress = new Roo.bootstrap.Progress({
26875             cls : 'roo-document-manager-progress',
26876             active : true,
26877             striped : true
26878         });
26879         
26880         this.progress.render(this.progressDialog.getChildContainer());
26881         
26882         this.progressBar = new Roo.bootstrap.ProgressBar({
26883             cls : 'roo-document-manager-progress-bar',
26884             aria_valuenow : 0,
26885             aria_valuemin : 0,
26886             aria_valuemax : 12,
26887             panel : 'success'
26888         });
26889         
26890         this.progressBar.render(this.progress.getChildContainer());
26891     },
26892     
26893     onUploaderClick : function(e)
26894     {
26895         e.preventDefault();
26896      
26897         if(this.fireEvent('beforeselectfile', this) != false){
26898             this.selectorEl.dom.click();
26899         }
26900         
26901     },
26902     
26903     onFileSelected : function(e)
26904     {
26905         e.preventDefault();
26906         
26907         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26908             return;
26909         }
26910         
26911         Roo.each(this.selectorEl.dom.files, function(file){
26912             if(this.fireEvent('inspect', this, file) != false){
26913                 this.files.push(file);
26914             }
26915         }, this);
26916         
26917         this.queue();
26918         
26919     },
26920     
26921     queue : function()
26922     {
26923         this.selectorEl.dom.value = '';
26924         
26925         if(!this.files.length){
26926             return;
26927         }
26928         
26929         if(this.boxes > 0 && this.files.length > this.boxes){
26930             this.files = this.files.slice(0, this.boxes);
26931         }
26932         
26933         this.uploader.show();
26934         
26935         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26936             this.uploader.hide();
26937         }
26938         
26939         var _this = this;
26940         
26941         var files = [];
26942         
26943         var docs = [];
26944         
26945         Roo.each(this.files, function(file){
26946             
26947             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26948                 var f = this.renderPreview(file);
26949                 files.push(f);
26950                 return;
26951             }
26952             
26953             if(file.type.indexOf('image') != -1){
26954                 this.delegates.push(
26955                     (function(){
26956                         _this.process(file);
26957                     }).createDelegate(this)
26958                 );
26959         
26960                 return;
26961             }
26962             
26963             docs.push(
26964                 (function(){
26965                     _this.process(file);
26966                 }).createDelegate(this)
26967             );
26968             
26969         }, this);
26970         
26971         this.files = files;
26972         
26973         this.delegates = this.delegates.concat(docs);
26974         
26975         if(!this.delegates.length){
26976             this.refresh();
26977             return;
26978         }
26979         
26980         this.progressBar.aria_valuemax = this.delegates.length;
26981         
26982         this.arrange();
26983         
26984         return;
26985     },
26986     
26987     arrange : function()
26988     {
26989         if(!this.delegates.length){
26990             this.progressDialog.hide();
26991             this.refresh();
26992             return;
26993         }
26994         
26995         var delegate = this.delegates.shift();
26996         
26997         this.progressDialog.show();
26998         
26999         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27000         
27001         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27002         
27003         delegate();
27004     },
27005     
27006     refresh : function()
27007     {
27008         this.uploader.show();
27009         
27010         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27011             this.uploader.hide();
27012         }
27013         
27014         Roo.isTouch ? this.closable(false) : this.closable(true);
27015         
27016         this.fireEvent('refresh', this);
27017     },
27018     
27019     onRemove : function(e, el, o)
27020     {
27021         e.preventDefault();
27022         
27023         this.fireEvent('remove', this, o);
27024         
27025     },
27026     
27027     remove : function(o)
27028     {
27029         var files = [];
27030         
27031         Roo.each(this.files, function(file){
27032             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27033                 files.push(file);
27034                 return;
27035             }
27036
27037             o.target.remove();
27038
27039         }, this);
27040         
27041         this.files = files;
27042         
27043         this.refresh();
27044     },
27045     
27046     clear : function()
27047     {
27048         Roo.each(this.files, function(file){
27049             if(!file.target){
27050                 return;
27051             }
27052             
27053             file.target.remove();
27054
27055         }, this);
27056         
27057         this.files = [];
27058         
27059         this.refresh();
27060     },
27061     
27062     onClick : function(e, el, o)
27063     {
27064         e.preventDefault();
27065         
27066         this.fireEvent('click', this, o);
27067         
27068     },
27069     
27070     closable : function(closable)
27071     {
27072         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27073             
27074             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27075             
27076             if(closable){
27077                 el.show();
27078                 return;
27079             }
27080             
27081             el.hide();
27082             
27083         }, this);
27084     },
27085     
27086     xhrOnLoad : function(xhr)
27087     {
27088         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27089             el.remove();
27090         }, this);
27091         
27092         if (xhr.readyState !== 4) {
27093             this.arrange();
27094             this.fireEvent('exception', this, xhr);
27095             return;
27096         }
27097
27098         var response = Roo.decode(xhr.responseText);
27099         
27100         if(!response.success){
27101             this.arrange();
27102             this.fireEvent('exception', this, xhr);
27103             return;
27104         }
27105         
27106         var file = this.renderPreview(response.data);
27107         
27108         this.files.push(file);
27109         
27110         this.arrange();
27111         
27112     },
27113     
27114     xhrOnError : function(xhr)
27115     {
27116         Roo.log('xhr on error');
27117         
27118         var response = Roo.decode(xhr.responseText);
27119           
27120         Roo.log(response);
27121         
27122         this.arrange();
27123     },
27124     
27125     process : function(file)
27126     {
27127         if(this.fireEvent('process', this, file) !== false){
27128             if(this.editable && file.type.indexOf('image') != -1){
27129                 this.fireEvent('edit', this, file);
27130                 return;
27131             }
27132
27133             this.uploadStart(file, false);
27134
27135             return;
27136         }
27137         
27138     },
27139     
27140     uploadStart : function(file, crop)
27141     {
27142         this.xhr = new XMLHttpRequest();
27143         
27144         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27145             this.arrange();
27146             return;
27147         }
27148         
27149         file.xhr = this.xhr;
27150             
27151         this.managerEl.createChild({
27152             tag : 'div',
27153             cls : 'roo-document-manager-loading',
27154             cn : [
27155                 {
27156                     tag : 'div',
27157                     tooltip : file.name,
27158                     cls : 'roo-document-manager-thumb',
27159                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27160                 }
27161             ]
27162
27163         });
27164
27165         this.xhr.open(this.method, this.url, true);
27166         
27167         var headers = {
27168             "Accept": "application/json",
27169             "Cache-Control": "no-cache",
27170             "X-Requested-With": "XMLHttpRequest"
27171         };
27172         
27173         for (var headerName in headers) {
27174             var headerValue = headers[headerName];
27175             if (headerValue) {
27176                 this.xhr.setRequestHeader(headerName, headerValue);
27177             }
27178         }
27179         
27180         var _this = this;
27181         
27182         this.xhr.onload = function()
27183         {
27184             _this.xhrOnLoad(_this.xhr);
27185         }
27186         
27187         this.xhr.onerror = function()
27188         {
27189             _this.xhrOnError(_this.xhr);
27190         }
27191         
27192         var formData = new FormData();
27193
27194         formData.append('returnHTML', 'NO');
27195         
27196         if(crop){
27197             formData.append('crop', crop);
27198         }
27199         
27200         formData.append(this.paramName, file, file.name);
27201         
27202         if(this.fireEvent('prepare', this, formData) != false){
27203             this.xhr.send(formData);
27204         };
27205     },
27206     
27207     uploadCancel : function()
27208     {
27209         if (this.xhr) {
27210             this.xhr.abort();
27211         }
27212         
27213         
27214         this.delegates = [];
27215         
27216         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27217             el.remove();
27218         }, this);
27219         
27220         this.arrange();
27221     },
27222     
27223     renderPreview : function(file)
27224     {
27225         if(typeof(file.target) != 'undefined' && file.target){
27226             return file;
27227         }
27228         
27229         var previewEl = this.managerEl.createChild({
27230             tag : 'div',
27231             cls : 'roo-document-manager-preview',
27232             cn : [
27233                 {
27234                     tag : 'div',
27235                     tooltip : file.filename,
27236                     cls : 'roo-document-manager-thumb',
27237                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27238                 },
27239                 {
27240                     tag : 'button',
27241                     cls : 'close',
27242                     html : '<i class="fa fa-times-circle"></i>'
27243                 }
27244             ]
27245         });
27246
27247         var close = previewEl.select('button.close', true).first();
27248
27249         close.on('click', this.onRemove, this, file);
27250
27251         file.target = previewEl;
27252
27253         var image = previewEl.select('img', true).first();
27254         
27255         var _this = this;
27256         
27257         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27258         
27259         image.on('click', this.onClick, this, file);
27260         
27261         return file;
27262         
27263     },
27264     
27265     onPreviewLoad : function(file, image)
27266     {
27267         if(typeof(file.target) == 'undefined' || !file.target){
27268             return;
27269         }
27270         
27271         var width = image.dom.naturalWidth || image.dom.width;
27272         var height = image.dom.naturalHeight || image.dom.height;
27273         
27274         if(width > height){
27275             file.target.addClass('wide');
27276             return;
27277         }
27278         
27279         file.target.addClass('tall');
27280         return;
27281         
27282     },
27283     
27284     uploadFromSource : function(file, crop)
27285     {
27286         this.xhr = new XMLHttpRequest();
27287         
27288         this.managerEl.createChild({
27289             tag : 'div',
27290             cls : 'roo-document-manager-loading',
27291             cn : [
27292                 {
27293                     tag : 'div',
27294                     tooltip : file.name,
27295                     cls : 'roo-document-manager-thumb',
27296                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27297                 }
27298             ]
27299
27300         });
27301
27302         this.xhr.open(this.method, this.url, true);
27303         
27304         var headers = {
27305             "Accept": "application/json",
27306             "Cache-Control": "no-cache",
27307             "X-Requested-With": "XMLHttpRequest"
27308         };
27309         
27310         for (var headerName in headers) {
27311             var headerValue = headers[headerName];
27312             if (headerValue) {
27313                 this.xhr.setRequestHeader(headerName, headerValue);
27314             }
27315         }
27316         
27317         var _this = this;
27318         
27319         this.xhr.onload = function()
27320         {
27321             _this.xhrOnLoad(_this.xhr);
27322         }
27323         
27324         this.xhr.onerror = function()
27325         {
27326             _this.xhrOnError(_this.xhr);
27327         }
27328         
27329         var formData = new FormData();
27330
27331         formData.append('returnHTML', 'NO');
27332         
27333         formData.append('crop', crop);
27334         
27335         if(typeof(file.filename) != 'undefined'){
27336             formData.append('filename', file.filename);
27337         }
27338         
27339         if(typeof(file.mimetype) != 'undefined'){
27340             formData.append('mimetype', file.mimetype);
27341         }
27342         
27343         if(this.fireEvent('prepare', this, formData) != false){
27344             this.xhr.send(formData);
27345         };
27346     }
27347 });
27348
27349 /*
27350 * Licence: LGPL
27351 */
27352
27353 /**
27354  * @class Roo.bootstrap.DocumentViewer
27355  * @extends Roo.bootstrap.Component
27356  * Bootstrap DocumentViewer class
27357  * 
27358  * @constructor
27359  * Create a new DocumentViewer
27360  * @param {Object} config The config object
27361  */
27362
27363 Roo.bootstrap.DocumentViewer = function(config){
27364     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27365     
27366     this.addEvents({
27367         /**
27368          * @event initial
27369          * Fire after initEvent
27370          * @param {Roo.bootstrap.DocumentViewer} this
27371          */
27372         "initial" : true,
27373         /**
27374          * @event click
27375          * Fire after click
27376          * @param {Roo.bootstrap.DocumentViewer} this
27377          */
27378         "click" : true,
27379         /**
27380          * @event trash
27381          * Fire after trash button
27382          * @param {Roo.bootstrap.DocumentViewer} this
27383          */
27384         "trash" : true
27385         
27386     });
27387 };
27388
27389 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27390     
27391     getAutoCreate : function()
27392     {
27393         var cfg = {
27394             tag : 'div',
27395             cls : 'roo-document-viewer',
27396             cn : [
27397                 {
27398                     tag : 'div',
27399                     cls : 'roo-document-viewer-body',
27400                     cn : [
27401                         {
27402                             tag : 'div',
27403                             cls : 'roo-document-viewer-thumb',
27404                             cn : [
27405                                 {
27406                                     tag : 'img',
27407                                     cls : 'roo-document-viewer-image'
27408                                 }
27409                             ]
27410                         }
27411                     ]
27412                 },
27413                 {
27414                     tag : 'div',
27415                     cls : 'roo-document-viewer-footer',
27416                     cn : {
27417                         tag : 'div',
27418                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27419                         cn : [
27420                             {
27421                                 tag : 'div',
27422                                 cls : 'btn-group',
27423                                 cn : [
27424                                     {
27425                                         tag : 'button',
27426                                         cls : 'btn btn-default roo-document-viewer-trash',
27427                                         html : '<i class="fa fa-trash"></i>'
27428                                     }
27429                                 ]
27430                             }
27431                         ]
27432                     }
27433                 }
27434             ]
27435         };
27436         
27437         return cfg;
27438     },
27439     
27440     initEvents : function()
27441     {
27442         
27443         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27444         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27445         
27446         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27447         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27448         
27449         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27450         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27451         
27452         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27453         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27454         
27455         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27456         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27457         
27458         this.bodyEl.on('click', this.onClick, this);
27459         
27460         this.trashBtn.on('click', this.onTrash, this);
27461         
27462     },
27463     
27464     initial : function()
27465     {
27466 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27467         
27468         
27469         this.fireEvent('initial', this);
27470         
27471     },
27472     
27473     onClick : function(e)
27474     {
27475         e.preventDefault();
27476         
27477         this.fireEvent('click', this);
27478     },
27479     
27480     onTrash : function(e)
27481     {
27482         e.preventDefault();
27483         
27484         this.fireEvent('trash', this);
27485     }
27486     
27487 });
27488 /*
27489  * - LGPL
27490  *
27491  * nav progress bar
27492  * 
27493  */
27494
27495 /**
27496  * @class Roo.bootstrap.NavProgressBar
27497  * @extends Roo.bootstrap.Component
27498  * Bootstrap NavProgressBar class
27499  * 
27500  * @constructor
27501  * Create a new nav progress bar
27502  * @param {Object} config The config object
27503  */
27504
27505 Roo.bootstrap.NavProgressBar = function(config){
27506     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27507
27508     this.bullets = this.bullets || [];
27509    
27510 //    Roo.bootstrap.NavProgressBar.register(this);
27511      this.addEvents({
27512         /**
27513              * @event changed
27514              * Fires when the active item changes
27515              * @param {Roo.bootstrap.NavProgressBar} this
27516              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27517              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27518          */
27519         'changed': true
27520      });
27521     
27522 };
27523
27524 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27525     
27526     bullets : [],
27527     barItems : [],
27528     
27529     getAutoCreate : function()
27530     {
27531         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27532         
27533         cfg = {
27534             tag : 'div',
27535             cls : 'roo-navigation-bar-group',
27536             cn : [
27537                 {
27538                     tag : 'div',
27539                     cls : 'roo-navigation-top-bar'
27540                 },
27541                 {
27542                     tag : 'div',
27543                     cls : 'roo-navigation-bullets-bar',
27544                     cn : [
27545                         {
27546                             tag : 'ul',
27547                             cls : 'roo-navigation-bar'
27548                         }
27549                     ]
27550                 },
27551                 
27552                 {
27553                     tag : 'div',
27554                     cls : 'roo-navigation-bottom-bar'
27555                 }
27556             ]
27557             
27558         };
27559         
27560         return cfg;
27561         
27562     },
27563     
27564     initEvents: function() 
27565     {
27566         
27567     },
27568     
27569     onRender : function(ct, position) 
27570     {
27571         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27572         
27573         if(this.bullets.length){
27574             Roo.each(this.bullets, function(b){
27575                this.addItem(b);
27576             }, this);
27577         }
27578         
27579         this.format();
27580         
27581     },
27582     
27583     addItem : function(cfg)
27584     {
27585         var item = new Roo.bootstrap.NavProgressItem(cfg);
27586         
27587         item.parentId = this.id;
27588         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27589         
27590         if(cfg.html){
27591             var top = new Roo.bootstrap.Element({
27592                 tag : 'div',
27593                 cls : 'roo-navigation-bar-text'
27594             });
27595             
27596             var bottom = new Roo.bootstrap.Element({
27597                 tag : 'div',
27598                 cls : 'roo-navigation-bar-text'
27599             });
27600             
27601             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27602             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27603             
27604             var topText = new Roo.bootstrap.Element({
27605                 tag : 'span',
27606                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27607             });
27608             
27609             var bottomText = new Roo.bootstrap.Element({
27610                 tag : 'span',
27611                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27612             });
27613             
27614             topText.onRender(top.el, null);
27615             bottomText.onRender(bottom.el, null);
27616             
27617             item.topEl = top;
27618             item.bottomEl = bottom;
27619         }
27620         
27621         this.barItems.push(item);
27622         
27623         return item;
27624     },
27625     
27626     getActive : function()
27627     {
27628         var active = false;
27629         
27630         Roo.each(this.barItems, function(v){
27631             
27632             if (!v.isActive()) {
27633                 return;
27634             }
27635             
27636             active = v;
27637             return false;
27638             
27639         });
27640         
27641         return active;
27642     },
27643     
27644     setActiveItem : function(item)
27645     {
27646         var prev = false;
27647         
27648         Roo.each(this.barItems, function(v){
27649             if (v.rid == item.rid) {
27650                 return ;
27651             }
27652             
27653             if (v.isActive()) {
27654                 v.setActive(false);
27655                 prev = v;
27656             }
27657         });
27658
27659         item.setActive(true);
27660         
27661         this.fireEvent('changed', this, item, prev);
27662     },
27663     
27664     getBarItem: function(rid)
27665     {
27666         var ret = false;
27667         
27668         Roo.each(this.barItems, function(e) {
27669             if (e.rid != rid) {
27670                 return;
27671             }
27672             
27673             ret =  e;
27674             return false;
27675         });
27676         
27677         return ret;
27678     },
27679     
27680     indexOfItem : function(item)
27681     {
27682         var index = false;
27683         
27684         Roo.each(this.barItems, function(v, i){
27685             
27686             if (v.rid != item.rid) {
27687                 return;
27688             }
27689             
27690             index = i;
27691             return false
27692         });
27693         
27694         return index;
27695     },
27696     
27697     setActiveNext : function()
27698     {
27699         var i = this.indexOfItem(this.getActive());
27700         
27701         if (i > this.barItems.length) {
27702             return;
27703         }
27704         
27705         this.setActiveItem(this.barItems[i+1]);
27706     },
27707     
27708     setActivePrev : function()
27709     {
27710         var i = this.indexOfItem(this.getActive());
27711         
27712         if (i  < 1) {
27713             return;
27714         }
27715         
27716         this.setActiveItem(this.barItems[i-1]);
27717     },
27718     
27719     format : function()
27720     {
27721         if(!this.barItems.length){
27722             return;
27723         }
27724      
27725         var width = 100 / this.barItems.length;
27726         
27727         Roo.each(this.barItems, function(i){
27728             i.el.setStyle('width', width + '%');
27729             i.topEl.el.setStyle('width', width + '%');
27730             i.bottomEl.el.setStyle('width', width + '%');
27731         }, this);
27732         
27733     }
27734     
27735 });
27736 /*
27737  * - LGPL
27738  *
27739  * Nav Progress Item
27740  * 
27741  */
27742
27743 /**
27744  * @class Roo.bootstrap.NavProgressItem
27745  * @extends Roo.bootstrap.Component
27746  * Bootstrap NavProgressItem class
27747  * @cfg {String} rid the reference id
27748  * @cfg {Boolean} active (true|false) Is item active default false
27749  * @cfg {Boolean} disabled (true|false) Is item active default false
27750  * @cfg {String} html
27751  * @cfg {String} position (top|bottom) text position default bottom
27752  * @cfg {String} icon show icon instead of number
27753  * 
27754  * @constructor
27755  * Create a new NavProgressItem
27756  * @param {Object} config The config object
27757  */
27758 Roo.bootstrap.NavProgressItem = function(config){
27759     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27760     this.addEvents({
27761         // raw events
27762         /**
27763          * @event click
27764          * The raw click event for the entire grid.
27765          * @param {Roo.bootstrap.NavProgressItem} this
27766          * @param {Roo.EventObject} e
27767          */
27768         "click" : true
27769     });
27770    
27771 };
27772
27773 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27774     
27775     rid : '',
27776     active : false,
27777     disabled : false,
27778     html : '',
27779     position : 'bottom',
27780     icon : false,
27781     
27782     getAutoCreate : function()
27783     {
27784         var iconCls = 'roo-navigation-bar-item-icon';
27785         
27786         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27787         
27788         var cfg = {
27789             tag: 'li',
27790             cls: 'roo-navigation-bar-item',
27791             cn : [
27792                 {
27793                     tag : 'i',
27794                     cls : iconCls
27795                 }
27796             ]
27797         };
27798         
27799         if(this.active){
27800             cfg.cls += ' active';
27801         }
27802         if(this.disabled){
27803             cfg.cls += ' disabled';
27804         }
27805         
27806         return cfg;
27807     },
27808     
27809     disable : function()
27810     {
27811         this.setDisabled(true);
27812     },
27813     
27814     enable : function()
27815     {
27816         this.setDisabled(false);
27817     },
27818     
27819     initEvents: function() 
27820     {
27821         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27822         
27823         this.iconEl.on('click', this.onClick, this);
27824     },
27825     
27826     onClick : function(e)
27827     {
27828         e.preventDefault();
27829         
27830         if(this.disabled){
27831             return;
27832         }
27833         
27834         if(this.fireEvent('click', this, e) === false){
27835             return;
27836         };
27837         
27838         this.parent().setActiveItem(this);
27839     },
27840     
27841     isActive: function () 
27842     {
27843         return this.active;
27844     },
27845     
27846     setActive : function(state)
27847     {
27848         if(this.active == state){
27849             return;
27850         }
27851         
27852         this.active = state;
27853         
27854         if (state) {
27855             this.el.addClass('active');
27856             return;
27857         }
27858         
27859         this.el.removeClass('active');
27860         
27861         return;
27862     },
27863     
27864     setDisabled : function(state)
27865     {
27866         if(this.disabled == state){
27867             return;
27868         }
27869         
27870         this.disabled = state;
27871         
27872         if (state) {
27873             this.el.addClass('disabled');
27874             return;
27875         }
27876         
27877         this.el.removeClass('disabled');
27878     },
27879     
27880     tooltipEl : function()
27881     {
27882         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27883     }
27884 });
27885  
27886
27887  /*
27888  * - LGPL
27889  *
27890  * FieldLabel
27891  * 
27892  */
27893
27894 /**
27895  * @class Roo.bootstrap.FieldLabel
27896  * @extends Roo.bootstrap.Component
27897  * Bootstrap FieldLabel class
27898  * @cfg {String} html contents of the element
27899  * @cfg {String} tag tag of the element default label
27900  * @cfg {String} cls class of the element
27901  * @cfg {String} target label target 
27902  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27903  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27904  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27905  * @cfg {String} iconTooltip default "This field is required"
27906  * 
27907  * @constructor
27908  * Create a new FieldLabel
27909  * @param {Object} config The config object
27910  */
27911
27912 Roo.bootstrap.FieldLabel = function(config){
27913     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27914     
27915     this.addEvents({
27916             /**
27917              * @event invalid
27918              * Fires after the field has been marked as invalid.
27919              * @param {Roo.form.FieldLabel} this
27920              * @param {String} msg The validation message
27921              */
27922             invalid : true,
27923             /**
27924              * @event valid
27925              * Fires after the field has been validated with no errors.
27926              * @param {Roo.form.FieldLabel} this
27927              */
27928             valid : true
27929         });
27930 };
27931
27932 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27933     
27934     tag: 'label',
27935     cls: '',
27936     html: '',
27937     target: '',
27938     allowBlank : true,
27939     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27940     validClass : 'text-success fa fa-lg fa-check',
27941     iconTooltip : 'This field is required',
27942     
27943     getAutoCreate : function(){
27944         
27945         var cfg = {
27946             tag : this.tag,
27947             cls : 'roo-bootstrap-field-label ' + this.cls,
27948             for : this.target,
27949             cn : [
27950                 {
27951                     tag : 'i',
27952                     cls : '',
27953                     tooltip : this.iconTooltip
27954                 },
27955                 {
27956                     tag : 'span',
27957                     html : this.html
27958                 }
27959             ] 
27960         };
27961         
27962         return cfg;
27963     },
27964     
27965     initEvents: function() 
27966     {
27967         Roo.bootstrap.Element.superclass.initEvents.call(this);
27968         
27969         this.iconEl = this.el.select('i', true).first();
27970         
27971         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27972         
27973         Roo.bootstrap.FieldLabel.register(this);
27974     },
27975     
27976     /**
27977      * Mark this field as valid
27978      */
27979     markValid : function()
27980     {
27981         this.iconEl.show();
27982         
27983         this.iconEl.removeClass(this.invalidClass);
27984         
27985         this.iconEl.addClass(this.validClass);
27986         
27987         this.fireEvent('valid', this);
27988     },
27989     
27990     /**
27991      * Mark this field as invalid
27992      * @param {String} msg The validation message
27993      */
27994     markInvalid : function(msg)
27995     {
27996         this.iconEl.show();
27997         
27998         this.iconEl.removeClass(this.validClass);
27999         
28000         this.iconEl.addClass(this.invalidClass);
28001         
28002         this.fireEvent('invalid', this, msg);
28003     }
28004     
28005    
28006 });
28007
28008 Roo.apply(Roo.bootstrap.FieldLabel, {
28009     
28010     groups: {},
28011     
28012      /**
28013     * register a FieldLabel Group
28014     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28015     */
28016     register : function(label)
28017     {
28018         if(this.groups.hasOwnProperty(label.target)){
28019             return;
28020         }
28021      
28022         this.groups[label.target] = label;
28023         
28024     },
28025     /**
28026     * fetch a FieldLabel Group based on the target
28027     * @param {string} target
28028     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28029     */
28030     get: function(target) {
28031         if (typeof(this.groups[target]) == 'undefined') {
28032             return false;
28033         }
28034         
28035         return this.groups[target] ;
28036     }
28037 });
28038
28039  
28040
28041  /*
28042  * - LGPL
28043  *
28044  * page DateSplitField.
28045  * 
28046  */
28047
28048
28049 /**
28050  * @class Roo.bootstrap.DateSplitField
28051  * @extends Roo.bootstrap.Component
28052  * Bootstrap DateSplitField class
28053  * @cfg {string} fieldLabel - the label associated
28054  * @cfg {Number} labelWidth set the width of label (0-12)
28055  * @cfg {String} labelAlign (top|left)
28056  * @cfg {Boolean} dayAllowBlank (true|false) default false
28057  * @cfg {Boolean} monthAllowBlank (true|false) default false
28058  * @cfg {Boolean} yearAllowBlank (true|false) default false
28059  * @cfg {string} dayPlaceholder 
28060  * @cfg {string} monthPlaceholder
28061  * @cfg {string} yearPlaceholder
28062  * @cfg {string} dayFormat default 'd'
28063  * @cfg {string} monthFormat default 'm'
28064  * @cfg {string} yearFormat default 'Y'
28065
28066  *     
28067  * @constructor
28068  * Create a new DateSplitField
28069  * @param {Object} config The config object
28070  */
28071
28072 Roo.bootstrap.DateSplitField = function(config){
28073     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28074     
28075     this.addEvents({
28076         // raw events
28077          /**
28078          * @event years
28079          * getting the data of years
28080          * @param {Roo.bootstrap.DateSplitField} this
28081          * @param {Object} years
28082          */
28083         "years" : true,
28084         /**
28085          * @event days
28086          * getting the data of days
28087          * @param {Roo.bootstrap.DateSplitField} this
28088          * @param {Object} days
28089          */
28090         "days" : true,
28091         /**
28092          * @event invalid
28093          * Fires after the field has been marked as invalid.
28094          * @param {Roo.form.Field} this
28095          * @param {String} msg The validation message
28096          */
28097         invalid : true,
28098        /**
28099          * @event valid
28100          * Fires after the field has been validated with no errors.
28101          * @param {Roo.form.Field} this
28102          */
28103         valid : true
28104     });
28105 };
28106
28107 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28108     
28109     fieldLabel : '',
28110     labelAlign : 'top',
28111     labelWidth : 3,
28112     dayAllowBlank : false,
28113     monthAllowBlank : false,
28114     yearAllowBlank : false,
28115     dayPlaceholder : '',
28116     monthPlaceholder : '',
28117     yearPlaceholder : '',
28118     dayFormat : 'd',
28119     monthFormat : 'm',
28120     yearFormat : 'Y',
28121     isFormField : true,
28122     
28123     getAutoCreate : function()
28124     {
28125         var cfg = {
28126             tag : 'div',
28127             cls : 'row roo-date-split-field-group',
28128             cn : [
28129                 {
28130                     tag : 'input',
28131                     type : 'hidden',
28132                     cls : 'form-hidden-field roo-date-split-field-group-value',
28133                     name : this.name
28134                 }
28135             ]
28136         };
28137         
28138         if(this.fieldLabel){
28139             cfg.cn.push({
28140                 tag : 'div',
28141                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28142                 cn : [
28143                     {
28144                         tag : 'label',
28145                         html : this.fieldLabel
28146                     }
28147                 ]
28148             });
28149         }
28150         
28151         Roo.each(['day', 'month', 'year'], function(t){
28152             cfg.cn.push({
28153                 tag : 'div',
28154                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28155             });
28156         }, this);
28157         
28158         return cfg;
28159     },
28160     
28161     inputEl: function ()
28162     {
28163         return this.el.select('.roo-date-split-field-group-value', true).first();
28164     },
28165     
28166     onRender : function(ct, position) 
28167     {
28168         var _this = this;
28169         
28170         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28171         
28172         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28173         
28174         this.dayField = new Roo.bootstrap.ComboBox({
28175             allowBlank : this.dayAllowBlank,
28176             alwaysQuery : true,
28177             displayField : 'value',
28178             editable : false,
28179             fieldLabel : '',
28180             forceSelection : true,
28181             mode : 'local',
28182             placeholder : this.dayPlaceholder,
28183             selectOnFocus : true,
28184             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28185             triggerAction : 'all',
28186             typeAhead : true,
28187             valueField : 'value',
28188             store : new Roo.data.SimpleStore({
28189                 data : (function() {    
28190                     var days = [];
28191                     _this.fireEvent('days', _this, days);
28192                     return days;
28193                 })(),
28194                 fields : [ 'value' ]
28195             }),
28196             listeners : {
28197                 select : function (_self, record, index)
28198                 {
28199                     _this.setValue(_this.getValue());
28200                 }
28201             }
28202         });
28203
28204         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28205         
28206         this.monthField = new Roo.bootstrap.MonthField({
28207             after : '<i class=\"fa fa-calendar\"></i>',
28208             allowBlank : this.monthAllowBlank,
28209             placeholder : this.monthPlaceholder,
28210             readOnly : true,
28211             listeners : {
28212                 render : function (_self)
28213                 {
28214                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28215                         e.preventDefault();
28216                         _self.focus();
28217                     });
28218                 },
28219                 select : function (_self, oldvalue, newvalue)
28220                 {
28221                     _this.setValue(_this.getValue());
28222                 }
28223             }
28224         });
28225         
28226         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28227         
28228         this.yearField = new Roo.bootstrap.ComboBox({
28229             allowBlank : this.yearAllowBlank,
28230             alwaysQuery : true,
28231             displayField : 'value',
28232             editable : false,
28233             fieldLabel : '',
28234             forceSelection : true,
28235             mode : 'local',
28236             placeholder : this.yearPlaceholder,
28237             selectOnFocus : true,
28238             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28239             triggerAction : 'all',
28240             typeAhead : true,
28241             valueField : 'value',
28242             store : new Roo.data.SimpleStore({
28243                 data : (function() {
28244                     var years = [];
28245                     _this.fireEvent('years', _this, years);
28246                     return years;
28247                 })(),
28248                 fields : [ 'value' ]
28249             }),
28250             listeners : {
28251                 select : function (_self, record, index)
28252                 {
28253                     _this.setValue(_this.getValue());
28254                 }
28255             }
28256         });
28257
28258         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28259     },
28260     
28261     setValue : function(v, format)
28262     {
28263         this.inputEl.dom.value = v;
28264         
28265         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28266         
28267         var d = Date.parseDate(v, f);
28268         
28269         if(!d){
28270             this.validate();
28271             return;
28272         }
28273         
28274         this.setDay(d.format(this.dayFormat));
28275         this.setMonth(d.format(this.monthFormat));
28276         this.setYear(d.format(this.yearFormat));
28277         
28278         this.validate();
28279         
28280         return;
28281     },
28282     
28283     setDay : function(v)
28284     {
28285         this.dayField.setValue(v);
28286         this.inputEl.dom.value = this.getValue();
28287         this.validate();
28288         return;
28289     },
28290     
28291     setMonth : function(v)
28292     {
28293         this.monthField.setValue(v, true);
28294         this.inputEl.dom.value = this.getValue();
28295         this.validate();
28296         return;
28297     },
28298     
28299     setYear : function(v)
28300     {
28301         this.yearField.setValue(v);
28302         this.inputEl.dom.value = this.getValue();
28303         this.validate();
28304         return;
28305     },
28306     
28307     getDay : function()
28308     {
28309         return this.dayField.getValue();
28310     },
28311     
28312     getMonth : function()
28313     {
28314         return this.monthField.getValue();
28315     },
28316     
28317     getYear : function()
28318     {
28319         return this.yearField.getValue();
28320     },
28321     
28322     getValue : function()
28323     {
28324         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28325         
28326         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28327         
28328         return date;
28329     },
28330     
28331     reset : function()
28332     {
28333         this.setDay('');
28334         this.setMonth('');
28335         this.setYear('');
28336         this.inputEl.dom.value = '';
28337         this.validate();
28338         return;
28339     },
28340     
28341     validate : function()
28342     {
28343         var d = this.dayField.validate();
28344         var m = this.monthField.validate();
28345         var y = this.yearField.validate();
28346         
28347         var valid = true;
28348         
28349         if(
28350                 (!this.dayAllowBlank && !d) ||
28351                 (!this.monthAllowBlank && !m) ||
28352                 (!this.yearAllowBlank && !y)
28353         ){
28354             valid = false;
28355         }
28356         
28357         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28358             return valid;
28359         }
28360         
28361         if(valid){
28362             this.markValid();
28363             return valid;
28364         }
28365         
28366         this.markInvalid();
28367         
28368         return valid;
28369     },
28370     
28371     markValid : function()
28372     {
28373         
28374         var label = this.el.select('label', true).first();
28375         var icon = this.el.select('i.fa-star', true).first();
28376
28377         if(label && icon){
28378             icon.remove();
28379         }
28380         
28381         this.fireEvent('valid', this);
28382     },
28383     
28384      /**
28385      * Mark this field as invalid
28386      * @param {String} msg The validation message
28387      */
28388     markInvalid : function(msg)
28389     {
28390         
28391         var label = this.el.select('label', true).first();
28392         var icon = this.el.select('i.fa-star', true).first();
28393
28394         if(label && !icon){
28395             this.el.select('.roo-date-split-field-label', true).createChild({
28396                 tag : 'i',
28397                 cls : 'text-danger fa fa-lg fa-star',
28398                 tooltip : 'This field is required',
28399                 style : 'margin-right:5px;'
28400             }, label, true);
28401         }
28402         
28403         this.fireEvent('invalid', this, msg);
28404     },
28405     
28406     clearInvalid : function()
28407     {
28408         var label = this.el.select('label', true).first();
28409         var icon = this.el.select('i.fa-star', true).first();
28410
28411         if(label && icon){
28412             icon.remove();
28413         }
28414         
28415         this.fireEvent('valid', this);
28416     },
28417     
28418     getName: function()
28419     {
28420         return this.name;
28421     }
28422     
28423 });
28424
28425  /**
28426  *
28427  * This is based on 
28428  * http://masonry.desandro.com
28429  *
28430  * The idea is to render all the bricks based on vertical width...
28431  *
28432  * The original code extends 'outlayer' - we might need to use that....
28433  * 
28434  */
28435
28436
28437 /**
28438  * @class Roo.bootstrap.LayoutMasonry
28439  * @extends Roo.bootstrap.Component
28440  * Bootstrap Layout Masonry class
28441  * 
28442  * @constructor
28443  * Create a new Element
28444  * @param {Object} config The config object
28445  */
28446
28447 Roo.bootstrap.LayoutMasonry = function(config){
28448     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28449     
28450     this.bricks = [];
28451     
28452 };
28453
28454 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28455     
28456     /**
28457      * @cfg {Boolean} isLayoutInstant = no animation?
28458      */   
28459     isLayoutInstant : false, // needed?
28460    
28461     /**
28462      * @cfg {Number} boxWidth  width of the columns
28463      */   
28464     boxWidth : 450,
28465     
28466       /**
28467      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28468      */   
28469     boxHeight : 0,
28470     
28471     /**
28472      * @cfg {Number} padWidth padding below box..
28473      */   
28474     padWidth : 10, 
28475     
28476     /**
28477      * @cfg {Number} gutter gutter width..
28478      */   
28479     gutter : 10,
28480     
28481      /**
28482      * @cfg {Number} maxCols maximum number of columns
28483      */   
28484     
28485     maxCols: 0,
28486     
28487     /**
28488      * @cfg {Boolean} isAutoInitial defalut true
28489      */   
28490     isAutoInitial : true, 
28491     
28492     containerWidth: 0,
28493     
28494     /**
28495      * @cfg {Boolean} isHorizontal defalut false
28496      */   
28497     isHorizontal : false, 
28498
28499     currentSize : null,
28500     
28501     tag: 'div',
28502     
28503     cls: '',
28504     
28505     bricks: null, //CompositeElement
28506     
28507     cols : 1,
28508     
28509     _isLayoutInited : false,
28510     
28511 //    isAlternative : false, // only use for vertical layout...
28512     
28513     /**
28514      * @cfg {Number} alternativePadWidth padding below box..
28515      */   
28516     alternativePadWidth : 50, 
28517     
28518     getAutoCreate : function(){
28519         
28520         var cfg = {
28521             tag: this.tag,
28522             cls: 'blog-masonary-wrapper ' + this.cls,
28523             cn : {
28524                 cls : 'mas-boxes masonary'
28525             }
28526         };
28527         
28528         return cfg;
28529     },
28530     
28531     getChildContainer: function( )
28532     {
28533         if (this.boxesEl) {
28534             return this.boxesEl;
28535         }
28536         
28537         this.boxesEl = this.el.select('.mas-boxes').first();
28538         
28539         return this.boxesEl;
28540     },
28541     
28542     
28543     initEvents : function()
28544     {
28545         var _this = this;
28546         
28547         if(this.isAutoInitial){
28548             Roo.log('hook children rendered');
28549             this.on('childrenrendered', function() {
28550                 Roo.log('children rendered');
28551                 _this.initial();
28552             } ,this);
28553         }
28554     },
28555     
28556     initial : function()
28557     {
28558         this.currentSize = this.el.getBox(true);
28559         
28560         Roo.EventManager.onWindowResize(this.resize, this); 
28561
28562         if(!this.isAutoInitial){
28563             this.layout();
28564             return;
28565         }
28566         
28567         this.layout();
28568         
28569         return;
28570         //this.layout.defer(500,this);
28571         
28572     },
28573     
28574     resize : function()
28575     {
28576         Roo.log('resize');
28577         
28578         var cs = this.el.getBox(true);
28579         
28580         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28581             Roo.log("no change in with or X");
28582             return;
28583         }
28584         
28585         this.currentSize = cs;
28586         
28587         this.layout();
28588         
28589     },
28590     
28591     layout : function()
28592     {   
28593         this._resetLayout();
28594         
28595         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28596         
28597         this.layoutItems( isInstant );
28598       
28599         this._isLayoutInited = true;
28600         
28601     },
28602     
28603     _resetLayout : function()
28604     {
28605         if(this.isHorizontal){
28606             this.horizontalMeasureColumns();
28607             return;
28608         }
28609         
28610         this.verticalMeasureColumns();
28611         
28612     },
28613     
28614     verticalMeasureColumns : function()
28615     {
28616         this.getContainerWidth();
28617         
28618 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28619 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28620 //            return;
28621 //        }
28622         
28623         var boxWidth = this.boxWidth + this.padWidth;
28624         
28625         if(this.containerWidth < this.boxWidth){
28626             boxWidth = this.containerWidth
28627         }
28628         
28629         var containerWidth = this.containerWidth;
28630         
28631         var cols = Math.floor(containerWidth / boxWidth);
28632         
28633         this.cols = Math.max( cols, 1 );
28634         
28635         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28636         
28637         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28638         
28639         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28640         
28641         this.colWidth = boxWidth + avail - this.padWidth;
28642         
28643         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28644         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28645     },
28646     
28647     horizontalMeasureColumns : function()
28648     {
28649         this.getContainerWidth();
28650         
28651         var boxWidth = this.boxWidth;
28652         
28653         if(this.containerWidth < boxWidth){
28654             boxWidth = this.containerWidth;
28655         }
28656         
28657         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28658         
28659         this.el.setHeight(boxWidth);
28660         
28661     },
28662     
28663     getContainerWidth : function()
28664     {
28665         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28666     },
28667     
28668     layoutItems : function( isInstant )
28669     {
28670         var items = Roo.apply([], this.bricks);
28671         
28672         if(this.isHorizontal){
28673             this._horizontalLayoutItems( items , isInstant );
28674             return;
28675         }
28676         
28677 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28678 //            this._verticalAlternativeLayoutItems( items , isInstant );
28679 //            return;
28680 //        }
28681         
28682         this._verticalLayoutItems( items , isInstant );
28683         
28684     },
28685     
28686     _verticalLayoutItems : function ( items , isInstant)
28687     {
28688         if ( !items || !items.length ) {
28689             return;
28690         }
28691         
28692         var standard = [
28693             ['xs', 'xs', 'xs', 'tall'],
28694             ['xs', 'xs', 'tall'],
28695             ['xs', 'xs', 'sm'],
28696             ['xs', 'xs', 'xs'],
28697             ['xs', 'tall'],
28698             ['xs', 'sm'],
28699             ['xs', 'xs'],
28700             ['xs'],
28701             
28702             ['sm', 'xs', 'xs'],
28703             ['sm', 'xs'],
28704             ['sm'],
28705             
28706             ['tall', 'xs', 'xs', 'xs'],
28707             ['tall', 'xs', 'xs'],
28708             ['tall', 'xs'],
28709             ['tall']
28710             
28711         ];
28712         
28713         var queue = [];
28714         
28715         var boxes = [];
28716         
28717         var box = [];
28718         
28719         Roo.each(items, function(item, k){
28720             
28721             switch (item.size) {
28722                 // these layouts take up a full box,
28723                 case 'md' :
28724                 case 'md-left' :
28725                 case 'md-right' :
28726                 case 'wide' :
28727                     
28728                     if(box.length){
28729                         boxes.push(box);
28730                         box = [];
28731                     }
28732                     
28733                     boxes.push([item]);
28734                     
28735                     break;
28736                     
28737                 case 'xs' :
28738                 case 'sm' :
28739                 case 'tall' :
28740                     
28741                     box.push(item);
28742                     
28743                     break;
28744                 default :
28745                     break;
28746                     
28747             }
28748             
28749         }, this);
28750         
28751         if(box.length){
28752             boxes.push(box);
28753             box = [];
28754         }
28755         
28756         var filterPattern = function(box, length)
28757         {
28758             if(!box.length){
28759                 return;
28760             }
28761             
28762             var match = false;
28763             
28764             var pattern = box.slice(0, length);
28765             
28766             var format = [];
28767             
28768             Roo.each(pattern, function(i){
28769                 format.push(i.size);
28770             }, this);
28771             
28772             Roo.each(standard, function(s){
28773                 
28774                 if(String(s) != String(format)){
28775                     return;
28776                 }
28777                 
28778                 match = true;
28779                 return false;
28780                 
28781             }, this);
28782             
28783             if(!match && length == 1){
28784                 return;
28785             }
28786             
28787             if(!match){
28788                 filterPattern(box, length - 1);
28789                 return;
28790             }
28791                 
28792             queue.push(pattern);
28793
28794             box = box.slice(length, box.length);
28795
28796             filterPattern(box, 4);
28797
28798             return;
28799             
28800         }
28801         
28802         Roo.each(boxes, function(box, k){
28803             
28804             if(!box.length){
28805                 return;
28806             }
28807             
28808             if(box.length == 1){
28809                 queue.push(box);
28810                 return;
28811             }
28812             
28813             filterPattern(box, 4);
28814             
28815         }, this);
28816         
28817         this._processVerticalLayoutQueue( queue, isInstant );
28818         
28819     },
28820     
28821 //    _verticalAlternativeLayoutItems : function( items , isInstant )
28822 //    {
28823 //        if ( !items || !items.length ) {
28824 //            return;
28825 //        }
28826 //
28827 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
28828 //        
28829 //    },
28830     
28831     _horizontalLayoutItems : function ( items , isInstant)
28832     {
28833         if ( !items || !items.length || items.length < 3) {
28834             return;
28835         }
28836         
28837         items.reverse();
28838         
28839         var eItems = items.slice(0, 3);
28840         
28841         items = items.slice(3, items.length);
28842         
28843         var standard = [
28844             ['xs', 'xs', 'xs', 'wide'],
28845             ['xs', 'xs', 'wide'],
28846             ['xs', 'xs', 'sm'],
28847             ['xs', 'xs', 'xs'],
28848             ['xs', 'wide'],
28849             ['xs', 'sm'],
28850             ['xs', 'xs'],
28851             ['xs'],
28852             
28853             ['sm', 'xs', 'xs'],
28854             ['sm', 'xs'],
28855             ['sm'],
28856             
28857             ['wide', 'xs', 'xs', 'xs'],
28858             ['wide', 'xs', 'xs'],
28859             ['wide', 'xs'],
28860             ['wide'],
28861             
28862             ['wide-thin']
28863         ];
28864         
28865         var queue = [];
28866         
28867         var boxes = [];
28868         
28869         var box = [];
28870         
28871         Roo.each(items, function(item, k){
28872             
28873             switch (item.size) {
28874                 case 'md' :
28875                 case 'md-left' :
28876                 case 'md-right' :
28877                 case 'tall' :
28878                     
28879                     if(box.length){
28880                         boxes.push(box);
28881                         box = [];
28882                     }
28883                     
28884                     boxes.push([item]);
28885                     
28886                     break;
28887                     
28888                 case 'xs' :
28889                 case 'sm' :
28890                 case 'wide' :
28891                 case 'wide-thin' :
28892                     
28893                     box.push(item);
28894                     
28895                     break;
28896                 default :
28897                     break;
28898                     
28899             }
28900             
28901         }, this);
28902         
28903         if(box.length){
28904             boxes.push(box);
28905             box = [];
28906         }
28907         
28908         var filterPattern = function(box, length)
28909         {
28910             if(!box.length){
28911                 return;
28912             }
28913             
28914             var match = false;
28915             
28916             var pattern = box.slice(0, length);
28917             
28918             var format = [];
28919             
28920             Roo.each(pattern, function(i){
28921                 format.push(i.size);
28922             }, this);
28923             
28924             Roo.each(standard, function(s){
28925                 
28926                 if(String(s) != String(format)){
28927                     return;
28928                 }
28929                 
28930                 match = true;
28931                 return false;
28932                 
28933             }, this);
28934             
28935             if(!match && length == 1){
28936                 return;
28937             }
28938             
28939             if(!match){
28940                 filterPattern(box, length - 1);
28941                 return;
28942             }
28943                 
28944             queue.push(pattern);
28945
28946             box = box.slice(length, box.length);
28947
28948             filterPattern(box, 4);
28949
28950             return;
28951             
28952         }
28953         
28954         Roo.each(boxes, function(box, k){
28955             
28956             if(!box.length){
28957                 return;
28958             }
28959             
28960             if(box.length == 1){
28961                 queue.push(box);
28962                 return;
28963             }
28964             
28965             filterPattern(box, 4);
28966             
28967         }, this);
28968         
28969         
28970         var prune = [];
28971         
28972         var pos = this.el.getBox(true);
28973         
28974         var minX = pos.x;
28975         
28976         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
28977         
28978         var hit_end = false;
28979         
28980         Roo.each(queue, function(box){
28981             
28982             if(hit_end){
28983                 
28984                 Roo.each(box, function(b){
28985                 
28986                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
28987                     b.el.hide();
28988
28989                 }, this);
28990
28991                 return;
28992             }
28993             
28994             var mx = 0;
28995             
28996             Roo.each(box, function(b){
28997                 
28998                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
28999                 b.el.show();
29000
29001                 mx = Math.max(mx, b.x);
29002                 
29003             }, this);
29004             
29005             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29006             
29007             if(maxX < minX){
29008                 
29009                 Roo.each(box, function(b){
29010                 
29011                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29012                     b.el.hide();
29013                     
29014                 }, this);
29015                 
29016                 hit_end = true;
29017                 
29018                 return;
29019             }
29020             
29021             prune.push(box);
29022             
29023         }, this);
29024         
29025         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29026     },
29027     
29028     /** Sets position of item in DOM
29029     * @param {Element} item
29030     * @param {Number} x - horizontal position
29031     * @param {Number} y - vertical position
29032     * @param {Boolean} isInstant - disables transitions
29033     */
29034     _processVerticalLayoutQueue : function( queue, isInstant )
29035     {
29036         var pos = this.el.getBox(true);
29037         var x = pos.x;
29038         var y = pos.y;
29039         var maxY = [];
29040         
29041         for (var i = 0; i < this.cols; i++){
29042             maxY[i] = pos.y;
29043         }
29044         
29045         Roo.each(queue, function(box, k){
29046             
29047             var col = k % this.cols;
29048             
29049             Roo.each(box, function(b,kk){
29050                 
29051                 b.el.position('absolute');
29052                 
29053                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29054                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29055                 
29056                 if(b.size == 'md-left' || b.size == 'md-right'){
29057                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29058                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29059                 }
29060                 
29061                 b.el.setWidth(width);
29062                 b.el.setHeight(height);
29063                 
29064             }, this);
29065             
29066             for (var i = 0; i < this.cols; i++){
29067                 
29068                 if(maxY[i] < maxY[col]){
29069                     col = i;
29070                     continue;
29071                 }
29072                 
29073                 col = Math.min(col, i);
29074                 
29075             }
29076             
29077             x = pos.x + col * (this.colWidth + this.padWidth);
29078             
29079             y = maxY[col];
29080             
29081             var positions = [];
29082             
29083             switch (box.length){
29084                 case 1 :
29085                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29086                     break;
29087                 case 2 :
29088                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29089                     break;
29090                 case 3 :
29091                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29092                     break;
29093                 case 4 :
29094                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29095                     break;
29096                 default :
29097                     break;
29098             }
29099             
29100             Roo.each(box, function(b,kk){
29101                 
29102                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29103                 
29104                 var sz = b.el.getSize();
29105                 
29106                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29107                 
29108             }, this);
29109             
29110         }, this);
29111         
29112         var mY = 0;
29113         
29114         for (var i = 0; i < this.cols; i++){
29115             mY = Math.max(mY, maxY[i]);
29116         }
29117         
29118         this.el.setHeight(mY - pos.y);
29119         
29120     },
29121     
29122 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29123 //    {
29124 //        var pos = this.el.getBox(true);
29125 //        var x = pos.x;
29126 //        var y = pos.y;
29127 //        var maxX = pos.right;
29128 //        
29129 //        var maxHeight = 0;
29130 //        
29131 //        Roo.each(items, function(item, k){
29132 //            
29133 //            var c = k % 2;
29134 //            
29135 //            item.el.position('absolute');
29136 //                
29137 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29138 //
29139 //            item.el.setWidth(width);
29140 //
29141 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29142 //
29143 //            item.el.setHeight(height);
29144 //            
29145 //            if(c == 0){
29146 //                item.el.setXY([x, y], isInstant ? false : true);
29147 //            } else {
29148 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29149 //            }
29150 //            
29151 //            y = y + height + this.alternativePadWidth;
29152 //            
29153 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29154 //            
29155 //        }, this);
29156 //        
29157 //        this.el.setHeight(maxHeight);
29158 //        
29159 //    },
29160     
29161     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29162     {
29163         var pos = this.el.getBox(true);
29164         
29165         var minX = pos.x;
29166         var minY = pos.y;
29167         
29168         var maxX = pos.right;
29169         
29170         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29171         
29172         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29173         
29174         Roo.each(queue, function(box, k){
29175             
29176             Roo.each(box, function(b, kk){
29177                 
29178                 b.el.position('absolute');
29179                 
29180                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29181                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29182                 
29183                 if(b.size == 'md-left' || b.size == 'md-right'){
29184                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29185                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29186                 }
29187                 
29188                 b.el.setWidth(width);
29189                 b.el.setHeight(height);
29190                 
29191             }, this);
29192             
29193             if(!box.length){
29194                 return;
29195             }
29196             
29197             var positions = [];
29198             
29199             switch (box.length){
29200                 case 1 :
29201                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29202                     break;
29203                 case 2 :
29204                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29205                     break;
29206                 case 3 :
29207                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29208                     break;
29209                 case 4 :
29210                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29211                     break;
29212                 default :
29213                     break;
29214             }
29215             
29216             Roo.each(box, function(b,kk){
29217                 
29218                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29219                 
29220                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29221                 
29222             }, this);
29223             
29224         }, this);
29225         
29226     },
29227     
29228     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29229     {
29230         Roo.each(eItems, function(b,k){
29231             
29232             b.size = (k == 0) ? 'sm' : 'xs';
29233             b.x = (k == 0) ? 2 : 1;
29234             b.y = (k == 0) ? 2 : 1;
29235             
29236             b.el.position('absolute');
29237             
29238             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29239                 
29240             b.el.setWidth(width);
29241             
29242             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29243             
29244             b.el.setHeight(height);
29245             
29246         }, this);
29247
29248         var positions = [];
29249         
29250         positions.push({
29251             x : maxX - this.unitWidth * 2 - this.gutter,
29252             y : minY
29253         });
29254         
29255         positions.push({
29256             x : maxX - this.unitWidth,
29257             y : minY + (this.unitWidth + this.gutter) * 2
29258         });
29259         
29260         positions.push({
29261             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29262             y : minY
29263         });
29264         
29265         Roo.each(eItems, function(b,k){
29266             
29267             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29268
29269         }, this);
29270         
29271     },
29272     
29273     getVerticalOneBoxColPositions : function(x, y, box)
29274     {
29275         var pos = [];
29276         
29277         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29278         
29279         if(box[0].size == 'md-left'){
29280             rand = 0;
29281         }
29282         
29283         if(box[0].size == 'md-right'){
29284             rand = 1;
29285         }
29286         
29287         pos.push({
29288             x : x + (this.unitWidth + this.gutter) * rand,
29289             y : y
29290         });
29291         
29292         return pos;
29293     },
29294     
29295     getVerticalTwoBoxColPositions : function(x, y, box)
29296     {
29297         var pos = [];
29298         
29299         if(box[0].size == 'xs'){
29300             
29301             pos.push({
29302                 x : x,
29303                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29304             });
29305
29306             pos.push({
29307                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29308                 y : y
29309             });
29310             
29311             return pos;
29312             
29313         }
29314         
29315         pos.push({
29316             x : x,
29317             y : y
29318         });
29319
29320         pos.push({
29321             x : x + (this.unitWidth + this.gutter) * 2,
29322             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29323         });
29324         
29325         return pos;
29326         
29327     },
29328     
29329     getVerticalThreeBoxColPositions : function(x, y, box)
29330     {
29331         var pos = [];
29332         
29333         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29334             
29335             pos.push({
29336                 x : x,
29337                 y : y
29338             });
29339
29340             pos.push({
29341                 x : x + (this.unitWidth + this.gutter) * 1,
29342                 y : y
29343             });
29344             
29345             pos.push({
29346                 x : x + (this.unitWidth + this.gutter) * 2,
29347                 y : y
29348             });
29349             
29350             return pos;
29351             
29352         }
29353         
29354         if(box[0].size == 'xs' && box[1].size == 'xs'){
29355             
29356             pos.push({
29357                 x : x,
29358                 y : y
29359             });
29360
29361             pos.push({
29362                 x : x,
29363                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29364             });
29365             
29366             pos.push({
29367                 x : x + (this.unitWidth + this.gutter) * 1,
29368                 y : y
29369             });
29370             
29371             return pos;
29372             
29373         }
29374         
29375         pos.push({
29376             x : x,
29377             y : y
29378         });
29379
29380         pos.push({
29381             x : x + (this.unitWidth + this.gutter) * 2,
29382             y : y
29383         });
29384
29385         pos.push({
29386             x : x + (this.unitWidth + this.gutter) * 2,
29387             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29388         });
29389             
29390         return pos;
29391         
29392     },
29393     
29394     getVerticalFourBoxColPositions : function(x, y, box)
29395     {
29396         var pos = [];
29397         
29398         if(box[0].size == 'xs'){
29399             
29400             pos.push({
29401                 x : x,
29402                 y : y
29403             });
29404
29405             pos.push({
29406                 x : x,
29407                 y : y + (this.unitHeight + this.gutter) * 1
29408             });
29409             
29410             pos.push({
29411                 x : x,
29412                 y : y + (this.unitHeight + this.gutter) * 2
29413             });
29414             
29415             pos.push({
29416                 x : x + (this.unitWidth + this.gutter) * 1,
29417                 y : y
29418             });
29419             
29420             return pos;
29421             
29422         }
29423         
29424         pos.push({
29425             x : x,
29426             y : y
29427         });
29428
29429         pos.push({
29430             x : x + (this.unitWidth + this.gutter) * 2,
29431             y : y
29432         });
29433
29434         pos.push({
29435             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29436             y : y + (this.unitHeight + this.gutter) * 1
29437         });
29438
29439         pos.push({
29440             x : x + (this.unitWidth + this.gutter) * 2,
29441             y : y + (this.unitWidth + this.gutter) * 2
29442         });
29443
29444         return pos;
29445         
29446     },
29447     
29448     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29449     {
29450         var pos = [];
29451         
29452         if(box[0].size == 'md-left'){
29453             pos.push({
29454                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29455                 y : minY
29456             });
29457             
29458             return pos;
29459         }
29460         
29461         if(box[0].size == 'md-right'){
29462             pos.push({
29463                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29464                 y : minY + (this.unitWidth + this.gutter) * 1
29465             });
29466             
29467             return pos;
29468         }
29469         
29470         var rand = Math.floor(Math.random() * (4 - box[0].y));
29471         
29472         pos.push({
29473             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29474             y : minY + (this.unitWidth + this.gutter) * rand
29475         });
29476         
29477         return pos;
29478         
29479     },
29480     
29481     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29482     {
29483         var pos = [];
29484         
29485         if(box[0].size == 'xs'){
29486             
29487             pos.push({
29488                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29489                 y : minY
29490             });
29491
29492             pos.push({
29493                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29494                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29495             });
29496             
29497             return pos;
29498             
29499         }
29500         
29501         pos.push({
29502             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29503             y : minY
29504         });
29505
29506         pos.push({
29507             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29508             y : minY + (this.unitWidth + this.gutter) * 2
29509         });
29510         
29511         return pos;
29512         
29513     },
29514     
29515     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29516     {
29517         var pos = [];
29518         
29519         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29520             
29521             pos.push({
29522                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29523                 y : minY
29524             });
29525
29526             pos.push({
29527                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29528                 y : minY + (this.unitWidth + this.gutter) * 1
29529             });
29530             
29531             pos.push({
29532                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29533                 y : minY + (this.unitWidth + this.gutter) * 2
29534             });
29535             
29536             return pos;
29537             
29538         }
29539         
29540         if(box[0].size == 'xs' && box[1].size == 'xs'){
29541             
29542             pos.push({
29543                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29544                 y : minY
29545             });
29546
29547             pos.push({
29548                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29549                 y : minY
29550             });
29551             
29552             pos.push({
29553                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29554                 y : minY + (this.unitWidth + this.gutter) * 1
29555             });
29556             
29557             return pos;
29558             
29559         }
29560         
29561         pos.push({
29562             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29563             y : minY
29564         });
29565
29566         pos.push({
29567             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29568             y : minY + (this.unitWidth + this.gutter) * 2
29569         });
29570
29571         pos.push({
29572             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29573             y : minY + (this.unitWidth + this.gutter) * 2
29574         });
29575             
29576         return pos;
29577         
29578     },
29579     
29580     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29581     {
29582         var pos = [];
29583         
29584         if(box[0].size == 'xs'){
29585             
29586             pos.push({
29587                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29588                 y : minY
29589             });
29590
29591             pos.push({
29592                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29593                 y : minY
29594             });
29595             
29596             pos.push({
29597                 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),
29598                 y : minY
29599             });
29600             
29601             pos.push({
29602                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29603                 y : minY + (this.unitWidth + this.gutter) * 1
29604             });
29605             
29606             return pos;
29607             
29608         }
29609         
29610         pos.push({
29611             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29612             y : minY
29613         });
29614         
29615         pos.push({
29616             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29617             y : minY + (this.unitWidth + this.gutter) * 2
29618         });
29619         
29620         pos.push({
29621             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29622             y : minY + (this.unitWidth + this.gutter) * 2
29623         });
29624         
29625         pos.push({
29626             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),
29627             y : minY + (this.unitWidth + this.gutter) * 2
29628         });
29629
29630         return pos;
29631         
29632     }
29633     
29634 });
29635
29636  
29637
29638  /**
29639  *
29640  * This is based on 
29641  * http://masonry.desandro.com
29642  *
29643  * The idea is to render all the bricks based on vertical width...
29644  *
29645  * The original code extends 'outlayer' - we might need to use that....
29646  * 
29647  */
29648
29649
29650 /**
29651  * @class Roo.bootstrap.LayoutMasonryAuto
29652  * @extends Roo.bootstrap.Component
29653  * Bootstrap Layout Masonry class
29654  * 
29655  * @constructor
29656  * Create a new Element
29657  * @param {Object} config The config object
29658  */
29659
29660 Roo.bootstrap.LayoutMasonryAuto = function(config){
29661     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29662 };
29663
29664 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
29665     
29666       /**
29667      * @cfg {Boolean} isFitWidth  - resize the width..
29668      */   
29669     isFitWidth : false,  // options..
29670     /**
29671      * @cfg {Boolean} isOriginLeft = left align?
29672      */   
29673     isOriginLeft : true,
29674     /**
29675      * @cfg {Boolean} isOriginTop = top align?
29676      */   
29677     isOriginTop : false,
29678     /**
29679      * @cfg {Boolean} isLayoutInstant = no animation?
29680      */   
29681     isLayoutInstant : false, // needed?
29682     /**
29683      * @cfg {Boolean} isResizingContainer = not sure if this is used..
29684      */   
29685     isResizingContainer : true,
29686     /**
29687      * @cfg {Number} columnWidth  width of the columns 
29688      */   
29689     
29690     columnWidth : 0,
29691     
29692     /**
29693      * @cfg {Number} maxCols maximum number of columns
29694      */   
29695     
29696     maxCols: 0,
29697     /**
29698      * @cfg {Number} padHeight padding below box..
29699      */   
29700     
29701     padHeight : 10, 
29702     
29703     /**
29704      * @cfg {Boolean} isAutoInitial defalut true
29705      */   
29706     
29707     isAutoInitial : true, 
29708     
29709     // private?
29710     gutter : 0,
29711     
29712     containerWidth: 0,
29713     initialColumnWidth : 0,
29714     currentSize : null,
29715     
29716     colYs : null, // array.
29717     maxY : 0,
29718     padWidth: 10,
29719     
29720     
29721     tag: 'div',
29722     cls: '',
29723     bricks: null, //CompositeElement
29724     cols : 0, // array?
29725     // element : null, // wrapped now this.el
29726     _isLayoutInited : null, 
29727     
29728     
29729     getAutoCreate : function(){
29730         
29731         var cfg = {
29732             tag: this.tag,
29733             cls: 'blog-masonary-wrapper ' + this.cls,
29734             cn : {
29735                 cls : 'mas-boxes masonary'
29736             }
29737         };
29738         
29739         return cfg;
29740     },
29741     
29742     getChildContainer: function( )
29743     {
29744         if (this.boxesEl) {
29745             return this.boxesEl;
29746         }
29747         
29748         this.boxesEl = this.el.select('.mas-boxes').first();
29749         
29750         return this.boxesEl;
29751     },
29752     
29753     
29754     initEvents : function()
29755     {
29756         var _this = this;
29757         
29758         if(this.isAutoInitial){
29759             Roo.log('hook children rendered');
29760             this.on('childrenrendered', function() {
29761                 Roo.log('children rendered');
29762                 _this.initial();
29763             } ,this);
29764         }
29765         
29766     },
29767     
29768     initial : function()
29769     {
29770         this.reloadItems();
29771
29772         this.currentSize = this.el.getBox(true);
29773
29774         /// was window resize... - let's see if this works..
29775         Roo.EventManager.onWindowResize(this.resize, this); 
29776
29777         if(!this.isAutoInitial){
29778             this.layout();
29779             return;
29780         }
29781         
29782         this.layout.defer(500,this);
29783     },
29784     
29785     reloadItems: function()
29786     {
29787         this.bricks = this.el.select('.masonry-brick', true);
29788         
29789         this.bricks.each(function(b) {
29790             //Roo.log(b.getSize());
29791             if (!b.attr('originalwidth')) {
29792                 b.attr('originalwidth',  b.getSize().width);
29793             }
29794             
29795         });
29796         
29797         Roo.log(this.bricks.elements.length);
29798     },
29799     
29800     resize : function()
29801     {
29802         Roo.log('resize');
29803         var cs = this.el.getBox(true);
29804         
29805         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29806             Roo.log("no change in with or X");
29807             return;
29808         }
29809         this.currentSize = cs;
29810         this.layout();
29811     },
29812     
29813     layout : function()
29814     {
29815          Roo.log('layout');
29816         this._resetLayout();
29817         //this._manageStamps();
29818       
29819         // don't animate first layout
29820         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29821         this.layoutItems( isInstant );
29822       
29823         // flag for initalized
29824         this._isLayoutInited = true;
29825     },
29826     
29827     layoutItems : function( isInstant )
29828     {
29829         //var items = this._getItemsForLayout( this.items );
29830         // original code supports filtering layout items.. we just ignore it..
29831         
29832         this._layoutItems( this.bricks , isInstant );
29833       
29834         this._postLayout();
29835     },
29836     _layoutItems : function ( items , isInstant)
29837     {
29838        //this.fireEvent( 'layout', this, items );
29839     
29840
29841         if ( !items || !items.elements.length ) {
29842           // no items, emit event with empty array
29843             return;
29844         }
29845
29846         var queue = [];
29847         items.each(function(item) {
29848             Roo.log("layout item");
29849             Roo.log(item);
29850             // get x/y object from method
29851             var position = this._getItemLayoutPosition( item );
29852             // enqueue
29853             position.item = item;
29854             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29855             queue.push( position );
29856         }, this);
29857       
29858         this._processLayoutQueue( queue );
29859     },
29860     /** Sets position of item in DOM
29861     * @param {Element} item
29862     * @param {Number} x - horizontal position
29863     * @param {Number} y - vertical position
29864     * @param {Boolean} isInstant - disables transitions
29865     */
29866     _processLayoutQueue : function( queue )
29867     {
29868         for ( var i=0, len = queue.length; i < len; i++ ) {
29869             var obj = queue[i];
29870             obj.item.position('absolute');
29871             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29872         }
29873     },
29874       
29875     
29876     /**
29877     * Any logic you want to do after each layout,
29878     * i.e. size the container
29879     */
29880     _postLayout : function()
29881     {
29882         this.resizeContainer();
29883     },
29884     
29885     resizeContainer : function()
29886     {
29887         if ( !this.isResizingContainer ) {
29888             return;
29889         }
29890         var size = this._getContainerSize();
29891         if ( size ) {
29892             this.el.setSize(size.width,size.height);
29893             this.boxesEl.setSize(size.width,size.height);
29894         }
29895     },
29896     
29897     
29898     
29899     _resetLayout : function()
29900     {
29901         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
29902         this.colWidth = this.el.getWidth();
29903         //this.gutter = this.el.getWidth(); 
29904         
29905         this.measureColumns();
29906
29907         // reset column Y
29908         var i = this.cols;
29909         this.colYs = [];
29910         while (i--) {
29911             this.colYs.push( 0 );
29912         }
29913     
29914         this.maxY = 0;
29915     },
29916
29917     measureColumns : function()
29918     {
29919         this.getContainerWidth();
29920       // if columnWidth is 0, default to outerWidth of first item
29921         if ( !this.columnWidth ) {
29922             var firstItem = this.bricks.first();
29923             Roo.log(firstItem);
29924             this.columnWidth  = this.containerWidth;
29925             if (firstItem && firstItem.attr('originalwidth') ) {
29926                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
29927             }
29928             // columnWidth fall back to item of first element
29929             Roo.log("set column width?");
29930                         this.initialColumnWidth = this.columnWidth  ;
29931
29932             // if first elem has no width, default to size of container
29933             
29934         }
29935         
29936         
29937         if (this.initialColumnWidth) {
29938             this.columnWidth = this.initialColumnWidth;
29939         }
29940         
29941         
29942             
29943         // column width is fixed at the top - however if container width get's smaller we should
29944         // reduce it...
29945         
29946         // this bit calcs how man columns..
29947             
29948         var columnWidth = this.columnWidth += this.gutter;
29949       
29950         // calculate columns
29951         var containerWidth = this.containerWidth + this.gutter;
29952         
29953         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
29954         // fix rounding errors, typically with gutters
29955         var excess = columnWidth - containerWidth % columnWidth;
29956         
29957         
29958         // if overshoot is less than a pixel, round up, otherwise floor it
29959         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
29960         cols = Math[ mathMethod ]( cols );
29961         this.cols = Math.max( cols, 1 );
29962         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29963         
29964          // padding positioning..
29965         var totalColWidth = this.cols * this.columnWidth;
29966         var padavail = this.containerWidth - totalColWidth;
29967         // so for 2 columns - we need 3 'pads'
29968         
29969         var padNeeded = (1+this.cols) * this.padWidth;
29970         
29971         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
29972         
29973         this.columnWidth += padExtra
29974         //this.padWidth = Math.floor(padavail /  ( this.cols));
29975         
29976         // adjust colum width so that padding is fixed??
29977         
29978         // we have 3 columns ... total = width * 3
29979         // we have X left over... that should be used by 
29980         
29981         //if (this.expandC) {
29982             
29983         //}
29984         
29985         
29986         
29987     },
29988     
29989     getContainerWidth : function()
29990     {
29991        /* // container is parent if fit width
29992         var container = this.isFitWidth ? this.element.parentNode : this.element;
29993         // check that this.size and size are there
29994         // IE8 triggers resize on body size change, so they might not be
29995         
29996         var size = getSize( container );  //FIXME
29997         this.containerWidth = size && size.innerWidth; //FIXME
29998         */
29999          
30000         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30001         
30002     },
30003     
30004     _getItemLayoutPosition : function( item )  // what is item?
30005     {
30006         // we resize the item to our columnWidth..
30007       
30008         item.setWidth(this.columnWidth);
30009         item.autoBoxAdjust  = false;
30010         
30011         var sz = item.getSize();
30012  
30013         // how many columns does this brick span
30014         var remainder = this.containerWidth % this.columnWidth;
30015         
30016         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30017         // round if off by 1 pixel, otherwise use ceil
30018         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30019         colSpan = Math.min( colSpan, this.cols );
30020         
30021         // normally this should be '1' as we dont' currently allow multi width columns..
30022         
30023         var colGroup = this._getColGroup( colSpan );
30024         // get the minimum Y value from the columns
30025         var minimumY = Math.min.apply( Math, colGroup );
30026         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30027         
30028         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30029          
30030         // position the brick
30031         var position = {
30032             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30033             y: this.currentSize.y + minimumY + this.padHeight
30034         };
30035         
30036         Roo.log(position);
30037         // apply setHeight to necessary columns
30038         var setHeight = minimumY + sz.height + this.padHeight;
30039         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30040         
30041         var setSpan = this.cols + 1 - colGroup.length;
30042         for ( var i = 0; i < setSpan; i++ ) {
30043           this.colYs[ shortColIndex + i ] = setHeight ;
30044         }
30045       
30046         return position;
30047     },
30048     
30049     /**
30050      * @param {Number} colSpan - number of columns the element spans
30051      * @returns {Array} colGroup
30052      */
30053     _getColGroup : function( colSpan )
30054     {
30055         if ( colSpan < 2 ) {
30056           // if brick spans only one column, use all the column Ys
30057           return this.colYs;
30058         }
30059       
30060         var colGroup = [];
30061         // how many different places could this brick fit horizontally
30062         var groupCount = this.cols + 1 - colSpan;
30063         // for each group potential horizontal position
30064         for ( var i = 0; i < groupCount; i++ ) {
30065           // make an array of colY values for that one group
30066           var groupColYs = this.colYs.slice( i, i + colSpan );
30067           // and get the max value of the array
30068           colGroup[i] = Math.max.apply( Math, groupColYs );
30069         }
30070         return colGroup;
30071     },
30072     /*
30073     _manageStamp : function( stamp )
30074     {
30075         var stampSize =  stamp.getSize();
30076         var offset = stamp.getBox();
30077         // get the columns that this stamp affects
30078         var firstX = this.isOriginLeft ? offset.x : offset.right;
30079         var lastX = firstX + stampSize.width;
30080         var firstCol = Math.floor( firstX / this.columnWidth );
30081         firstCol = Math.max( 0, firstCol );
30082         
30083         var lastCol = Math.floor( lastX / this.columnWidth );
30084         // lastCol should not go over if multiple of columnWidth #425
30085         lastCol -= lastX % this.columnWidth ? 0 : 1;
30086         lastCol = Math.min( this.cols - 1, lastCol );
30087         
30088         // set colYs to bottom of the stamp
30089         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30090             stampSize.height;
30091             
30092         for ( var i = firstCol; i <= lastCol; i++ ) {
30093           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30094         }
30095     },
30096     */
30097     
30098     _getContainerSize : function()
30099     {
30100         this.maxY = Math.max.apply( Math, this.colYs );
30101         var size = {
30102             height: this.maxY
30103         };
30104       
30105         if ( this.isFitWidth ) {
30106             size.width = this._getContainerFitWidth();
30107         }
30108       
30109         return size;
30110     },
30111     
30112     _getContainerFitWidth : function()
30113     {
30114         var unusedCols = 0;
30115         // count unused columns
30116         var i = this.cols;
30117         while ( --i ) {
30118           if ( this.colYs[i] !== 0 ) {
30119             break;
30120           }
30121           unusedCols++;
30122         }
30123         // fit container to columns that have been used
30124         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30125     },
30126     
30127     needsResizeLayout : function()
30128     {
30129         var previousWidth = this.containerWidth;
30130         this.getContainerWidth();
30131         return previousWidth !== this.containerWidth;
30132     }
30133  
30134 });
30135
30136  
30137
30138  /*
30139  * - LGPL
30140  *
30141  * element
30142  * 
30143  */
30144
30145 /**
30146  * @class Roo.bootstrap.MasonryBrick
30147  * @extends Roo.bootstrap.Component
30148  * Bootstrap MasonryBrick class
30149  * 
30150  * @constructor
30151  * Create a new MasonryBrick
30152  * @param {Object} config The config object
30153  */
30154
30155 Roo.bootstrap.MasonryBrick = function(config){
30156     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30157     
30158     this.addEvents({
30159         // raw events
30160         /**
30161          * @event click
30162          * When a MasonryBrick is clcik
30163          * @param {Roo.bootstrap.MasonryBrick} this
30164          * @param {Roo.EventObject} e
30165          */
30166         "click" : true
30167     });
30168 };
30169
30170 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30171     
30172     /**
30173      * @cfg {String} title
30174      */   
30175     title : '',
30176     /**
30177      * @cfg {String} html
30178      */   
30179     html : '',
30180     /**
30181      * @cfg {String} bgimage
30182      */   
30183     bgimage : '',
30184     /**
30185      * @cfg {String} cls
30186      */   
30187     cls : '',
30188     /**
30189      * @cfg {String} href
30190      */   
30191     href : '',
30192     /**
30193      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30194      */   
30195     size : 'xs',
30196     
30197     /**
30198      * @cfg {String} (center|bottom) placetitle
30199      */   
30200     placetitle : '',
30201     
30202     getAutoCreate : function()
30203     {
30204         var cls = 'masonry-brick';
30205         
30206         if(this.href.length){
30207             cls += ' masonry-brick-link';
30208         }
30209         
30210         if(this.bgimage.length){
30211             cls += ' masonry-brick-image';
30212         }
30213         
30214         if(this.size){
30215             cls += ' masonry-' + this.size + '-brick';
30216         }
30217         
30218         if(this.placetitle.length){
30219             
30220             switch (this.placetitle) {
30221                 case 'center' :
30222                     cls += ' masonry-center-title';
30223                     break;
30224                 case 'bottom' :
30225                     cls += ' masonry-bottom-title';
30226                     break;
30227                 default:
30228                     break;
30229             }
30230             
30231         } else {
30232             if(!this.html.length && !this.bgimage.length){
30233                 cls += ' masonry-center-title';
30234             }
30235
30236             if(!this.html.length && this.bgimage.length){
30237                 cls += ' masonry-bottom-title';
30238             }
30239         }
30240         
30241         if(this.cls){
30242             cls += ' ' + this.cls;
30243         }
30244         
30245         var cfg = {
30246             tag: (this.href.length) ? 'a' : 'div',
30247             cls: cls,
30248             cn: [
30249                 {
30250                     tag: 'div',
30251                     cls: 'masonry-brick-paragraph',
30252                     cn: []
30253                 }
30254             ]
30255         };
30256         
30257         if(this.href.length){
30258             cfg.href = this.href;
30259         }
30260         
30261         var cn = cfg.cn[0].cn;
30262         
30263         if(this.title.length){
30264             cn.push({
30265                 tag: 'h4',
30266                 cls: 'masonry-brick-title',
30267                 html: this.title
30268             });
30269         }
30270         
30271         if(this.html.length){
30272             cn.push({
30273                 tag: 'p',
30274                 cls: 'masonry-brick-text',
30275                 html: this.html
30276             });
30277         }
30278         
30279         if(this.bgimage.length){
30280             cfg.cn.push({
30281                 tag: 'img',
30282                 cls: 'masonry-brick-image-view',
30283                 src: this.bgimage
30284             });
30285         }
30286         
30287         return cfg;
30288         
30289     },
30290     
30291     initEvents: function() 
30292     {
30293         switch (this.size) {
30294             case 'xs' :
30295 //                this.intSize = 1;
30296                 this.x = 1;
30297                 this.y = 1;
30298                 break;
30299             case 'sm' :
30300 //                this.intSize = 2;
30301                 this.x = 2;
30302                 this.y = 2;
30303                 break;
30304             case 'md' :
30305             case 'md-left' :
30306             case 'md-right' :
30307 //                this.intSize = 3;
30308                 this.x = 3;
30309                 this.y = 3;
30310                 break;
30311             case 'tall' :
30312 //                this.intSize = 3;
30313                 this.x = 2;
30314                 this.y = 3;
30315                 break;
30316             case 'wide' :
30317 //                this.intSize = 3;
30318                 this.x = 3;
30319                 this.y = 2;
30320                 break;
30321             case 'wide-thin' :
30322 //                this.intSize = 3;
30323                 this.x = 3;
30324                 this.y = 1;
30325                 break;
30326                         
30327             default :
30328                 break;
30329         }
30330         
30331         
30332         
30333         if(Roo.isTouch){
30334             this.el.on('touchstart', this.onTouchStart, this);
30335             this.el.on('touchmove', this.onTouchMove, this);
30336             this.el.on('touchend', this.onTouchEnd, this);
30337             this.el.on('contextmenu', this.onContextMenu, this);
30338         } else {
30339             this.el.on('mouseenter'  ,this.enter, this);
30340             this.el.on('mouseleave', this.leave, this);
30341         }
30342         
30343         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30344             this.parent().bricks.push(this);   
30345         }
30346         
30347     },
30348     
30349     onClick: function(e, el)
30350     {
30351        // alert('click');
30352         
30353         if(!Roo.isTouch){
30354             return;
30355         }
30356         
30357         var time = this.endTimer - this.startTimer;
30358         
30359         //alert(time);
30360         
30361         if(time < 1000){
30362             return;
30363         }
30364         
30365         e.preventDefault();
30366     },
30367     
30368     enter: function(e, el)
30369     {
30370         e.preventDefault();
30371         
30372         if(this.bgimage.length && this.html.length){
30373             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30374         }
30375     },
30376     
30377     leave: function(e, el)
30378     {
30379         e.preventDefault();
30380         
30381         if(this.bgimage.length && this.html.length){
30382             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30383         }
30384     },
30385     
30386     onTouchStart: function(e, el)
30387     {
30388 //        e.preventDefault();
30389         
30390         if(!this.bgimage.length || !this.html.length){
30391             return;
30392         }
30393         
30394         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30395         
30396         this.timer = new Date().getTime();
30397         
30398         this.touchmoved = false;
30399     },
30400     
30401     onTouchMove: function(e, el)
30402     {
30403         this.touchmoved = true;
30404     },
30405     onContextMenu : function(e,el)
30406     {
30407             e.preventDefault();
30408             e.stopPropagation();
30409             return false;
30410     },
30411     
30412     
30413     onTouchEnd: function(e, el)
30414     {
30415 //        e.preventDefault();
30416         
30417         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30418             
30419             this.leave(e,el);
30420             
30421             return;
30422         }
30423         
30424         if(!this.bgimage.length || !this.html.length){
30425             
30426             if(this.href.length){
30427                 window.location.href = this.href;
30428             }
30429             
30430             return;
30431         }
30432         
30433         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30434         
30435         window.location.href = this.href;
30436     }
30437     
30438 });
30439
30440  
30441
30442  /*
30443  * - LGPL
30444  *
30445  * element
30446  * 
30447  */
30448
30449 /**
30450  * @class Roo.bootstrap.Brick
30451  * @extends Roo.bootstrap.Component
30452  * Bootstrap Brick class
30453  * 
30454  * @constructor
30455  * Create a new Brick
30456  * @param {Object} config The config object
30457  */
30458
30459 Roo.bootstrap.Brick = function(config){
30460     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30461     
30462     this.addEvents({
30463         // raw events
30464         /**
30465          * @event click
30466          * When a Brick is click
30467          * @param {Roo.bootstrap.Brick} this
30468          * @param {Roo.EventObject} e
30469          */
30470         "click" : true
30471     });
30472 };
30473
30474 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30475     
30476     /**
30477      * @cfg {String} title
30478      */   
30479     title : '',
30480     /**
30481      * @cfg {String} html
30482      */   
30483     html : '',
30484     /**
30485      * @cfg {String} bgimage
30486      */   
30487     bgimage : '',
30488     /**
30489      * @cfg {String} cls
30490      */   
30491     cls : '',
30492     /**
30493      * @cfg {String} href
30494      */   
30495     href : '',
30496     /**
30497      * @cfg {String} video
30498      */   
30499     video : '',
30500     /**
30501      * @cfg {Boolean} square
30502      */   
30503     square : true,
30504     
30505     getAutoCreate : function()
30506     {
30507         var cls = 'roo-brick';
30508         
30509         if(this.href.length){
30510             cls += ' roo-brick-link';
30511         }
30512         
30513         if(this.bgimage.length){
30514             cls += ' roo-brick-image';
30515         }
30516         
30517         if(!this.html.length && !this.bgimage.length){
30518             cls += ' roo-brick-center-title';
30519         }
30520         
30521         if(!this.html.length && this.bgimage.length){
30522             cls += ' roo-brick-bottom-title';
30523         }
30524         
30525         if(this.cls){
30526             cls += ' ' + this.cls;
30527         }
30528         
30529         var cfg = {
30530             tag: (this.href.length) ? 'a' : 'div',
30531             cls: cls,
30532             cn: [
30533                 {
30534                     tag: 'div',
30535                     cls: 'roo-brick-paragraph',
30536                     cn: []
30537                 }
30538             ]
30539         };
30540         
30541         if(this.href.length){
30542             cfg.href = this.href;
30543         }
30544         
30545         var cn = cfg.cn[0].cn;
30546         
30547         if(this.title.length){
30548             cn.push({
30549                 tag: 'h4',
30550                 cls: 'roo-brick-title',
30551                 html: this.title
30552             });
30553         }
30554         
30555         if(this.html.length){
30556             cn.push({
30557                 tag: 'p',
30558                 cls: 'roo-brick-text',
30559                 html: this.html
30560             });
30561         }
30562         
30563         if(this.bgimage.length){
30564             cfg.cn.push({
30565                 tag: 'img',
30566                 cls: 'roo-brick-image-view',
30567                 src: this.bgimage
30568             });
30569         }
30570         
30571         return cfg;
30572     },
30573     
30574     initEvents: function() 
30575     {
30576         if(this.title.length || this.html.length){
30577             this.el.on('mouseenter'  ,this.enter, this);
30578             this.el.on('mouseleave', this.leave, this);
30579         }
30580         
30581         
30582         Roo.EventManager.onWindowResize(this.resize, this); 
30583         
30584         this.resize();
30585     },
30586     
30587     resize : function()
30588     {
30589         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30590         
30591         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30592 //        paragraph.setHeight(paragraph.getWidth());
30593         
30594         if(this.bgimage.length){
30595             var image = this.el.select('.roo-brick-image-view', true).first();
30596             image.setWidth(paragraph.getWidth());
30597             image.setHeight(paragraph.getWidth());
30598         }
30599         
30600     },
30601     
30602     enter: function(e, el)
30603     {
30604         e.preventDefault();
30605         
30606         if(this.bgimage.length){
30607             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30608             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30609         }
30610     },
30611     
30612     leave: function(e, el)
30613     {
30614         e.preventDefault();
30615         
30616         if(this.bgimage.length){
30617             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30618             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30619         }
30620     }
30621     
30622 });
30623
30624  
30625
30626  /*
30627  * Based on:
30628  * Ext JS Library 1.1.1
30629  * Copyright(c) 2006-2007, Ext JS, LLC.
30630  *
30631  * Originally Released Under LGPL - original licence link has changed is not relivant.
30632  *
30633  * Fork - LGPL
30634  * <script type="text/javascript">
30635  */
30636
30637
30638 /**
30639  * @class Roo.bootstrap.SplitBar
30640  * @extends Roo.util.Observable
30641  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30642  * <br><br>
30643  * Usage:
30644  * <pre><code>
30645 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30646                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30647 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30648 split.minSize = 100;
30649 split.maxSize = 600;
30650 split.animate = true;
30651 split.on('moved', splitterMoved);
30652 </code></pre>
30653  * @constructor
30654  * Create a new SplitBar
30655  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
30656  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
30657  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30658  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
30659                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30660                         position of the SplitBar).
30661  */
30662 Roo.bootstrap.SplitBar = function(cfg){
30663     
30664     /** @private */
30665     
30666     //{
30667     //  dragElement : elm
30668     //  resizingElement: el,
30669         // optional..
30670     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30671     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
30672         // existingProxy ???
30673     //}
30674     
30675     this.el = Roo.get(cfg.dragElement, true);
30676     this.el.dom.unselectable = "on";
30677     /** @private */
30678     this.resizingEl = Roo.get(cfg.resizingElement, true);
30679
30680     /**
30681      * @private
30682      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30683      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30684      * @type Number
30685      */
30686     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30687     
30688     /**
30689      * The minimum size of the resizing element. (Defaults to 0)
30690      * @type Number
30691      */
30692     this.minSize = 0;
30693     
30694     /**
30695      * The maximum size of the resizing element. (Defaults to 2000)
30696      * @type Number
30697      */
30698     this.maxSize = 2000;
30699     
30700     /**
30701      * Whether to animate the transition to the new size
30702      * @type Boolean
30703      */
30704     this.animate = false;
30705     
30706     /**
30707      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30708      * @type Boolean
30709      */
30710     this.useShim = false;
30711     
30712     /** @private */
30713     this.shim = null;
30714     
30715     if(!cfg.existingProxy){
30716         /** @private */
30717         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30718     }else{
30719         this.proxy = Roo.get(cfg.existingProxy).dom;
30720     }
30721     /** @private */
30722     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30723     
30724     /** @private */
30725     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30726     
30727     /** @private */
30728     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30729     
30730     /** @private */
30731     this.dragSpecs = {};
30732     
30733     /**
30734      * @private The adapter to use to positon and resize elements
30735      */
30736     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30737     this.adapter.init(this);
30738     
30739     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30740         /** @private */
30741         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30742         this.el.addClass("roo-splitbar-h");
30743     }else{
30744         /** @private */
30745         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30746         this.el.addClass("roo-splitbar-v");
30747     }
30748     
30749     this.addEvents({
30750         /**
30751          * @event resize
30752          * Fires when the splitter is moved (alias for {@link #event-moved})
30753          * @param {Roo.bootstrap.SplitBar} this
30754          * @param {Number} newSize the new width or height
30755          */
30756         "resize" : true,
30757         /**
30758          * @event moved
30759          * Fires when the splitter is moved
30760          * @param {Roo.bootstrap.SplitBar} this
30761          * @param {Number} newSize the new width or height
30762          */
30763         "moved" : true,
30764         /**
30765          * @event beforeresize
30766          * Fires before the splitter is dragged
30767          * @param {Roo.bootstrap.SplitBar} this
30768          */
30769         "beforeresize" : true,
30770
30771         "beforeapply" : true
30772     });
30773
30774     Roo.util.Observable.call(this);
30775 };
30776
30777 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30778     onStartProxyDrag : function(x, y){
30779         this.fireEvent("beforeresize", this);
30780         if(!this.overlay){
30781             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
30782             o.unselectable();
30783             o.enableDisplayMode("block");
30784             // all splitbars share the same overlay
30785             Roo.bootstrap.SplitBar.prototype.overlay = o;
30786         }
30787         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30788         this.overlay.show();
30789         Roo.get(this.proxy).setDisplayed("block");
30790         var size = this.adapter.getElementSize(this);
30791         this.activeMinSize = this.getMinimumSize();;
30792         this.activeMaxSize = this.getMaximumSize();;
30793         var c1 = size - this.activeMinSize;
30794         var c2 = Math.max(this.activeMaxSize - size, 0);
30795         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30796             this.dd.resetConstraints();
30797             this.dd.setXConstraint(
30798                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
30799                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30800             );
30801             this.dd.setYConstraint(0, 0);
30802         }else{
30803             this.dd.resetConstraints();
30804             this.dd.setXConstraint(0, 0);
30805             this.dd.setYConstraint(
30806                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
30807                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30808             );
30809          }
30810         this.dragSpecs.startSize = size;
30811         this.dragSpecs.startPoint = [x, y];
30812         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30813     },
30814     
30815     /** 
30816      * @private Called after the drag operation by the DDProxy
30817      */
30818     onEndProxyDrag : function(e){
30819         Roo.get(this.proxy).setDisplayed(false);
30820         var endPoint = Roo.lib.Event.getXY(e);
30821         if(this.overlay){
30822             this.overlay.hide();
30823         }
30824         var newSize;
30825         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30826             newSize = this.dragSpecs.startSize + 
30827                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30828                     endPoint[0] - this.dragSpecs.startPoint[0] :
30829                     this.dragSpecs.startPoint[0] - endPoint[0]
30830                 );
30831         }else{
30832             newSize = this.dragSpecs.startSize + 
30833                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30834                     endPoint[1] - this.dragSpecs.startPoint[1] :
30835                     this.dragSpecs.startPoint[1] - endPoint[1]
30836                 );
30837         }
30838         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30839         if(newSize != this.dragSpecs.startSize){
30840             if(this.fireEvent('beforeapply', this, newSize) !== false){
30841                 this.adapter.setElementSize(this, newSize);
30842                 this.fireEvent("moved", this, newSize);
30843                 this.fireEvent("resize", this, newSize);
30844             }
30845         }
30846     },
30847     
30848     /**
30849      * Get the adapter this SplitBar uses
30850      * @return The adapter object
30851      */
30852     getAdapter : function(){
30853         return this.adapter;
30854     },
30855     
30856     /**
30857      * Set the adapter this SplitBar uses
30858      * @param {Object} adapter A SplitBar adapter object
30859      */
30860     setAdapter : function(adapter){
30861         this.adapter = adapter;
30862         this.adapter.init(this);
30863     },
30864     
30865     /**
30866      * Gets the minimum size for the resizing element
30867      * @return {Number} The minimum size
30868      */
30869     getMinimumSize : function(){
30870         return this.minSize;
30871     },
30872     
30873     /**
30874      * Sets the minimum size for the resizing element
30875      * @param {Number} minSize The minimum size
30876      */
30877     setMinimumSize : function(minSize){
30878         this.minSize = minSize;
30879     },
30880     
30881     /**
30882      * Gets the maximum size for the resizing element
30883      * @return {Number} The maximum size
30884      */
30885     getMaximumSize : function(){
30886         return this.maxSize;
30887     },
30888     
30889     /**
30890      * Sets the maximum size for the resizing element
30891      * @param {Number} maxSize The maximum size
30892      */
30893     setMaximumSize : function(maxSize){
30894         this.maxSize = maxSize;
30895     },
30896     
30897     /**
30898      * Sets the initialize size for the resizing element
30899      * @param {Number} size The initial size
30900      */
30901     setCurrentSize : function(size){
30902         var oldAnimate = this.animate;
30903         this.animate = false;
30904         this.adapter.setElementSize(this, size);
30905         this.animate = oldAnimate;
30906     },
30907     
30908     /**
30909      * Destroy this splitbar. 
30910      * @param {Boolean} removeEl True to remove the element
30911      */
30912     destroy : function(removeEl){
30913         if(this.shim){
30914             this.shim.remove();
30915         }
30916         this.dd.unreg();
30917         this.proxy.parentNode.removeChild(this.proxy);
30918         if(removeEl){
30919             this.el.remove();
30920         }
30921     }
30922 });
30923
30924 /**
30925  * @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.
30926  */
30927 Roo.bootstrap.SplitBar.createProxy = function(dir){
30928     var proxy = new Roo.Element(document.createElement("div"));
30929     proxy.unselectable();
30930     var cls = 'roo-splitbar-proxy';
30931     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
30932     document.body.appendChild(proxy.dom);
30933     return proxy.dom;
30934 };
30935
30936 /** 
30937  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
30938  * Default Adapter. It assumes the splitter and resizing element are not positioned
30939  * elements and only gets/sets the width of the element. Generally used for table based layouts.
30940  */
30941 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
30942 };
30943
30944 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
30945     // do nothing for now
30946     init : function(s){
30947     
30948     },
30949     /**
30950      * Called before drag operations to get the current size of the resizing element. 
30951      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30952      */
30953      getElementSize : function(s){
30954         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30955             return s.resizingEl.getWidth();
30956         }else{
30957             return s.resizingEl.getHeight();
30958         }
30959     },
30960     
30961     /**
30962      * Called after drag operations to set the size of the resizing element.
30963      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30964      * @param {Number} newSize The new size to set
30965      * @param {Function} onComplete A function to be invoked when resizing is complete
30966      */
30967     setElementSize : function(s, newSize, onComplete){
30968         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30969             if(!s.animate){
30970                 s.resizingEl.setWidth(newSize);
30971                 if(onComplete){
30972                     onComplete(s, newSize);
30973                 }
30974             }else{
30975                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
30976             }
30977         }else{
30978             
30979             if(!s.animate){
30980                 s.resizingEl.setHeight(newSize);
30981                 if(onComplete){
30982                     onComplete(s, newSize);
30983                 }
30984             }else{
30985                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
30986             }
30987         }
30988     }
30989 };
30990
30991 /** 
30992  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
30993  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
30994  * Adapter that  moves the splitter element to align with the resized sizing element. 
30995  * Used with an absolute positioned SplitBar.
30996  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
30997  * document.body, make sure you assign an id to the body element.
30998  */
30999 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31000     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31001     this.container = Roo.get(container);
31002 };
31003
31004 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31005     init : function(s){
31006         this.basic.init(s);
31007     },
31008     
31009     getElementSize : function(s){
31010         return this.basic.getElementSize(s);
31011     },
31012     
31013     setElementSize : function(s, newSize, onComplete){
31014         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31015     },
31016     
31017     moveSplitter : function(s){
31018         var yes = Roo.bootstrap.SplitBar;
31019         switch(s.placement){
31020             case yes.LEFT:
31021                 s.el.setX(s.resizingEl.getRight());
31022                 break;
31023             case yes.RIGHT:
31024                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31025                 break;
31026             case yes.TOP:
31027                 s.el.setY(s.resizingEl.getBottom());
31028                 break;
31029             case yes.BOTTOM:
31030                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31031                 break;
31032         }
31033     }
31034 };
31035
31036 /**
31037  * Orientation constant - Create a vertical SplitBar
31038  * @static
31039  * @type Number
31040  */
31041 Roo.bootstrap.SplitBar.VERTICAL = 1;
31042
31043 /**
31044  * Orientation constant - Create a horizontal SplitBar
31045  * @static
31046  * @type Number
31047  */
31048 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31049
31050 /**
31051  * Placement constant - The resizing element is to the left of the splitter element
31052  * @static
31053  * @type Number
31054  */
31055 Roo.bootstrap.SplitBar.LEFT = 1;
31056
31057 /**
31058  * Placement constant - The resizing element is to the right of the splitter element
31059  * @static
31060  * @type Number
31061  */
31062 Roo.bootstrap.SplitBar.RIGHT = 2;
31063
31064 /**
31065  * Placement constant - The resizing element is positioned above the splitter element
31066  * @static
31067  * @type Number
31068  */
31069 Roo.bootstrap.SplitBar.TOP = 3;
31070
31071 /**
31072  * Placement constant - The resizing element is positioned under splitter element
31073  * @static
31074  * @type Number
31075  */
31076 Roo.bootstrap.SplitBar.BOTTOM = 4;
31077 Roo.namespace("Roo.bootstrap.layout");/*
31078  * Based on:
31079  * Ext JS Library 1.1.1
31080  * Copyright(c) 2006-2007, Ext JS, LLC.
31081  *
31082  * Originally Released Under LGPL - original licence link has changed is not relivant.
31083  *
31084  * Fork - LGPL
31085  * <script type="text/javascript">
31086  */
31087  
31088 /**
31089  * @class Roo.bootstrap.layout.Manager
31090  * @extends Roo.bootstrap.Component
31091  * Base class for layout managers.
31092  */
31093 Roo.bootstrap.layout.Manager = function(config)
31094 {
31095     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31096     this.el = Roo.get(config.el);
31097     // ie scrollbar fix
31098     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31099         document.body.scroll = "no";
31100     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31101         this.el.position('relative');
31102     }
31103     
31104     this.id = this.el.id;
31105     this.el.addClass("roo-layout-container");
31106     /** false to disable window resize monitoring @type Boolean */
31107     this.monitorWindowResize = true;
31108     this.regions = {};
31109     this.addEvents({
31110         /**
31111          * @event layout
31112          * Fires when a layout is performed. 
31113          * @param {Roo.LayoutManager} this
31114          */
31115         "layout" : true,
31116         /**
31117          * @event regionresized
31118          * Fires when the user resizes a region. 
31119          * @param {Roo.LayoutRegion} region The resized region
31120          * @param {Number} newSize The new size (width for east/west, height for north/south)
31121          */
31122         "regionresized" : true,
31123         /**
31124          * @event regioncollapsed
31125          * Fires when a region is collapsed. 
31126          * @param {Roo.LayoutRegion} region The collapsed region
31127          */
31128         "regioncollapsed" : true,
31129         /**
31130          * @event regionexpanded
31131          * Fires when a region is expanded.  
31132          * @param {Roo.LayoutRegion} region The expanded region
31133          */
31134         "regionexpanded" : true
31135     });
31136     this.updating = false;
31137     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31138 };
31139
31140 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31141     
31142     
31143     regions : null,
31144     
31145     monitorWindowResize : true,
31146     
31147     
31148     updating : false,
31149     
31150     /**
31151      * Returns true if this layout is currently being updated
31152      * @return {Boolean}
31153      */
31154     isUpdating : function(){
31155         return this.updating; 
31156     },
31157     
31158     /**
31159      * Suspend the LayoutManager from doing auto-layouts while
31160      * making multiple add or remove calls
31161      */
31162     beginUpdate : function(){
31163         this.updating = true;    
31164     },
31165     
31166     /**
31167      * Restore auto-layouts and optionally disable the manager from performing a layout
31168      * @param {Boolean} noLayout true to disable a layout update 
31169      */
31170     endUpdate : function(noLayout){
31171         this.updating = false;
31172         if(!noLayout){
31173             this.layout();
31174         }    
31175     },
31176     
31177     layout: function(){
31178         // abstract...
31179     },
31180     
31181     onRegionResized : function(region, newSize){
31182         this.fireEvent("regionresized", region, newSize);
31183         this.layout();
31184     },
31185     
31186     onRegionCollapsed : function(region){
31187         this.fireEvent("regioncollapsed", region);
31188     },
31189     
31190     onRegionExpanded : function(region){
31191         this.fireEvent("regionexpanded", region);
31192     },
31193         
31194     /**
31195      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31196      * performs box-model adjustments.
31197      * @return {Object} The size as an object {width: (the width), height: (the height)}
31198      */
31199     getViewSize : function()
31200     {
31201         var size;
31202         if(this.el.dom != document.body){
31203             size = this.el.getSize();
31204         }else{
31205             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31206         }
31207         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31208         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31209         return size;
31210     },
31211     
31212     /**
31213      * Returns the Element this layout is bound to.
31214      * @return {Roo.Element}
31215      */
31216     getEl : function(){
31217         return this.el;
31218     },
31219     
31220     /**
31221      * Returns the specified region.
31222      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31223      * @return {Roo.LayoutRegion}
31224      */
31225     getRegion : function(target){
31226         return this.regions[target.toLowerCase()];
31227     },
31228     
31229     onWindowResize : function(){
31230         if(this.monitorWindowResize){
31231             this.layout();
31232         }
31233     }
31234 });/*
31235  * Based on:
31236  * Ext JS Library 1.1.1
31237  * Copyright(c) 2006-2007, Ext JS, LLC.
31238  *
31239  * Originally Released Under LGPL - original licence link has changed is not relivant.
31240  *
31241  * Fork - LGPL
31242  * <script type="text/javascript">
31243  */
31244 /**
31245  * @class Roo.bootstrap.layout.Border
31246  * @extends Roo.bootstrap.layout.Manager
31247  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31248  * please see: examples/bootstrap/nested.html<br><br>
31249  
31250 <b>The container the layout is rendered into can be either the body element or any other element.
31251 If it is not the body element, the container needs to either be an absolute positioned element,
31252 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31253 the container size if it is not the body element.</b>
31254
31255 * @constructor
31256 * Create a new Border
31257 * @param {Object} config Configuration options
31258  */
31259 Roo.bootstrap.layout.Border = function(config){
31260     config = config || {};
31261     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31262     
31263     
31264     
31265     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31266         if(config[region]){
31267             config[region].region = region;
31268             this.addRegion(config[region]);
31269         }
31270     },this);
31271     
31272 };
31273
31274 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31275
31276 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31277     /**
31278      * Creates and adds a new region if it doesn't already exist.
31279      * @param {String} target The target region key (north, south, east, west or center).
31280      * @param {Object} config The regions config object
31281      * @return {BorderLayoutRegion} The new region
31282      */
31283     addRegion : function(config)
31284     {
31285         if(!this.regions[config.region]){
31286             var r = this.factory(config);
31287             this.bindRegion(r);
31288         }
31289         return this.regions[config.region];
31290     },
31291
31292     // private (kinda)
31293     bindRegion : function(r){
31294         this.regions[r.config.region] = r;
31295         
31296         r.on("visibilitychange",    this.layout, this);
31297         r.on("paneladded",          this.layout, this);
31298         r.on("panelremoved",        this.layout, this);
31299         r.on("invalidated",         this.layout, this);
31300         r.on("resized",             this.onRegionResized, this);
31301         r.on("collapsed",           this.onRegionCollapsed, this);
31302         r.on("expanded",            this.onRegionExpanded, this);
31303     },
31304
31305     /**
31306      * Performs a layout update.
31307      */
31308     layout : function()
31309     {
31310         if(this.updating) {
31311             return;
31312         }
31313         var size = this.getViewSize();
31314         var w = size.width;
31315         var h = size.height;
31316         var centerW = w;
31317         var centerH = h;
31318         var centerY = 0;
31319         var centerX = 0;
31320         //var x = 0, y = 0;
31321
31322         var rs = this.regions;
31323         var north = rs["north"];
31324         var south = rs["south"]; 
31325         var west = rs["west"];
31326         var east = rs["east"];
31327         var center = rs["center"];
31328         //if(this.hideOnLayout){ // not supported anymore
31329             //c.el.setStyle("display", "none");
31330         //}
31331         if(north && north.isVisible()){
31332             var b = north.getBox();
31333             var m = north.getMargins();
31334             b.width = w - (m.left+m.right);
31335             b.x = m.left;
31336             b.y = m.top;
31337             centerY = b.height + b.y + m.bottom;
31338             centerH -= centerY;
31339             north.updateBox(this.safeBox(b));
31340         }
31341         if(south && south.isVisible()){
31342             var b = south.getBox();
31343             var m = south.getMargins();
31344             b.width = w - (m.left+m.right);
31345             b.x = m.left;
31346             var totalHeight = (b.height + m.top + m.bottom);
31347             b.y = h - totalHeight + m.top;
31348             centerH -= totalHeight;
31349             south.updateBox(this.safeBox(b));
31350         }
31351         if(west && west.isVisible()){
31352             var b = west.getBox();
31353             var m = west.getMargins();
31354             b.height = centerH - (m.top+m.bottom);
31355             b.x = m.left;
31356             b.y = centerY + m.top;
31357             var totalWidth = (b.width + m.left + m.right);
31358             centerX += totalWidth;
31359             centerW -= totalWidth;
31360             west.updateBox(this.safeBox(b));
31361         }
31362         if(east && east.isVisible()){
31363             var b = east.getBox();
31364             var m = east.getMargins();
31365             b.height = centerH - (m.top+m.bottom);
31366             var totalWidth = (b.width + m.left + m.right);
31367             b.x = w - totalWidth + m.left;
31368             b.y = centerY + m.top;
31369             centerW -= totalWidth;
31370             east.updateBox(this.safeBox(b));
31371         }
31372         if(center){
31373             var m = center.getMargins();
31374             var centerBox = {
31375                 x: centerX + m.left,
31376                 y: centerY + m.top,
31377                 width: centerW - (m.left+m.right),
31378                 height: centerH - (m.top+m.bottom)
31379             };
31380             //if(this.hideOnLayout){
31381                 //center.el.setStyle("display", "block");
31382             //}
31383             center.updateBox(this.safeBox(centerBox));
31384         }
31385         this.el.repaint();
31386         this.fireEvent("layout", this);
31387     },
31388
31389     // private
31390     safeBox : function(box){
31391         box.width = Math.max(0, box.width);
31392         box.height = Math.max(0, box.height);
31393         return box;
31394     },
31395
31396     /**
31397      * Adds a ContentPanel (or subclass) to this layout.
31398      * @param {String} target The target region key (north, south, east, west or center).
31399      * @param {Roo.ContentPanel} panel The panel to add
31400      * @return {Roo.ContentPanel} The added panel
31401      */
31402     add : function(target, panel){
31403          
31404         target = target.toLowerCase();
31405         return this.regions[target].add(panel);
31406     },
31407
31408     /**
31409      * Remove a ContentPanel (or subclass) to this layout.
31410      * @param {String} target The target region key (north, south, east, west or center).
31411      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31412      * @return {Roo.ContentPanel} The removed panel
31413      */
31414     remove : function(target, panel){
31415         target = target.toLowerCase();
31416         return this.regions[target].remove(panel);
31417     },
31418
31419     /**
31420      * Searches all regions for a panel with the specified id
31421      * @param {String} panelId
31422      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31423      */
31424     findPanel : function(panelId){
31425         var rs = this.regions;
31426         for(var target in rs){
31427             if(typeof rs[target] != "function"){
31428                 var p = rs[target].getPanel(panelId);
31429                 if(p){
31430                     return p;
31431                 }
31432             }
31433         }
31434         return null;
31435     },
31436
31437     /**
31438      * Searches all regions for a panel with the specified id and activates (shows) it.
31439      * @param {String/ContentPanel} panelId The panels id or the panel itself
31440      * @return {Roo.ContentPanel} The shown panel or null
31441      */
31442     showPanel : function(panelId) {
31443       var rs = this.regions;
31444       for(var target in rs){
31445          var r = rs[target];
31446          if(typeof r != "function"){
31447             if(r.hasPanel(panelId)){
31448                return r.showPanel(panelId);
31449             }
31450          }
31451       }
31452       return null;
31453    },
31454
31455    /**
31456      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31457      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31458      */
31459    /*
31460     restoreState : function(provider){
31461         if(!provider){
31462             provider = Roo.state.Manager;
31463         }
31464         var sm = new Roo.LayoutStateManager();
31465         sm.init(this, provider);
31466     },
31467 */
31468  
31469  
31470     /**
31471      * Adds a xtype elements to the layout.
31472      * <pre><code>
31473
31474 layout.addxtype({
31475        xtype : 'ContentPanel',
31476        region: 'west',
31477        items: [ .... ]
31478    }
31479 );
31480
31481 layout.addxtype({
31482         xtype : 'NestedLayoutPanel',
31483         region: 'west',
31484         layout: {
31485            center: { },
31486            west: { }   
31487         },
31488         items : [ ... list of content panels or nested layout panels.. ]
31489    }
31490 );
31491 </code></pre>
31492      * @param {Object} cfg Xtype definition of item to add.
31493      */
31494     addxtype : function(cfg)
31495     {
31496         // basically accepts a pannel...
31497         // can accept a layout region..!?!?
31498         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31499         
31500         
31501         // theory?  children can only be panels??
31502         
31503         //if (!cfg.xtype.match(/Panel$/)) {
31504         //    return false;
31505         //}
31506         var ret = false;
31507         
31508         if (typeof(cfg.region) == 'undefined') {
31509             Roo.log("Failed to add Panel, region was not set");
31510             Roo.log(cfg);
31511             return false;
31512         }
31513         var region = cfg.region;
31514         delete cfg.region;
31515         
31516           
31517         var xitems = [];
31518         if (cfg.items) {
31519             xitems = cfg.items;
31520             delete cfg.items;
31521         }
31522         var nb = false;
31523         
31524         switch(cfg.xtype) 
31525         {
31526             case 'Content':  // ContentPanel (el, cfg)
31527             case 'Scroll':  // ContentPanel (el, cfg)
31528             case 'View': 
31529                 cfg.autoCreate = true;
31530                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31531                 //} else {
31532                 //    var el = this.el.createChild();
31533                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31534                 //}
31535                 
31536                 this.add(region, ret);
31537                 break;
31538             
31539             /*
31540             case 'TreePanel': // our new panel!
31541                 cfg.el = this.el.createChild();
31542                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31543                 this.add(region, ret);
31544                 break;
31545             */
31546             
31547             case 'Nest': 
31548                 // create a new Layout (which is  a Border Layout...
31549                 
31550                 var clayout = cfg.layout;
31551                 clayout.el  = this.el.createChild();
31552                 clayout.items   = clayout.items  || [];
31553                 
31554                 delete cfg.layout;
31555                 
31556                 // replace this exitems with the clayout ones..
31557                 xitems = clayout.items;
31558                  
31559                 // force background off if it's in center...
31560                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31561                     cfg.background = false;
31562                 }
31563                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31564                 
31565                 
31566                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31567                 //console.log('adding nested layout panel '  + cfg.toSource());
31568                 this.add(region, ret);
31569                 nb = {}; /// find first...
31570                 break;
31571             
31572             case 'Grid':
31573                 
31574                 // needs grid and region
31575                 
31576                 //var el = this.getRegion(region).el.createChild();
31577                 var el = this.el.createChild();
31578                 // create the grid first...
31579                 cfg.grid.container = el;
31580                 cfg.grid.scrollBody = true;
31581                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31582                 
31583                 
31584                 if (region == 'center' && this.active ) {
31585                     cfg.background = false;
31586                 }
31587                 
31588                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31589                 
31590                 this.add(region, ret);
31591                 
31592                 if (cfg.background) {
31593                     // render grid on panel activation (if panel background)
31594                     ret.on('activate', function(gp) {
31595                         if (!gp.grid.rendered) {
31596                             gp.grid.render(gp.grid.getGridEl());
31597                         }
31598                     });
31599                 } else {
31600                     cfg.grid.render(cfg.grid.getGridEl());
31601                 }
31602                 break;
31603            
31604            
31605             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31606                 // it was the old xcomponent building that caused this before.
31607                 // espeically if border is the top element in the tree.
31608                 ret = this;
31609                 break; 
31610                 
31611                     
31612                 
31613                 
31614                 
31615             default:
31616                 /*
31617                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31618                     
31619                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31620                     this.add(region, ret);
31621                 } else {
31622                 */
31623                     Roo.log(cfg);
31624                     throw "Can not add '" + cfg.xtype + "' to Border";
31625                     return null;
31626              
31627                                 
31628              
31629         }
31630         this.beginUpdate();
31631         // add children..
31632         var region = '';
31633         var abn = {};
31634         Roo.each(xitems, function(i)  {
31635             region = nb && i.region ? i.region : false;
31636             
31637             var add = ret.addxtype(i);
31638            
31639             if (region) {
31640                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31641                 if (!i.background) {
31642                     abn[region] = nb[region] ;
31643                 }
31644             }
31645             
31646         });
31647         this.endUpdate();
31648
31649         // make the last non-background panel active..
31650         //if (nb) { Roo.log(abn); }
31651         if (nb) {
31652             
31653             for(var r in abn) {
31654                 region = this.getRegion(r);
31655                 if (region) {
31656                     // tried using nb[r], but it does not work..
31657                      
31658                     region.showPanel(abn[r]);
31659                    
31660                 }
31661             }
31662         }
31663         return ret;
31664         
31665     },
31666     
31667     
31668 // private
31669     factory : function(cfg)
31670     {
31671         
31672         var validRegions = Roo.bootstrap.layout.Border.regions;
31673
31674         var target = cfg.region;
31675         cfg.mgr = this;
31676         
31677         var r = Roo.bootstrap.layout;
31678         Roo.log(target);
31679         switch(target){
31680             case "north":
31681                 return new r.North(cfg);
31682             case "south":
31683                 return new r.South(cfg);
31684             case "east":
31685                 return new r.East(cfg);
31686             case "west":
31687                 return new r.West(cfg);
31688             case "center":
31689                 return new r.Center(cfg);
31690         }
31691         throw 'Layout region "'+target+'" not supported.';
31692     }
31693     
31694     
31695 });
31696  /*
31697  * Based on:
31698  * Ext JS Library 1.1.1
31699  * Copyright(c) 2006-2007, Ext JS, LLC.
31700  *
31701  * Originally Released Under LGPL - original licence link has changed is not relivant.
31702  *
31703  * Fork - LGPL
31704  * <script type="text/javascript">
31705  */
31706  
31707 /**
31708  * @class Roo.bootstrap.layout.Basic
31709  * @extends Roo.util.Observable
31710  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31711  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31712  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31713  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31714  * @cfg {string}   region  the region that it inhabits..
31715  * @cfg {bool}   skipConfig skip config?
31716  * 
31717
31718  */
31719 Roo.bootstrap.layout.Basic = function(config){
31720     
31721     this.mgr = config.mgr;
31722     
31723     this.position = config.region;
31724     
31725     var skipConfig = config.skipConfig;
31726     
31727     this.events = {
31728         /**
31729          * @scope Roo.BasicLayoutRegion
31730          */
31731         
31732         /**
31733          * @event beforeremove
31734          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31735          * @param {Roo.LayoutRegion} this
31736          * @param {Roo.ContentPanel} panel The panel
31737          * @param {Object} e The cancel event object
31738          */
31739         "beforeremove" : true,
31740         /**
31741          * @event invalidated
31742          * Fires when the layout for this region is changed.
31743          * @param {Roo.LayoutRegion} this
31744          */
31745         "invalidated" : true,
31746         /**
31747          * @event visibilitychange
31748          * Fires when this region is shown or hidden 
31749          * @param {Roo.LayoutRegion} this
31750          * @param {Boolean} visibility true or false
31751          */
31752         "visibilitychange" : true,
31753         /**
31754          * @event paneladded
31755          * Fires when a panel is added. 
31756          * @param {Roo.LayoutRegion} this
31757          * @param {Roo.ContentPanel} panel The panel
31758          */
31759         "paneladded" : true,
31760         /**
31761          * @event panelremoved
31762          * Fires when a panel is removed. 
31763          * @param {Roo.LayoutRegion} this
31764          * @param {Roo.ContentPanel} panel The panel
31765          */
31766         "panelremoved" : true,
31767         /**
31768          * @event beforecollapse
31769          * Fires when this region before collapse.
31770          * @param {Roo.LayoutRegion} this
31771          */
31772         "beforecollapse" : true,
31773         /**
31774          * @event collapsed
31775          * Fires when this region is collapsed.
31776          * @param {Roo.LayoutRegion} this
31777          */
31778         "collapsed" : true,
31779         /**
31780          * @event expanded
31781          * Fires when this region is expanded.
31782          * @param {Roo.LayoutRegion} this
31783          */
31784         "expanded" : true,
31785         /**
31786          * @event slideshow
31787          * Fires when this region is slid into view.
31788          * @param {Roo.LayoutRegion} this
31789          */
31790         "slideshow" : true,
31791         /**
31792          * @event slidehide
31793          * Fires when this region slides out of view. 
31794          * @param {Roo.LayoutRegion} this
31795          */
31796         "slidehide" : true,
31797         /**
31798          * @event panelactivated
31799          * Fires when a panel is activated. 
31800          * @param {Roo.LayoutRegion} this
31801          * @param {Roo.ContentPanel} panel The activated panel
31802          */
31803         "panelactivated" : true,
31804         /**
31805          * @event resized
31806          * Fires when the user resizes this region. 
31807          * @param {Roo.LayoutRegion} this
31808          * @param {Number} newSize The new size (width for east/west, height for north/south)
31809          */
31810         "resized" : true
31811     };
31812     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31813     this.panels = new Roo.util.MixedCollection();
31814     this.panels.getKey = this.getPanelId.createDelegate(this);
31815     this.box = null;
31816     this.activePanel = null;
31817     // ensure listeners are added...
31818     
31819     if (config.listeners || config.events) {
31820         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31821             listeners : config.listeners || {},
31822             events : config.events || {}
31823         });
31824     }
31825     
31826     if(skipConfig !== true){
31827         this.applyConfig(config);
31828     }
31829 };
31830
31831 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31832 {
31833     getPanelId : function(p){
31834         return p.getId();
31835     },
31836     
31837     applyConfig : function(config){
31838         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31839         this.config = config;
31840         
31841     },
31842     
31843     /**
31844      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31845      * the width, for horizontal (north, south) the height.
31846      * @param {Number} newSize The new width or height
31847      */
31848     resizeTo : function(newSize){
31849         var el = this.el ? this.el :
31850                  (this.activePanel ? this.activePanel.getEl() : null);
31851         if(el){
31852             switch(this.position){
31853                 case "east":
31854                 case "west":
31855                     el.setWidth(newSize);
31856                     this.fireEvent("resized", this, newSize);
31857                 break;
31858                 case "north":
31859                 case "south":
31860                     el.setHeight(newSize);
31861                     this.fireEvent("resized", this, newSize);
31862                 break;                
31863             }
31864         }
31865     },
31866     
31867     getBox : function(){
31868         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31869     },
31870     
31871     getMargins : function(){
31872         return this.margins;
31873     },
31874     
31875     updateBox : function(box){
31876         this.box = box;
31877         var el = this.activePanel.getEl();
31878         el.dom.style.left = box.x + "px";
31879         el.dom.style.top = box.y + "px";
31880         this.activePanel.setSize(box.width, box.height);
31881     },
31882     
31883     /**
31884      * Returns the container element for this region.
31885      * @return {Roo.Element}
31886      */
31887     getEl : function(){
31888         return this.activePanel;
31889     },
31890     
31891     /**
31892      * Returns true if this region is currently visible.
31893      * @return {Boolean}
31894      */
31895     isVisible : function(){
31896         return this.activePanel ? true : false;
31897     },
31898     
31899     setActivePanel : function(panel){
31900         panel = this.getPanel(panel);
31901         if(this.activePanel && this.activePanel != panel){
31902             this.activePanel.setActiveState(false);
31903             this.activePanel.getEl().setLeftTop(-10000,-10000);
31904         }
31905         this.activePanel = panel;
31906         panel.setActiveState(true);
31907         if(this.box){
31908             panel.setSize(this.box.width, this.box.height);
31909         }
31910         this.fireEvent("panelactivated", this, panel);
31911         this.fireEvent("invalidated");
31912     },
31913     
31914     /**
31915      * Show the specified panel.
31916      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31917      * @return {Roo.ContentPanel} The shown panel or null
31918      */
31919     showPanel : function(panel){
31920         panel = this.getPanel(panel);
31921         if(panel){
31922             this.setActivePanel(panel);
31923         }
31924         return panel;
31925     },
31926     
31927     /**
31928      * Get the active panel for this region.
31929      * @return {Roo.ContentPanel} The active panel or null
31930      */
31931     getActivePanel : function(){
31932         return this.activePanel;
31933     },
31934     
31935     /**
31936      * Add the passed ContentPanel(s)
31937      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31938      * @return {Roo.ContentPanel} The panel added (if only one was added)
31939      */
31940     add : function(panel){
31941         if(arguments.length > 1){
31942             for(var i = 0, len = arguments.length; i < len; i++) {
31943                 this.add(arguments[i]);
31944             }
31945             return null;
31946         }
31947         if(this.hasPanel(panel)){
31948             this.showPanel(panel);
31949             return panel;
31950         }
31951         var el = panel.getEl();
31952         if(el.dom.parentNode != this.mgr.el.dom){
31953             this.mgr.el.dom.appendChild(el.dom);
31954         }
31955         if(panel.setRegion){
31956             panel.setRegion(this);
31957         }
31958         this.panels.add(panel);
31959         el.setStyle("position", "absolute");
31960         if(!panel.background){
31961             this.setActivePanel(panel);
31962             if(this.config.initialSize && this.panels.getCount()==1){
31963                 this.resizeTo(this.config.initialSize);
31964             }
31965         }
31966         this.fireEvent("paneladded", this, panel);
31967         return panel;
31968     },
31969     
31970     /**
31971      * Returns true if the panel is in this region.
31972      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31973      * @return {Boolean}
31974      */
31975     hasPanel : function(panel){
31976         if(typeof panel == "object"){ // must be panel obj
31977             panel = panel.getId();
31978         }
31979         return this.getPanel(panel) ? true : false;
31980     },
31981     
31982     /**
31983      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31984      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31985      * @param {Boolean} preservePanel Overrides the config preservePanel option
31986      * @return {Roo.ContentPanel} The panel that was removed
31987      */
31988     remove : function(panel, preservePanel){
31989         panel = this.getPanel(panel);
31990         if(!panel){
31991             return null;
31992         }
31993         var e = {};
31994         this.fireEvent("beforeremove", this, panel, e);
31995         if(e.cancel === true){
31996             return null;
31997         }
31998         var panelId = panel.getId();
31999         this.panels.removeKey(panelId);
32000         return panel;
32001     },
32002     
32003     /**
32004      * Returns the panel specified or null if it's not in this region.
32005      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32006      * @return {Roo.ContentPanel}
32007      */
32008     getPanel : function(id){
32009         if(typeof id == "object"){ // must be panel obj
32010             return id;
32011         }
32012         return this.panels.get(id);
32013     },
32014     
32015     /**
32016      * Returns this regions position (north/south/east/west/center).
32017      * @return {String} 
32018      */
32019     getPosition: function(){
32020         return this.position;    
32021     }
32022 });/*
32023  * Based on:
32024  * Ext JS Library 1.1.1
32025  * Copyright(c) 2006-2007, Ext JS, LLC.
32026  *
32027  * Originally Released Under LGPL - original licence link has changed is not relivant.
32028  *
32029  * Fork - LGPL
32030  * <script type="text/javascript">
32031  */
32032  
32033 /**
32034  * @class Roo.bootstrap.layout.Region
32035  * @extends Roo.bootstrap.layout.Basic
32036  * This class represents a region in a layout manager.
32037  
32038  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32039  * @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})
32040  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32041  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32042  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32043  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32044  * @cfg {String}    title           The title for the region (overrides panel titles)
32045  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32046  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32047  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32048  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32049  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32050  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32051  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32052  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32053  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32054  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32055
32056  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32057  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32058  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32059  * @cfg {Number}    width           For East/West panels
32060  * @cfg {Number}    height          For North/South panels
32061  * @cfg {Boolean}   split           To show the splitter
32062  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32063  * 
32064  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32065  * @cfg {string}   region  the region that it inhabits..
32066  *
32067
32068  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32069  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32070
32071  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32072  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32073  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32074  */
32075 Roo.bootstrap.layout.Region = function(config)
32076 {
32077     
32078     var mgr = config.mgr;
32079     var pos = config.region;
32080     config.skipConfig = true;
32081     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32082     var dh = Roo.DomHelper;
32083     /** This region's container element 
32084     * @type Roo.Element */
32085     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "roo-layout-region roo-layout-panel roo-layout-panel-" + this.position}, true);
32086     /** This region's title element 
32087     * @type Roo.Element */
32088
32089     this.titleEl = dh.append(this.el.dom,
32090         {
32091                 tag: "div",
32092                 unselectable: "on",
32093                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32094                 children:[
32095                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32096                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32097                 ]}, true);
32098     
32099     this.titleEl.enableDisplayMode();
32100     /** This region's title text element 
32101     * @type HTMLElement */
32102     this.titleTextEl = this.titleEl.dom.firstChild;
32103     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32104     /*
32105     this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32106     this.closeBtn.enableDisplayMode();
32107     this.closeBtn.on("click", this.closeClicked, this);
32108     this.closeBtn.hide();
32109 */
32110     this.createBody(config);
32111     this.visible = true;
32112     this.collapsed = false;
32113
32114     if(config.hideWhenEmpty){
32115         this.hide();
32116         this.on("paneladded", this.validateVisibility, this);
32117         this.on("panelremoved", this.validateVisibility, this);
32118     }
32119     this.applyConfig(config);
32120 };
32121
32122 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32123
32124
32125
32126     createBody : function(){
32127         /** This region's body element 
32128         * @type Roo.Element */
32129         this.bodyEl = this.el.createChild({
32130                 tag: "div",
32131                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32132         });
32133     },
32134
32135     applyConfig : function(c)
32136     {
32137         /*
32138          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32139             var dh = Roo.DomHelper;
32140             if(c.titlebar !== false){
32141                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32142                 this.collapseBtn.on("click", this.collapse, this);
32143                 this.collapseBtn.enableDisplayMode();
32144                 /*
32145                 if(c.showPin === true || this.showPin){
32146                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32147                     this.stickBtn.enableDisplayMode();
32148                     this.stickBtn.on("click", this.expand, this);
32149                     this.stickBtn.hide();
32150                 }
32151                 
32152             }
32153             */
32154             /** This region's collapsed element
32155             * @type Roo.Element */
32156             /*
32157              *
32158             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32159                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32160             ]}, true);
32161             
32162             if(c.floatable !== false){
32163                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32164                this.collapsedEl.on("click", this.collapseClick, this);
32165             }
32166
32167             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32168                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32169                    id: "message", unselectable: "on", style:{"float":"left"}});
32170                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32171              }
32172             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32173             this.expandBtn.on("click", this.expand, this);
32174             
32175         }
32176         
32177         if(this.collapseBtn){
32178             this.collapseBtn.setVisible(c.collapsible == true);
32179         }
32180         
32181         this.cmargins = c.cmargins || this.cmargins ||
32182                          (this.position == "west" || this.position == "east" ?
32183                              {top: 0, left: 2, right:2, bottom: 0} :
32184                              {top: 2, left: 0, right:0, bottom: 2});
32185         */
32186         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32187         
32188         
32189         this.bottomTabs = c.tabPosition != "top";
32190         
32191         this.autoScroll = c.autoScroll || false;
32192         
32193         
32194         if(this.autoScroll){
32195             this.bodyEl.setStyle("overflow", "auto");
32196         }else{
32197             this.bodyEl.setStyle("overflow", c.overflow || 'hidden');
32198         }
32199         //if(c.titlebar !== false){
32200             if((!c.titlebar && !c.title) || c.titlebar === false){
32201                 this.titleEl.hide();
32202             }else{
32203                 this.titleEl.show();
32204                 if(c.title){
32205                     this.titleTextEl.innerHTML = c.title;
32206                 }
32207             }
32208         //}
32209         this.duration = c.duration || .30;
32210         this.slideDuration = c.slideDuration || .45;
32211         this.config = c;
32212         if(c.collapsed){
32213             this.collapse(true);
32214         }
32215         if(c.hidden){
32216             this.hide();
32217         }
32218     },
32219     /**
32220      * Returns true if this region is currently visible.
32221      * @return {Boolean}
32222      */
32223     isVisible : function(){
32224         return this.visible;
32225     },
32226
32227     /**
32228      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32229      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32230      */
32231     //setCollapsedTitle : function(title){
32232     //    title = title || "&#160;";
32233      //   if(this.collapsedTitleTextEl){
32234       //      this.collapsedTitleTextEl.innerHTML = title;
32235        // }
32236     //},
32237
32238     getBox : function(){
32239         var b;
32240       //  if(!this.collapsed){
32241             b = this.el.getBox(false, true);
32242        // }else{
32243           //  b = this.collapsedEl.getBox(false, true);
32244         //}
32245         return b;
32246     },
32247
32248     getMargins : function(){
32249         return this.margins;
32250         //return this.collapsed ? this.cmargins : this.margins;
32251     },
32252 /*
32253     highlight : function(){
32254         this.el.addClass("x-layout-panel-dragover");
32255     },
32256
32257     unhighlight : function(){
32258         this.el.removeClass("x-layout-panel-dragover");
32259     },
32260 */
32261     updateBox : function(box)
32262     {
32263         this.box = box;
32264         if(!this.collapsed){
32265             this.el.dom.style.left = box.x + "px";
32266             this.el.dom.style.top = box.y + "px";
32267             this.updateBody(box.width, box.height);
32268         }else{
32269             this.collapsedEl.dom.style.left = box.x + "px";
32270             this.collapsedEl.dom.style.top = box.y + "px";
32271             this.collapsedEl.setSize(box.width, box.height);
32272         }
32273         if(this.tabs){
32274             this.tabs.autoSizeTabs();
32275         }
32276     },
32277
32278     updateBody : function(w, h)
32279     {
32280         if(w !== null){
32281             this.el.setWidth(w);
32282             w -= this.el.getBorderWidth("rl");
32283             if(this.config.adjustments){
32284                 w += this.config.adjustments[0];
32285             }
32286         }
32287         if(h !== null){
32288             this.el.setHeight(h);
32289             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32290             h -= this.el.getBorderWidth("tb");
32291             if(this.config.adjustments){
32292                 h += this.config.adjustments[1];
32293             }
32294             this.bodyEl.setHeight(h);
32295             if(this.tabs){
32296                 h = this.tabs.syncHeight(h);
32297             }
32298         }
32299         if(this.panelSize){
32300             w = w !== null ? w : this.panelSize.width;
32301             h = h !== null ? h : this.panelSize.height;
32302         }
32303         if(this.activePanel){
32304             var el = this.activePanel.getEl();
32305             w = w !== null ? w : el.getWidth();
32306             h = h !== null ? h : el.getHeight();
32307             this.panelSize = {width: w, height: h};
32308             this.activePanel.setSize(w, h);
32309         }
32310         if(Roo.isIE && this.tabs){
32311             this.tabs.el.repaint();
32312         }
32313     },
32314
32315     /**
32316      * Returns the container element for this region.
32317      * @return {Roo.Element}
32318      */
32319     getEl : function(){
32320         return this.el;
32321     },
32322
32323     /**
32324      * Hides this region.
32325      */
32326     hide : function(){
32327         //if(!this.collapsed){
32328             this.el.dom.style.left = "-2000px";
32329             this.el.hide();
32330         //}else{
32331          //   this.collapsedEl.dom.style.left = "-2000px";
32332          //   this.collapsedEl.hide();
32333        // }
32334         this.visible = false;
32335         this.fireEvent("visibilitychange", this, false);
32336     },
32337
32338     /**
32339      * Shows this region if it was previously hidden.
32340      */
32341     show : function(){
32342         //if(!this.collapsed){
32343             this.el.show();
32344         //}else{
32345         //    this.collapsedEl.show();
32346        // }
32347         this.visible = true;
32348         this.fireEvent("visibilitychange", this, true);
32349     },
32350 /*
32351     closeClicked : function(){
32352         if(this.activePanel){
32353             this.remove(this.activePanel);
32354         }
32355     },
32356
32357     collapseClick : function(e){
32358         if(this.isSlid){
32359            e.stopPropagation();
32360            this.slideIn();
32361         }else{
32362            e.stopPropagation();
32363            this.slideOut();
32364         }
32365     },
32366 */
32367     /**
32368      * Collapses this region.
32369      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32370      */
32371     /*
32372     collapse : function(skipAnim, skipCheck = false){
32373         if(this.collapsed) {
32374             return;
32375         }
32376         
32377         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32378             
32379             this.collapsed = true;
32380             if(this.split){
32381                 this.split.el.hide();
32382             }
32383             if(this.config.animate && skipAnim !== true){
32384                 this.fireEvent("invalidated", this);
32385                 this.animateCollapse();
32386             }else{
32387                 this.el.setLocation(-20000,-20000);
32388                 this.el.hide();
32389                 this.collapsedEl.show();
32390                 this.fireEvent("collapsed", this);
32391                 this.fireEvent("invalidated", this);
32392             }
32393         }
32394         
32395     },
32396 */
32397     animateCollapse : function(){
32398         // overridden
32399     },
32400
32401     /**
32402      * Expands this region if it was previously collapsed.
32403      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32404      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32405      */
32406     /*
32407     expand : function(e, skipAnim){
32408         if(e) {
32409             e.stopPropagation();
32410         }
32411         if(!this.collapsed || this.el.hasActiveFx()) {
32412             return;
32413         }
32414         if(this.isSlid){
32415             this.afterSlideIn();
32416             skipAnim = true;
32417         }
32418         this.collapsed = false;
32419         if(this.config.animate && skipAnim !== true){
32420             this.animateExpand();
32421         }else{
32422             this.el.show();
32423             if(this.split){
32424                 this.split.el.show();
32425             }
32426             this.collapsedEl.setLocation(-2000,-2000);
32427             this.collapsedEl.hide();
32428             this.fireEvent("invalidated", this);
32429             this.fireEvent("expanded", this);
32430         }
32431     },
32432 */
32433     animateExpand : function(){
32434         // overridden
32435     },
32436
32437     initTabs : function()
32438     {
32439         this.bodyEl.setStyle("overflow", "hidden");
32440         var ts = new Roo.bootstrap.panel.Tabs({
32441                 el: this.bodyEl.dom,
32442                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32443                 disableTooltips: this.config.disableTabTips,
32444                 toolbar : this.config.toolbar
32445             });
32446         
32447         if(this.config.hideTabs){
32448             ts.stripWrap.setDisplayed(false);
32449         }
32450         this.tabs = ts;
32451         ts.resizeTabs = this.config.resizeTabs === true;
32452         ts.minTabWidth = this.config.minTabWidth || 40;
32453         ts.maxTabWidth = this.config.maxTabWidth || 250;
32454         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32455         ts.monitorResize = false;
32456         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32457         ts.bodyEl.addClass('roo-layout-tabs-body');
32458         this.panels.each(this.initPanelAsTab, this);
32459     },
32460
32461     initPanelAsTab : function(panel){
32462         var ti = this.tabs.addTab(
32463                     panel.getEl().id,
32464                     panel.getTitle(), null,
32465                     this.config.closeOnTab && panel.isClosable()
32466             );
32467         if(panel.tabTip !== undefined){
32468             ti.setTooltip(panel.tabTip);
32469         }
32470         ti.on("activate", function(){
32471               this.setActivePanel(panel);
32472         }, this);
32473         
32474         if(this.config.closeOnTab){
32475             ti.on("beforeclose", function(t, e){
32476                 e.cancel = true;
32477                 this.remove(panel);
32478             }, this);
32479         }
32480         return ti;
32481     },
32482
32483     updatePanelTitle : function(panel, title)
32484     {
32485         if(this.activePanel == panel){
32486             this.updateTitle(title);
32487         }
32488         if(this.tabs){
32489             var ti = this.tabs.getTab(panel.getEl().id);
32490             ti.setText(title);
32491             if(panel.tabTip !== undefined){
32492                 ti.setTooltip(panel.tabTip);
32493             }
32494         }
32495     },
32496
32497     updateTitle : function(title){
32498         if(this.titleTextEl && !this.config.title){
32499             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32500         }
32501     },
32502
32503     setActivePanel : function(panel)
32504     {
32505         panel = this.getPanel(panel);
32506         if(this.activePanel && this.activePanel != panel){
32507             this.activePanel.setActiveState(false);
32508         }
32509         this.activePanel = panel;
32510         panel.setActiveState(true);
32511         if(this.panelSize){
32512             panel.setSize(this.panelSize.width, this.panelSize.height);
32513         }
32514         if(this.closeBtn){
32515             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32516         }
32517         this.updateTitle(panel.getTitle());
32518         if(this.tabs){
32519             this.fireEvent("invalidated", this);
32520         }
32521         this.fireEvent("panelactivated", this, panel);
32522     },
32523
32524     /**
32525      * Shows the specified panel.
32526      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32527      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32528      */
32529     showPanel : function(panel)
32530     {
32531         panel = this.getPanel(panel);
32532         if(panel){
32533             if(this.tabs){
32534                 var tab = this.tabs.getTab(panel.getEl().id);
32535                 if(tab.isHidden()){
32536                     this.tabs.unhideTab(tab.id);
32537                 }
32538                 tab.activate();
32539             }else{
32540                 this.setActivePanel(panel);
32541             }
32542         }
32543         return panel;
32544     },
32545
32546     /**
32547      * Get the active panel for this region.
32548      * @return {Roo.ContentPanel} The active panel or null
32549      */
32550     getActivePanel : function(){
32551         return this.activePanel;
32552     },
32553
32554     validateVisibility : function(){
32555         if(this.panels.getCount() < 1){
32556             this.updateTitle("&#160;");
32557             this.closeBtn.hide();
32558             this.hide();
32559         }else{
32560             if(!this.isVisible()){
32561                 this.show();
32562             }
32563         }
32564     },
32565
32566     /**
32567      * Adds the passed ContentPanel(s) to this region.
32568      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32569      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32570      */
32571     add : function(panel){
32572         if(arguments.length > 1){
32573             for(var i = 0, len = arguments.length; i < len; i++) {
32574                 this.add(arguments[i]);
32575             }
32576             return null;
32577         }
32578         if(this.hasPanel(panel)){
32579             this.showPanel(panel);
32580             return panel;
32581         }
32582         panel.setRegion(this);
32583         this.panels.add(panel);
32584         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32585             this.bodyEl.dom.appendChild(panel.getEl().dom);
32586             if(panel.background !== true){
32587                 this.setActivePanel(panel);
32588             }
32589             this.fireEvent("paneladded", this, panel);
32590             return panel;
32591         }
32592         if(!this.tabs){
32593             this.initTabs();
32594         }else{
32595             this.initPanelAsTab(panel);
32596         }
32597         
32598         
32599         if(panel.background !== true){
32600             this.tabs.activate(panel.getEl().id);
32601         }
32602         this.fireEvent("paneladded", this, panel);
32603         return panel;
32604     },
32605
32606     /**
32607      * Hides the tab for the specified panel.
32608      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32609      */
32610     hidePanel : function(panel){
32611         if(this.tabs && (panel = this.getPanel(panel))){
32612             this.tabs.hideTab(panel.getEl().id);
32613         }
32614     },
32615
32616     /**
32617      * Unhides the tab for a previously hidden panel.
32618      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32619      */
32620     unhidePanel : function(panel){
32621         if(this.tabs && (panel = this.getPanel(panel))){
32622             this.tabs.unhideTab(panel.getEl().id);
32623         }
32624     },
32625
32626     clearPanels : function(){
32627         while(this.panels.getCount() > 0){
32628              this.remove(this.panels.first());
32629         }
32630     },
32631
32632     /**
32633      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32634      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32635      * @param {Boolean} preservePanel Overrides the config preservePanel option
32636      * @return {Roo.ContentPanel} The panel that was removed
32637      */
32638     remove : function(panel, preservePanel)
32639     {
32640         panel = this.getPanel(panel);
32641         if(!panel){
32642             return null;
32643         }
32644         var e = {};
32645         this.fireEvent("beforeremove", this, panel, e);
32646         if(e.cancel === true){
32647             return null;
32648         }
32649         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32650         var panelId = panel.getId();
32651         this.panels.removeKey(panelId);
32652         if(preservePanel){
32653             document.body.appendChild(panel.getEl().dom);
32654         }
32655         if(this.tabs){
32656             this.tabs.removeTab(panel.getEl().id);
32657         }else if (!preservePanel){
32658             this.bodyEl.dom.removeChild(panel.getEl().dom);
32659         }
32660         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32661             var p = this.panels.first();
32662             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32663             tempEl.appendChild(p.getEl().dom);
32664             this.bodyEl.update("");
32665             this.bodyEl.dom.appendChild(p.getEl().dom);
32666             tempEl = null;
32667             this.updateTitle(p.getTitle());
32668             this.tabs = null;
32669             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32670             this.setActivePanel(p);
32671         }
32672         panel.setRegion(null);
32673         if(this.activePanel == panel){
32674             this.activePanel = null;
32675         }
32676         if(this.config.autoDestroy !== false && preservePanel !== true){
32677             try{panel.destroy();}catch(e){}
32678         }
32679         this.fireEvent("panelremoved", this, panel);
32680         return panel;
32681     },
32682
32683     /**
32684      * Returns the TabPanel component used by this region
32685      * @return {Roo.TabPanel}
32686      */
32687     getTabs : function(){
32688         return this.tabs;
32689     },
32690
32691     createTool : function(parentEl, className){
32692         var btn = Roo.DomHelper.append(parentEl, {
32693             tag: "div",
32694             cls: "x-layout-tools-button",
32695             children: [ {
32696                 tag: "div",
32697                 cls: "roo-layout-tools-button-inner " + className,
32698                 html: "&#160;"
32699             }]
32700         }, true);
32701         btn.addClassOnOver("roo-layout-tools-button-over");
32702         return btn;
32703     }
32704 });/*
32705  * Based on:
32706  * Ext JS Library 1.1.1
32707  * Copyright(c) 2006-2007, Ext JS, LLC.
32708  *
32709  * Originally Released Under LGPL - original licence link has changed is not relivant.
32710  *
32711  * Fork - LGPL
32712  * <script type="text/javascript">
32713  */
32714  
32715
32716
32717 /**
32718  * @class Roo.SplitLayoutRegion
32719  * @extends Roo.LayoutRegion
32720  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32721  */
32722 Roo.bootstrap.layout.Split = function(config){
32723     this.cursor = config.cursor;
32724     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32725 };
32726
32727 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32728 {
32729     splitTip : "Drag to resize.",
32730     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32731     useSplitTips : false,
32732
32733     applyConfig : function(config){
32734         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32735         
32736         if(config.split){
32737             if(!this.split){
32738                 
32739                 
32740                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,  {
32741                                 tag: "div",
32742                                 id: this.el.id + "-split",
32743                                 cls: "roo-layout-split roo-layout-split-"+this.position,
32744                                 html: "&#160;"
32745                 });
32746                 /** The SplitBar for this region 
32747                 * @type Roo.SplitBar */
32748                 // does not exist yet...
32749                 Roo.log([this.position, this.orientation]);
32750                 
32751                 this.split = new Roo.bootstrap.SplitBar({
32752                     dragElement : splitEl,
32753                     resizingElement: this.el,
32754                     orientation : this.orientation
32755                 });
32756                 
32757                 this.split.on("moved", this.onSplitMove, this);
32758                 this.split.useShim = config.useShim === true;
32759                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32760                 if(this.useSplitTips){
32761                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32762                 }
32763                 //if(config.collapsible){
32764                 //    this.split.el.on("dblclick", this.collapse,  this);
32765                 //}
32766             }
32767             if(typeof config.minSize != "undefined"){
32768                 this.split.minSize = config.minSize;
32769             }
32770             if(typeof config.maxSize != "undefined"){
32771                 this.split.maxSize = config.maxSize;
32772             }
32773             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32774                 this.hideSplitter();
32775             }
32776         }
32777     },
32778
32779     getHMaxSize : function(){
32780          var cmax = this.config.maxSize || 10000;
32781          var center = this.mgr.getRegion("center");
32782          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32783     },
32784
32785     getVMaxSize : function(){
32786          var cmax = this.config.maxSize || 10000;
32787          var center = this.mgr.getRegion("center");
32788          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32789     },
32790
32791     onSplitMove : function(split, newSize){
32792         this.fireEvent("resized", this, newSize);
32793     },
32794     
32795     /** 
32796      * Returns the {@link Roo.SplitBar} for this region.
32797      * @return {Roo.SplitBar}
32798      */
32799     getSplitBar : function(){
32800         return this.split;
32801     },
32802     
32803     hide : function(){
32804         this.hideSplitter();
32805         Roo.bootstrap.layout.Split.superclass.hide.call(this);
32806     },
32807
32808     hideSplitter : function(){
32809         if(this.split){
32810             this.split.el.setLocation(-2000,-2000);
32811             this.split.el.hide();
32812         }
32813     },
32814
32815     show : function(){
32816         if(this.split){
32817             this.split.el.show();
32818         }
32819         Roo.bootstrap.layout.Split.superclass.show.call(this);
32820     },
32821     
32822     beforeSlide: function(){
32823         if(Roo.isGecko){// firefox overflow auto bug workaround
32824             this.bodyEl.clip();
32825             if(this.tabs) {
32826                 this.tabs.bodyEl.clip();
32827             }
32828             if(this.activePanel){
32829                 this.activePanel.getEl().clip();
32830                 
32831                 if(this.activePanel.beforeSlide){
32832                     this.activePanel.beforeSlide();
32833                 }
32834             }
32835         }
32836     },
32837     
32838     afterSlide : function(){
32839         if(Roo.isGecko){// firefox overflow auto bug workaround
32840             this.bodyEl.unclip();
32841             if(this.tabs) {
32842                 this.tabs.bodyEl.unclip();
32843             }
32844             if(this.activePanel){
32845                 this.activePanel.getEl().unclip();
32846                 if(this.activePanel.afterSlide){
32847                     this.activePanel.afterSlide();
32848                 }
32849             }
32850         }
32851     },
32852
32853     initAutoHide : function(){
32854         if(this.autoHide !== false){
32855             if(!this.autoHideHd){
32856                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32857                 this.autoHideHd = {
32858                     "mouseout": function(e){
32859                         if(!e.within(this.el, true)){
32860                             st.delay(500);
32861                         }
32862                     },
32863                     "mouseover" : function(e){
32864                         st.cancel();
32865                     },
32866                     scope : this
32867                 };
32868             }
32869             this.el.on(this.autoHideHd);
32870         }
32871     },
32872
32873     clearAutoHide : function(){
32874         if(this.autoHide !== false){
32875             this.el.un("mouseout", this.autoHideHd.mouseout);
32876             this.el.un("mouseover", this.autoHideHd.mouseover);
32877         }
32878     },
32879
32880     clearMonitor : function(){
32881         Roo.get(document).un("click", this.slideInIf, this);
32882     },
32883
32884     // these names are backwards but not changed for compat
32885     slideOut : function(){
32886         if(this.isSlid || this.el.hasActiveFx()){
32887             return;
32888         }
32889         this.isSlid = true;
32890         if(this.collapseBtn){
32891             this.collapseBtn.hide();
32892         }
32893         this.closeBtnState = this.closeBtn.getStyle('display');
32894         this.closeBtn.hide();
32895         if(this.stickBtn){
32896             this.stickBtn.show();
32897         }
32898         this.el.show();
32899         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32900         this.beforeSlide();
32901         this.el.setStyle("z-index", 10001);
32902         this.el.slideIn(this.getSlideAnchor(), {
32903             callback: function(){
32904                 this.afterSlide();
32905                 this.initAutoHide();
32906                 Roo.get(document).on("click", this.slideInIf, this);
32907                 this.fireEvent("slideshow", this);
32908             },
32909             scope: this,
32910             block: true
32911         });
32912     },
32913
32914     afterSlideIn : function(){
32915         this.clearAutoHide();
32916         this.isSlid = false;
32917         this.clearMonitor();
32918         this.el.setStyle("z-index", "");
32919         if(this.collapseBtn){
32920             this.collapseBtn.show();
32921         }
32922         this.closeBtn.setStyle('display', this.closeBtnState);
32923         if(this.stickBtn){
32924             this.stickBtn.hide();
32925         }
32926         this.fireEvent("slidehide", this);
32927     },
32928
32929     slideIn : function(cb){
32930         if(!this.isSlid || this.el.hasActiveFx()){
32931             Roo.callback(cb);
32932             return;
32933         }
32934         this.isSlid = false;
32935         this.beforeSlide();
32936         this.el.slideOut(this.getSlideAnchor(), {
32937             callback: function(){
32938                 this.el.setLeftTop(-10000, -10000);
32939                 this.afterSlide();
32940                 this.afterSlideIn();
32941                 Roo.callback(cb);
32942             },
32943             scope: this,
32944             block: true
32945         });
32946     },
32947     
32948     slideInIf : function(e){
32949         if(!e.within(this.el)){
32950             this.slideIn();
32951         }
32952     },
32953
32954     animateCollapse : function(){
32955         this.beforeSlide();
32956         this.el.setStyle("z-index", 20000);
32957         var anchor = this.getSlideAnchor();
32958         this.el.slideOut(anchor, {
32959             callback : function(){
32960                 this.el.setStyle("z-index", "");
32961                 this.collapsedEl.slideIn(anchor, {duration:.3});
32962                 this.afterSlide();
32963                 this.el.setLocation(-10000,-10000);
32964                 this.el.hide();
32965                 this.fireEvent("collapsed", this);
32966             },
32967             scope: this,
32968             block: true
32969         });
32970     },
32971
32972     animateExpand : function(){
32973         this.beforeSlide();
32974         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32975         this.el.setStyle("z-index", 20000);
32976         this.collapsedEl.hide({
32977             duration:.1
32978         });
32979         this.el.slideIn(this.getSlideAnchor(), {
32980             callback : function(){
32981                 this.el.setStyle("z-index", "");
32982                 this.afterSlide();
32983                 if(this.split){
32984                     this.split.el.show();
32985                 }
32986                 this.fireEvent("invalidated", this);
32987                 this.fireEvent("expanded", this);
32988             },
32989             scope: this,
32990             block: true
32991         });
32992     },
32993
32994     anchors : {
32995         "west" : "left",
32996         "east" : "right",
32997         "north" : "top",
32998         "south" : "bottom"
32999     },
33000
33001     sanchors : {
33002         "west" : "l",
33003         "east" : "r",
33004         "north" : "t",
33005         "south" : "b"
33006     },
33007
33008     canchors : {
33009         "west" : "tl-tr",
33010         "east" : "tr-tl",
33011         "north" : "tl-bl",
33012         "south" : "bl-tl"
33013     },
33014
33015     getAnchor : function(){
33016         return this.anchors[this.position];
33017     },
33018
33019     getCollapseAnchor : function(){
33020         return this.canchors[this.position];
33021     },
33022
33023     getSlideAnchor : function(){
33024         return this.sanchors[this.position];
33025     },
33026
33027     getAlignAdj : function(){
33028         var cm = this.cmargins;
33029         switch(this.position){
33030             case "west":
33031                 return [0, 0];
33032             break;
33033             case "east":
33034                 return [0, 0];
33035             break;
33036             case "north":
33037                 return [0, 0];
33038             break;
33039             case "south":
33040                 return [0, 0];
33041             break;
33042         }
33043     },
33044
33045     getExpandAdj : function(){
33046         var c = this.collapsedEl, cm = this.cmargins;
33047         switch(this.position){
33048             case "west":
33049                 return [-(cm.right+c.getWidth()+cm.left), 0];
33050             break;
33051             case "east":
33052                 return [cm.right+c.getWidth()+cm.left, 0];
33053             break;
33054             case "north":
33055                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33056             break;
33057             case "south":
33058                 return [0, cm.top+cm.bottom+c.getHeight()];
33059             break;
33060         }
33061     }
33062 });/*
33063  * Based on:
33064  * Ext JS Library 1.1.1
33065  * Copyright(c) 2006-2007, Ext JS, LLC.
33066  *
33067  * Originally Released Under LGPL - original licence link has changed is not relivant.
33068  *
33069  * Fork - LGPL
33070  * <script type="text/javascript">
33071  */
33072 /*
33073  * These classes are private internal classes
33074  */
33075 Roo.bootstrap.layout.Center = function(config){
33076     config.region = "center";
33077     Roo.bootstrap.layout.Region.call(this, config);
33078     this.visible = true;
33079     this.minWidth = config.minWidth || 20;
33080     this.minHeight = config.minHeight || 20;
33081 };
33082
33083 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33084     hide : function(){
33085         // center panel can't be hidden
33086     },
33087     
33088     show : function(){
33089         // center panel can't be hidden
33090     },
33091     
33092     getMinWidth: function(){
33093         return this.minWidth;
33094     },
33095     
33096     getMinHeight: function(){
33097         return this.minHeight;
33098     }
33099 });
33100
33101
33102
33103
33104  
33105
33106
33107
33108
33109
33110 Roo.bootstrap.layout.North = function(config)
33111 {
33112     config.region = 'north';
33113     config.cursor = 'n-resize';
33114     
33115     Roo.bootstrap.layout.Split.call(this, config);
33116     if(this.split){
33117         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33118         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33119         this.split.el.addClass("roo-layout-split-v");
33120     }
33121     var size = config.initialSize || config.height;
33122     if(typeof size != "undefined"){
33123         this.el.setHeight(size);
33124     }
33125 };
33126 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33127 {
33128     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33129     getBox : function(){
33130         if(this.collapsed){
33131             return this.collapsedEl.getBox();
33132         }
33133         var box = this.el.getBox();
33134         if(this.split){
33135             box.height += this.split.el.getHeight();
33136         }
33137         return box;
33138     },
33139     
33140     updateBox : function(box){
33141         if(this.split && !this.collapsed){
33142             box.height -= this.split.el.getHeight();
33143             this.split.el.setLeft(box.x);
33144             this.split.el.setTop(box.y+box.height);
33145             this.split.el.setWidth(box.width);
33146         }
33147         if(this.collapsed){
33148             this.updateBody(box.width, null);
33149         }
33150         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33151     }
33152 });
33153
33154
33155
33156
33157
33158 Roo.bootstrap.layout.South = function(config){
33159     config.region = 'south';
33160     config.cursor = 's-resize';
33161     Roo.bootstrap.layout.Split.call(this, config);
33162     if(this.split){
33163         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33164         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33165         this.split.el.addClass("roo-layout-split-v");
33166     }
33167     var size = config.initialSize || config.height;
33168     if(typeof size != "undefined"){
33169         this.el.setHeight(size);
33170     }
33171 };
33172
33173 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33174     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33175     getBox : function(){
33176         if(this.collapsed){
33177             return this.collapsedEl.getBox();
33178         }
33179         var box = this.el.getBox();
33180         if(this.split){
33181             var sh = this.split.el.getHeight();
33182             box.height += sh;
33183             box.y -= sh;
33184         }
33185         return box;
33186     },
33187     
33188     updateBox : function(box){
33189         if(this.split && !this.collapsed){
33190             var sh = this.split.el.getHeight();
33191             box.height -= sh;
33192             box.y += sh;
33193             this.split.el.setLeft(box.x);
33194             this.split.el.setTop(box.y-sh);
33195             this.split.el.setWidth(box.width);
33196         }
33197         if(this.collapsed){
33198             this.updateBody(box.width, null);
33199         }
33200         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33201     }
33202 });
33203
33204 Roo.bootstrap.layout.East = function(config){
33205     config.region = "east";
33206     config.cursor = "e-resize";
33207     Roo.bootstrap.layout.Split.call(this, config);
33208     if(this.split){
33209         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33210         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33211         this.split.el.addClass("roo-layout-split-h");
33212     }
33213     var size = config.initialSize || config.width;
33214     if(typeof size != "undefined"){
33215         this.el.setWidth(size);
33216     }
33217 };
33218 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33219     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33220     getBox : function(){
33221         if(this.collapsed){
33222             return this.collapsedEl.getBox();
33223         }
33224         var box = this.el.getBox();
33225         if(this.split){
33226             var sw = this.split.el.getWidth();
33227             box.width += sw;
33228             box.x -= sw;
33229         }
33230         return box;
33231     },
33232
33233     updateBox : function(box){
33234         if(this.split && !this.collapsed){
33235             var sw = this.split.el.getWidth();
33236             box.width -= sw;
33237             this.split.el.setLeft(box.x);
33238             this.split.el.setTop(box.y);
33239             this.split.el.setHeight(box.height);
33240             box.x += sw;
33241         }
33242         if(this.collapsed){
33243             this.updateBody(null, box.height);
33244         }
33245         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33246     }
33247 });
33248
33249 Roo.bootstrap.layout.West = function(config){
33250     config.region = "west";
33251     config.cursor = "w-resize";
33252     
33253     Roo.bootstrap.layout.Split.call(this, config);
33254     if(this.split){
33255         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33256         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33257         this.split.el.addClass("roo-layout-split-h");
33258     }
33259     var size = config.initialSize || config.width;
33260     if(typeof size != "undefined"){
33261         this.el.setWidth(size);
33262     }
33263 };
33264 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33265     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33266     getBox : function(){
33267         if(this.collapsed){
33268             return this.collapsedEl.getBox();
33269         }
33270         var box = this.el.getBox();
33271         if(this.split){
33272             box.width += this.split.el.getWidth();
33273         }
33274         return box;
33275     },
33276     
33277     updateBox : function(box){
33278         if(this.split && !this.collapsed){
33279             var sw = this.split.el.getWidth();
33280             box.width -= sw;
33281             this.split.el.setLeft(box.x+box.width);
33282             this.split.el.setTop(box.y);
33283             this.split.el.setHeight(box.height);
33284         }
33285         if(this.collapsed){
33286             this.updateBody(null, box.height);
33287         }
33288         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33289     }
33290 });
33291 Roo.namespace("Roo.bootstrap.panel");/*
33292  * Based on:
33293  * Ext JS Library 1.1.1
33294  * Copyright(c) 2006-2007, Ext JS, LLC.
33295  *
33296  * Originally Released Under LGPL - original licence link has changed is not relivant.
33297  *
33298  * Fork - LGPL
33299  * <script type="text/javascript">
33300  */
33301 /**
33302  * @class Roo.ContentPanel
33303  * @extends Roo.util.Observable
33304  * A basic ContentPanel element.
33305  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33306  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33307  * @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
33308  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33309  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33310  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33311  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33312  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33313  * @cfg {String} title          The title for this panel
33314  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33315  * @cfg {String} url            Calls {@link #setUrl} with this value
33316  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33317  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33318  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33319  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33320
33321  * @constructor
33322  * Create a new ContentPanel.
33323  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33324  * @param {String/Object} config A string to set only the title or a config object
33325  * @param {String} content (optional) Set the HTML content for this panel
33326  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33327  */
33328 Roo.bootstrap.panel.Content = function( config){
33329     
33330     var el = config.el;
33331     var content = config.content;
33332
33333     if(config.autoCreate){ // xtype is available if this is called from factory
33334         el = Roo.id();
33335     }
33336     this.el = Roo.get(el);
33337     if(!this.el && config && config.autoCreate){
33338         if(typeof config.autoCreate == "object"){
33339             if(!config.autoCreate.id){
33340                 config.autoCreate.id = config.id||el;
33341             }
33342             this.el = Roo.DomHelper.append(document.body,
33343                         config.autoCreate, true);
33344         }else{
33345             var elcfg =  {   tag: "div",
33346                             cls: "roo-layout-inactive-content",
33347                             id: config.id||el
33348                             };
33349             if (config.html) {
33350                 elcfg.html = config.html;
33351                 
33352             }
33353                         
33354             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33355         }
33356     } 
33357     this.closable = false;
33358     this.loaded = false;
33359     this.active = false;
33360     if(typeof config == "string"){
33361         this.title = config;
33362     }else{
33363         Roo.apply(this, config);
33364     }
33365     /*
33366     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33367         this.wrapEl = this.el.wrap();
33368         this.toolbar.container = this.el.insertSibling(false, 'before');
33369         this.toolbar = new Roo.Toolbar(this.toolbar);
33370     }
33371     
33372     // xtype created footer. - not sure if will work as we normally have to render first..
33373     if (this.footer && !this.footer.el && this.footer.xtype) {
33374         if (!this.wrapEl) {
33375             this.wrapEl = this.el.wrap();
33376         }
33377     
33378         this.footer.container = this.wrapEl.createChild();
33379          
33380         this.footer = Roo.factory(this.footer, Roo);
33381         
33382     }
33383     */
33384     if(this.resizeEl){
33385         this.resizeEl = Roo.get(this.resizeEl, true);
33386     }else{
33387         this.resizeEl = this.el;
33388     }
33389     // handle view.xtype
33390     
33391  
33392     
33393     
33394     this.addEvents({
33395         /**
33396          * @event activate
33397          * Fires when this panel is activated. 
33398          * @param {Roo.ContentPanel} this
33399          */
33400         "activate" : true,
33401         /**
33402          * @event deactivate
33403          * Fires when this panel is activated. 
33404          * @param {Roo.ContentPanel} this
33405          */
33406         "deactivate" : true,
33407
33408         /**
33409          * @event resize
33410          * Fires when this panel is resized if fitToFrame is true.
33411          * @param {Roo.ContentPanel} this
33412          * @param {Number} width The width after any component adjustments
33413          * @param {Number} height The height after any component adjustments
33414          */
33415         "resize" : true,
33416         
33417          /**
33418          * @event render
33419          * Fires when this tab is created
33420          * @param {Roo.ContentPanel} this
33421          */
33422         "render" : true
33423         
33424         
33425         
33426     });
33427     
33428
33429     
33430     
33431     if(this.autoScroll){
33432         this.resizeEl.setStyle("overflow", "auto");
33433     } else {
33434         // fix randome scrolling
33435         this.el.on('scroll', function() {
33436             Roo.log('fix random scolling');
33437             this.scrollTo('top',0); 
33438         });
33439     }
33440     content = content || this.content;
33441     if(content){
33442         this.setContent(content);
33443     }
33444     if(config && config.url){
33445         this.setUrl(this.url, this.params, this.loadOnce);
33446     }
33447     
33448     
33449     
33450     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33451     
33452     if (this.view && typeof(this.view.xtype) != 'undefined') {
33453         this.view.el = this.el.appendChild(document.createElement("div"));
33454         this.view = Roo.factory(this.view); 
33455         this.view.render  &&  this.view.render(false, '');  
33456     }
33457     
33458     
33459     this.fireEvent('render', this);
33460 };
33461
33462 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33463     tabTip:'',
33464     setRegion : function(region){
33465         this.region = region;
33466         if(region){
33467            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33468         }else{
33469            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33470         } 
33471     },
33472     
33473     /**
33474      * Returns the toolbar for this Panel if one was configured. 
33475      * @return {Roo.Toolbar} 
33476      */
33477     getToolbar : function(){
33478         return this.toolbar;
33479     },
33480     
33481     setActiveState : function(active){
33482         this.active = active;
33483         if(!active){
33484             this.fireEvent("deactivate", this);
33485         }else{
33486             this.fireEvent("activate", this);
33487         }
33488     },
33489     /**
33490      * Updates this panel's element
33491      * @param {String} content The new content
33492      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33493     */
33494     setContent : function(content, loadScripts){
33495         this.el.update(content, loadScripts);
33496     },
33497
33498     ignoreResize : function(w, h){
33499         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33500             return true;
33501         }else{
33502             this.lastSize = {width: w, height: h};
33503             return false;
33504         }
33505     },
33506     /**
33507      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33508      * @return {Roo.UpdateManager} The UpdateManager
33509      */
33510     getUpdateManager : function(){
33511         return this.el.getUpdateManager();
33512     },
33513      /**
33514      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33515      * @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:
33516 <pre><code>
33517 panel.load({
33518     url: "your-url.php",
33519     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33520     callback: yourFunction,
33521     scope: yourObject, //(optional scope)
33522     discardUrl: false,
33523     nocache: false,
33524     text: "Loading...",
33525     timeout: 30,
33526     scripts: false
33527 });
33528 </code></pre>
33529      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33530      * 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.
33531      * @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}
33532      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33533      * @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.
33534      * @return {Roo.ContentPanel} this
33535      */
33536     load : function(){
33537         var um = this.el.getUpdateManager();
33538         um.update.apply(um, arguments);
33539         return this;
33540     },
33541
33542
33543     /**
33544      * 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.
33545      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33546      * @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)
33547      * @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)
33548      * @return {Roo.UpdateManager} The UpdateManager
33549      */
33550     setUrl : function(url, params, loadOnce){
33551         if(this.refreshDelegate){
33552             this.removeListener("activate", this.refreshDelegate);
33553         }
33554         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33555         this.on("activate", this.refreshDelegate);
33556         return this.el.getUpdateManager();
33557     },
33558     
33559     _handleRefresh : function(url, params, loadOnce){
33560         if(!loadOnce || !this.loaded){
33561             var updater = this.el.getUpdateManager();
33562             updater.update(url, params, this._setLoaded.createDelegate(this));
33563         }
33564     },
33565     
33566     _setLoaded : function(){
33567         this.loaded = true;
33568     }, 
33569     
33570     /**
33571      * Returns this panel's id
33572      * @return {String} 
33573      */
33574     getId : function(){
33575         return this.el.id;
33576     },
33577     
33578     /** 
33579      * Returns this panel's element - used by regiosn to add.
33580      * @return {Roo.Element} 
33581      */
33582     getEl : function(){
33583         return this.wrapEl || this.el;
33584     },
33585     
33586    
33587     
33588     adjustForComponents : function(width, height)
33589     {
33590         //Roo.log('adjustForComponents ');
33591         if(this.resizeEl != this.el){
33592             width -= this.el.getFrameWidth('lr');
33593             height -= this.el.getFrameWidth('tb');
33594         }
33595         if(this.toolbar){
33596             var te = this.toolbar.getEl();
33597             height -= te.getHeight();
33598             te.setWidth(width);
33599         }
33600         if(this.footer){
33601             var te = this.footer.getEl();
33602             Roo.log("footer:" + te.getHeight());
33603             
33604             height -= te.getHeight();
33605             te.setWidth(width);
33606         }
33607         
33608         
33609         if(this.adjustments){
33610             width += this.adjustments[0];
33611             height += this.adjustments[1];
33612         }
33613         return {"width": width, "height": height};
33614     },
33615     
33616     setSize : function(width, height){
33617         if(this.fitToFrame && !this.ignoreResize(width, height)){
33618             if(this.fitContainer && this.resizeEl != this.el){
33619                 this.el.setSize(width, height);
33620             }
33621             var size = this.adjustForComponents(width, height);
33622             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33623             this.fireEvent('resize', this, size.width, size.height);
33624         }
33625     },
33626     
33627     /**
33628      * Returns this panel's title
33629      * @return {String} 
33630      */
33631     getTitle : function(){
33632         return this.title;
33633     },
33634     
33635     /**
33636      * Set this panel's title
33637      * @param {String} title
33638      */
33639     setTitle : function(title){
33640         this.title = title;
33641         if(this.region){
33642             this.region.updatePanelTitle(this, title);
33643         }
33644     },
33645     
33646     /**
33647      * Returns true is this panel was configured to be closable
33648      * @return {Boolean} 
33649      */
33650     isClosable : function(){
33651         return this.closable;
33652     },
33653     
33654     beforeSlide : function(){
33655         this.el.clip();
33656         this.resizeEl.clip();
33657     },
33658     
33659     afterSlide : function(){
33660         this.el.unclip();
33661         this.resizeEl.unclip();
33662     },
33663     
33664     /**
33665      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33666      *   Will fail silently if the {@link #setUrl} method has not been called.
33667      *   This does not activate the panel, just updates its content.
33668      */
33669     refresh : function(){
33670         if(this.refreshDelegate){
33671            this.loaded = false;
33672            this.refreshDelegate();
33673         }
33674     },
33675     
33676     /**
33677      * Destroys this panel
33678      */
33679     destroy : function(){
33680         this.el.removeAllListeners();
33681         var tempEl = document.createElement("span");
33682         tempEl.appendChild(this.el.dom);
33683         tempEl.innerHTML = "";
33684         this.el.remove();
33685         this.el = null;
33686     },
33687     
33688     /**
33689      * form - if the content panel contains a form - this is a reference to it.
33690      * @type {Roo.form.Form}
33691      */
33692     form : false,
33693     /**
33694      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33695      *    This contains a reference to it.
33696      * @type {Roo.View}
33697      */
33698     view : false,
33699     
33700       /**
33701      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33702      * <pre><code>
33703
33704 layout.addxtype({
33705        xtype : 'Form',
33706        items: [ .... ]
33707    }
33708 );
33709
33710 </code></pre>
33711      * @param {Object} cfg Xtype definition of item to add.
33712      */
33713     
33714     
33715     getChildContainer: function () {
33716         return this.getEl();
33717     }
33718     
33719     
33720     /*
33721         var  ret = new Roo.factory(cfg);
33722         return ret;
33723         
33724         
33725         // add form..
33726         if (cfg.xtype.match(/^Form$/)) {
33727             
33728             var el;
33729             //if (this.footer) {
33730             //    el = this.footer.container.insertSibling(false, 'before');
33731             //} else {
33732                 el = this.el.createChild();
33733             //}
33734
33735             this.form = new  Roo.form.Form(cfg);
33736             
33737             
33738             if ( this.form.allItems.length) {
33739                 this.form.render(el.dom);
33740             }
33741             return this.form;
33742         }
33743         // should only have one of theses..
33744         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33745             // views.. should not be just added - used named prop 'view''
33746             
33747             cfg.el = this.el.appendChild(document.createElement("div"));
33748             // factory?
33749             
33750             var ret = new Roo.factory(cfg);
33751              
33752              ret.render && ret.render(false, ''); // render blank..
33753             this.view = ret;
33754             return ret;
33755         }
33756         return false;
33757     }
33758     \*/
33759 });
33760  
33761 /**
33762  * @class Roo.bootstrap.panel.Grid
33763  * @extends Roo.bootstrap.panel.Content
33764  * @constructor
33765  * Create a new GridPanel.
33766  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33767  * @param {String/Object} config A string to set only the panel's title, or a config object
33768
33769   new Roo.bootstrap.panel.Grid({
33770                 grid: .....
33771                 ....
33772   }
33773
33774  */
33775
33776
33777
33778 Roo.bootstrap.panel.Grid = function(config){
33779     
33780   
33781     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33782         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33783
33784     
33785     if(config.toolbar){
33786         var tool_el = this.wrapper.createChild();    
33787         this.toolbar = Roo.factory(config.toolbar);
33788         var ti = [];
33789         if (config.toolbar.items) {
33790             ti = config.toolbar.items ;
33791             delete config.toolbar.items ;
33792         }
33793         
33794         var nitems = [];
33795         this.toolbar.render(tool_el);
33796         for(var i =0;i < ti.length;i++) {
33797           //  Roo.log(['add child', items[i]]);
33798             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33799         }
33800         this.toolbar.items = nitems;
33801         
33802         delete config.toolbar;
33803     }
33804     
33805     this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
33806     config.el = this.wrapper;
33807     
33808     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
33809     
33810   
33811     // xtype created footer. - not sure if will work as we normally have to render first..
33812     if (this.footer && !this.footer.el && this.footer.xtype) {
33813         
33814         var ctr = this.grid.getView().getFooterPanel(true);
33815         this.footer.dataSource = this.grid.dataSource;
33816         this.footer = Roo.factory(this.footer, Roo);
33817         this.footer.render(ctr);
33818         
33819     }
33820     
33821     
33822     config.grid.monitorWindowResize = false; // turn off autosizing
33823     config.grid.autoHeight = false;
33824     config.grid.autoWidth = false;
33825     this.grid = config.grid;
33826     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33827     
33828      
33829 };
33830
33831 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
33832     getId : function(){
33833         return this.grid.id;
33834     },
33835     
33836     /**
33837      * Returns the grid for this panel
33838      * @return {Roo.bootstrap.Table} 
33839      */
33840     getGrid : function(){
33841         return this.grid;    
33842     },
33843     
33844     setSize : function(width, height){
33845         if(!this.ignoreResize(width, height)){
33846             var grid = this.grid;
33847             var size = this.adjustForComponents(width, height);
33848             grid.getGridEl().setSize(size.width, size.height);
33849             /*
33850             var thd = grid.getGridEl().select('thead',true).first();
33851             var tbd = grid.getGridEl().select('tbody', true).first();
33852             if (tbd) {
33853                 tbd.setSize(width, height - thd.getHeight());
33854             }
33855             */
33856             grid.autoSize();
33857         }
33858     },
33859      
33860     
33861     
33862     beforeSlide : function(){
33863         this.grid.getView().scroller.clip();
33864     },
33865     
33866     afterSlide : function(){
33867         this.grid.getView().scroller.unclip();
33868     },
33869     
33870     destroy : function(){
33871         this.grid.destroy();
33872         delete this.grid;
33873         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
33874     }
33875 });
33876
33877 /**
33878  * @class Roo.bootstrap.panel.Nest
33879  * @extends Roo.bootstrap.panel.Content
33880  * @constructor
33881  * Create a new Panel, that can contain a layout.Border.
33882  * 
33883  * 
33884  * @param {Roo.BorderLayout} layout The layout for this panel
33885  * @param {String/Object} config A string to set only the title or a config object
33886  */
33887 Roo.bootstrap.panel.Nest = function(config)
33888 {
33889     // construct with only one argument..
33890     /* FIXME - implement nicer consturctors
33891     if (layout.layout) {
33892         config = layout;
33893         layout = config.layout;
33894         delete config.layout;
33895     }
33896     if (layout.xtype && !layout.getEl) {
33897         // then layout needs constructing..
33898         layout = Roo.factory(layout, Roo);
33899     }
33900     */
33901     
33902     config.el =  config.layout.getEl();
33903     
33904     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
33905     
33906     config.layout.monitorWindowResize = false; // turn off autosizing
33907     this.layout = config.layout;
33908     this.layout.getEl().addClass("roo-layout-nested-layout");
33909     
33910     
33911     
33912     
33913 };
33914
33915 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
33916
33917     setSize : function(width, height){
33918         if(!this.ignoreResize(width, height)){
33919             var size = this.adjustForComponents(width, height);
33920             var el = this.layout.getEl();
33921             el.setSize(size.width, size.height);
33922             var touch = el.dom.offsetWidth;
33923             this.layout.layout();
33924             // ie requires a double layout on the first pass
33925             if(Roo.isIE && !this.initialized){
33926                 this.initialized = true;
33927                 this.layout.layout();
33928             }
33929         }
33930     },
33931     
33932     // activate all subpanels if not currently active..
33933     
33934     setActiveState : function(active){
33935         this.active = active;
33936         if(!active){
33937             this.fireEvent("deactivate", this);
33938             return;
33939         }
33940         
33941         this.fireEvent("activate", this);
33942         // not sure if this should happen before or after..
33943         if (!this.layout) {
33944             return; // should not happen..
33945         }
33946         var reg = false;
33947         for (var r in this.layout.regions) {
33948             reg = this.layout.getRegion(r);
33949             if (reg.getActivePanel()) {
33950                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33951                 reg.setActivePanel(reg.getActivePanel());
33952                 continue;
33953             }
33954             if (!reg.panels.length) {
33955                 continue;
33956             }
33957             reg.showPanel(reg.getPanel(0));
33958         }
33959         
33960         
33961         
33962         
33963     },
33964     
33965     /**
33966      * Returns the nested BorderLayout for this panel
33967      * @return {Roo.BorderLayout} 
33968      */
33969     getLayout : function(){
33970         return this.layout;
33971     },
33972     
33973      /**
33974      * Adds a xtype elements to the layout of the nested panel
33975      * <pre><code>
33976
33977 panel.addxtype({
33978        xtype : 'ContentPanel',
33979        region: 'west',
33980        items: [ .... ]
33981    }
33982 );
33983
33984 panel.addxtype({
33985         xtype : 'NestedLayoutPanel',
33986         region: 'west',
33987         layout: {
33988            center: { },
33989            west: { }   
33990         },
33991         items : [ ... list of content panels or nested layout panels.. ]
33992    }
33993 );
33994 </code></pre>
33995      * @param {Object} cfg Xtype definition of item to add.
33996      */
33997     addxtype : function(cfg) {
33998         return this.layout.addxtype(cfg);
33999     
34000     }
34001 });        /*
34002  * Based on:
34003  * Ext JS Library 1.1.1
34004  * Copyright(c) 2006-2007, Ext JS, LLC.
34005  *
34006  * Originally Released Under LGPL - original licence link has changed is not relivant.
34007  *
34008  * Fork - LGPL
34009  * <script type="text/javascript">
34010  */
34011 /**
34012  * @class Roo.TabPanel
34013  * @extends Roo.util.Observable
34014  * A lightweight tab container.
34015  * <br><br>
34016  * Usage:
34017  * <pre><code>
34018 // basic tabs 1, built from existing content
34019 var tabs = new Roo.TabPanel("tabs1");
34020 tabs.addTab("script", "View Script");
34021 tabs.addTab("markup", "View Markup");
34022 tabs.activate("script");
34023
34024 // more advanced tabs, built from javascript
34025 var jtabs = new Roo.TabPanel("jtabs");
34026 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34027
34028 // set up the UpdateManager
34029 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34030 var updater = tab2.getUpdateManager();
34031 updater.setDefaultUrl("ajax1.htm");
34032 tab2.on('activate', updater.refresh, updater, true);
34033
34034 // Use setUrl for Ajax loading
34035 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34036 tab3.setUrl("ajax2.htm", null, true);
34037
34038 // Disabled tab
34039 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34040 tab4.disable();
34041
34042 jtabs.activate("jtabs-1");
34043  * </code></pre>
34044  * @constructor
34045  * Create a new TabPanel.
34046  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34047  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34048  */
34049 Roo.bootstrap.panel.Tabs = function(config){
34050     /**
34051     * The container element for this TabPanel.
34052     * @type Roo.Element
34053     */
34054     this.el = Roo.get(config.el);
34055     delete config.el;
34056     if(config){
34057         if(typeof config == "boolean"){
34058             this.tabPosition = config ? "bottom" : "top";
34059         }else{
34060             Roo.apply(this, config);
34061         }
34062     }
34063     
34064     if(this.tabPosition == "bottom"){
34065         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34066         this.el.addClass("roo-tabs-bottom");
34067     }
34068     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34069     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34070     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34071     if(Roo.isIE){
34072         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34073     }
34074     if(this.tabPosition != "bottom"){
34075         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34076          * @type Roo.Element
34077          */
34078         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34079         this.el.addClass("roo-tabs-top");
34080     }
34081     this.items = [];
34082
34083     this.bodyEl.setStyle("position", "relative");
34084
34085     this.active = null;
34086     this.activateDelegate = this.activate.createDelegate(this);
34087
34088     this.addEvents({
34089         /**
34090          * @event tabchange
34091          * Fires when the active tab changes
34092          * @param {Roo.TabPanel} this
34093          * @param {Roo.TabPanelItem} activePanel The new active tab
34094          */
34095         "tabchange": true,
34096         /**
34097          * @event beforetabchange
34098          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34099          * @param {Roo.TabPanel} this
34100          * @param {Object} e Set cancel to true on this object to cancel the tab change
34101          * @param {Roo.TabPanelItem} tab The tab being changed to
34102          */
34103         "beforetabchange" : true
34104     });
34105
34106     Roo.EventManager.onWindowResize(this.onResize, this);
34107     this.cpad = this.el.getPadding("lr");
34108     this.hiddenCount = 0;
34109
34110
34111     // toolbar on the tabbar support...
34112     if (this.toolbar) {
34113         alert("no toolbar support yet");
34114         this.toolbar  = false;
34115         /*
34116         var tcfg = this.toolbar;
34117         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34118         this.toolbar = new Roo.Toolbar(tcfg);
34119         if (Roo.isSafari) {
34120             var tbl = tcfg.container.child('table', true);
34121             tbl.setAttribute('width', '100%');
34122         }
34123         */
34124         
34125     }
34126    
34127
34128
34129     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34130 };
34131
34132 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34133     /*
34134      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34135      */
34136     tabPosition : "top",
34137     /*
34138      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34139      */
34140     currentTabWidth : 0,
34141     /*
34142      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34143      */
34144     minTabWidth : 40,
34145     /*
34146      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34147      */
34148     maxTabWidth : 250,
34149     /*
34150      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34151      */
34152     preferredTabWidth : 175,
34153     /*
34154      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34155      */
34156     resizeTabs : false,
34157     /*
34158      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34159      */
34160     monitorResize : true,
34161     /*
34162      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34163      */
34164     toolbar : false,
34165
34166     /**
34167      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34168      * @param {String} id The id of the div to use <b>or create</b>
34169      * @param {String} text The text for the tab
34170      * @param {String} content (optional) Content to put in the TabPanelItem body
34171      * @param {Boolean} closable (optional) True to create a close icon on the tab
34172      * @return {Roo.TabPanelItem} The created TabPanelItem
34173      */
34174     addTab : function(id, text, content, closable)
34175     {
34176         var item = new Roo.bootstrap.panel.TabItem({
34177             panel: this,
34178             id : id,
34179             text : text,
34180             closable : closable
34181         });
34182         this.addTabItem(item);
34183         if(content){
34184             item.setContent(content);
34185         }
34186         return item;
34187     },
34188
34189     /**
34190      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34191      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34192      * @return {Roo.TabPanelItem}
34193      */
34194     getTab : function(id){
34195         return this.items[id];
34196     },
34197
34198     /**
34199      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34200      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34201      */
34202     hideTab : function(id){
34203         var t = this.items[id];
34204         if(!t.isHidden()){
34205            t.setHidden(true);
34206            this.hiddenCount++;
34207            this.autoSizeTabs();
34208         }
34209     },
34210
34211     /**
34212      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34213      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34214      */
34215     unhideTab : function(id){
34216         var t = this.items[id];
34217         if(t.isHidden()){
34218            t.setHidden(false);
34219            this.hiddenCount--;
34220            this.autoSizeTabs();
34221         }
34222     },
34223
34224     /**
34225      * Adds an existing {@link Roo.TabPanelItem}.
34226      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34227      */
34228     addTabItem : function(item){
34229         this.items[item.id] = item;
34230         this.items.push(item);
34231       //  if(this.resizeTabs){
34232     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34233   //         this.autoSizeTabs();
34234 //        }else{
34235 //            item.autoSize();
34236        // }
34237     },
34238
34239     /**
34240      * Removes a {@link Roo.TabPanelItem}.
34241      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34242      */
34243     removeTab : function(id){
34244         var items = this.items;
34245         var tab = items[id];
34246         if(!tab) { return; }
34247         var index = items.indexOf(tab);
34248         if(this.active == tab && items.length > 1){
34249             var newTab = this.getNextAvailable(index);
34250             if(newTab) {
34251                 newTab.activate();
34252             }
34253         }
34254         this.stripEl.dom.removeChild(tab.pnode.dom);
34255         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34256             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34257         }
34258         items.splice(index, 1);
34259         delete this.items[tab.id];
34260         tab.fireEvent("close", tab);
34261         tab.purgeListeners();
34262         this.autoSizeTabs();
34263     },
34264
34265     getNextAvailable : function(start){
34266         var items = this.items;
34267         var index = start;
34268         // look for a next tab that will slide over to
34269         // replace the one being removed
34270         while(index < items.length){
34271             var item = items[++index];
34272             if(item && !item.isHidden()){
34273                 return item;
34274             }
34275         }
34276         // if one isn't found select the previous tab (on the left)
34277         index = start;
34278         while(index >= 0){
34279             var item = items[--index];
34280             if(item && !item.isHidden()){
34281                 return item;
34282             }
34283         }
34284         return null;
34285     },
34286
34287     /**
34288      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34289      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34290      */
34291     disableTab : function(id){
34292         var tab = this.items[id];
34293         if(tab && this.active != tab){
34294             tab.disable();
34295         }
34296     },
34297
34298     /**
34299      * Enables a {@link Roo.TabPanelItem} that is disabled.
34300      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34301      */
34302     enableTab : function(id){
34303         var tab = this.items[id];
34304         tab.enable();
34305     },
34306
34307     /**
34308      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34309      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34310      * @return {Roo.TabPanelItem} The TabPanelItem.
34311      */
34312     activate : function(id){
34313         var tab = this.items[id];
34314         if(!tab){
34315             return null;
34316         }
34317         if(tab == this.active || tab.disabled){
34318             return tab;
34319         }
34320         var e = {};
34321         this.fireEvent("beforetabchange", this, e, tab);
34322         if(e.cancel !== true && !tab.disabled){
34323             if(this.active){
34324                 this.active.hide();
34325             }
34326             this.active = this.items[id];
34327             this.active.show();
34328             this.fireEvent("tabchange", this, this.active);
34329         }
34330         return tab;
34331     },
34332
34333     /**
34334      * Gets the active {@link Roo.TabPanelItem}.
34335      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34336      */
34337     getActiveTab : function(){
34338         return this.active;
34339     },
34340
34341     /**
34342      * Updates the tab body element to fit the height of the container element
34343      * for overflow scrolling
34344      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34345      */
34346     syncHeight : function(targetHeight){
34347         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34348         var bm = this.bodyEl.getMargins();
34349         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34350         this.bodyEl.setHeight(newHeight);
34351         return newHeight;
34352     },
34353
34354     onResize : function(){
34355         if(this.monitorResize){
34356             this.autoSizeTabs();
34357         }
34358     },
34359
34360     /**
34361      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34362      */
34363     beginUpdate : function(){
34364         this.updating = true;
34365     },
34366
34367     /**
34368      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34369      */
34370     endUpdate : function(){
34371         this.updating = false;
34372         this.autoSizeTabs();
34373     },
34374
34375     /**
34376      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34377      */
34378     autoSizeTabs : function(){
34379         var count = this.items.length;
34380         var vcount = count - this.hiddenCount;
34381         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34382             return;
34383         }
34384         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34385         var availWidth = Math.floor(w / vcount);
34386         var b = this.stripBody;
34387         if(b.getWidth() > w){
34388             var tabs = this.items;
34389             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34390             if(availWidth < this.minTabWidth){
34391                 /*if(!this.sleft){    // incomplete scrolling code
34392                     this.createScrollButtons();
34393                 }
34394                 this.showScroll();
34395                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34396             }
34397         }else{
34398             if(this.currentTabWidth < this.preferredTabWidth){
34399                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34400             }
34401         }
34402     },
34403
34404     /**
34405      * Returns the number of tabs in this TabPanel.
34406      * @return {Number}
34407      */
34408      getCount : function(){
34409          return this.items.length;
34410      },
34411
34412     /**
34413      * Resizes all the tabs to the passed width
34414      * @param {Number} The new width
34415      */
34416     setTabWidth : function(width){
34417         this.currentTabWidth = width;
34418         for(var i = 0, len = this.items.length; i < len; i++) {
34419                 if(!this.items[i].isHidden()) {
34420                 this.items[i].setWidth(width);
34421             }
34422         }
34423     },
34424
34425     /**
34426      * Destroys this TabPanel
34427      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34428      */
34429     destroy : function(removeEl){
34430         Roo.EventManager.removeResizeListener(this.onResize, this);
34431         for(var i = 0, len = this.items.length; i < len; i++){
34432             this.items[i].purgeListeners();
34433         }
34434         if(removeEl === true){
34435             this.el.update("");
34436             this.el.remove();
34437         }
34438     },
34439     
34440     createStrip : function(container)
34441     {
34442         var strip = document.createElement("nav");
34443         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34444         container.appendChild(strip);
34445         return strip;
34446     },
34447     
34448     createStripList : function(strip)
34449     {
34450         // div wrapper for retard IE
34451         // returns the "tr" element.
34452         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34453         //'<div class="x-tabs-strip-wrap">'+
34454           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34455           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34456         return strip.firstChild; //.firstChild.firstChild.firstChild;
34457     },
34458     createBody : function(container)
34459     {
34460         var body = document.createElement("div");
34461         Roo.id(body, "tab-body");
34462         //Roo.fly(body).addClass("x-tabs-body");
34463         Roo.fly(body).addClass("tab-content");
34464         container.appendChild(body);
34465         return body;
34466     },
34467     createItemBody :function(bodyEl, id){
34468         var body = Roo.getDom(id);
34469         if(!body){
34470             body = document.createElement("div");
34471             body.id = id;
34472         }
34473         //Roo.fly(body).addClass("x-tabs-item-body");
34474         Roo.fly(body).addClass("tab-pane");
34475          bodyEl.insertBefore(body, bodyEl.firstChild);
34476         return body;
34477     },
34478     /** @private */
34479     createStripElements :  function(stripEl, text, closable)
34480     {
34481         var td = document.createElement("li"); // was td..
34482         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34483         //stripEl.appendChild(td);
34484         /*if(closable){
34485             td.className = "x-tabs-closable";
34486             if(!this.closeTpl){
34487                 this.closeTpl = new Roo.Template(
34488                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34489                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34490                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34491                 );
34492             }
34493             var el = this.closeTpl.overwrite(td, {"text": text});
34494             var close = el.getElementsByTagName("div")[0];
34495             var inner = el.getElementsByTagName("em")[0];
34496             return {"el": el, "close": close, "inner": inner};
34497         } else {
34498         */
34499         // not sure what this is..
34500             if(!this.tabTpl){
34501                 //this.tabTpl = new Roo.Template(
34502                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34503                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34504                 //);
34505                 this.tabTpl = new Roo.Template(
34506                    '<a href="#">' +
34507                    '<span unselectable="on"' +
34508                             (this.disableTooltips ? '' : ' title="{text}"') +
34509                             ' >{text}</span></span></a>'
34510                 );
34511                 
34512             }
34513             var el = this.tabTpl.overwrite(td, {"text": text});
34514             var inner = el.getElementsByTagName("span")[0];
34515             return {"el": el, "inner": inner};
34516         //}
34517     }
34518         
34519     
34520 });
34521
34522 /**
34523  * @class Roo.TabPanelItem
34524  * @extends Roo.util.Observable
34525  * Represents an individual item (tab plus body) in a TabPanel.
34526  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34527  * @param {String} id The id of this TabPanelItem
34528  * @param {String} text The text for the tab of this TabPanelItem
34529  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34530  */
34531 Roo.bootstrap.panel.TabItem = function(config){
34532     /**
34533      * The {@link Roo.TabPanel} this TabPanelItem belongs to
34534      * @type Roo.TabPanel
34535      */
34536     this.tabPanel = config.panel;
34537     /**
34538      * The id for this TabPanelItem
34539      * @type String
34540      */
34541     this.id = config.id;
34542     /** @private */
34543     this.disabled = false;
34544     /** @private */
34545     this.text = config.text;
34546     /** @private */
34547     this.loaded = false;
34548     this.closable = config.closable;
34549
34550     /**
34551      * The body element for this TabPanelItem.
34552      * @type Roo.Element
34553      */
34554     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34555     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34556     this.bodyEl.setStyle("display", "block");
34557     this.bodyEl.setStyle("zoom", "1");
34558     //this.hideAction();
34559
34560     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34561     /** @private */
34562     this.el = Roo.get(els.el);
34563     this.inner = Roo.get(els.inner, true);
34564     this.textEl = Roo.get(this.el.dom.firstChild, true);
34565     this.pnode = Roo.get(els.el.parentNode, true);
34566     this.el.on("mousedown", this.onTabMouseDown, this);
34567     this.el.on("click", this.onTabClick, this);
34568     /** @private */
34569     if(config.closable){
34570         var c = Roo.get(els.close, true);
34571         c.dom.title = this.closeText;
34572         c.addClassOnOver("close-over");
34573         c.on("click", this.closeClick, this);
34574      }
34575
34576     this.addEvents({
34577          /**
34578          * @event activate
34579          * Fires when this tab becomes the active tab.
34580          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34581          * @param {Roo.TabPanelItem} this
34582          */
34583         "activate": true,
34584         /**
34585          * @event beforeclose
34586          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34587          * @param {Roo.TabPanelItem} this
34588          * @param {Object} e Set cancel to true on this object to cancel the close.
34589          */
34590         "beforeclose": true,
34591         /**
34592          * @event close
34593          * Fires when this tab is closed.
34594          * @param {Roo.TabPanelItem} this
34595          */
34596          "close": true,
34597         /**
34598          * @event deactivate
34599          * Fires when this tab is no longer the active tab.
34600          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34601          * @param {Roo.TabPanelItem} this
34602          */
34603          "deactivate" : true
34604     });
34605     this.hidden = false;
34606
34607     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34608 };
34609
34610 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34611            {
34612     purgeListeners : function(){
34613        Roo.util.Observable.prototype.purgeListeners.call(this);
34614        this.el.removeAllListeners();
34615     },
34616     /**
34617      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34618      */
34619     show : function(){
34620         this.pnode.addClass("active");
34621         this.showAction();
34622         if(Roo.isOpera){
34623             this.tabPanel.stripWrap.repaint();
34624         }
34625         this.fireEvent("activate", this.tabPanel, this);
34626     },
34627
34628     /**
34629      * Returns true if this tab is the active tab.
34630      * @return {Boolean}
34631      */
34632     isActive : function(){
34633         return this.tabPanel.getActiveTab() == this;
34634     },
34635
34636     /**
34637      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34638      */
34639     hide : function(){
34640         this.pnode.removeClass("active");
34641         this.hideAction();
34642         this.fireEvent("deactivate", this.tabPanel, this);
34643     },
34644
34645     hideAction : function(){
34646         this.bodyEl.hide();
34647         this.bodyEl.setStyle("position", "absolute");
34648         this.bodyEl.setLeft("-20000px");
34649         this.bodyEl.setTop("-20000px");
34650     },
34651
34652     showAction : function(){
34653         this.bodyEl.setStyle("position", "relative");
34654         this.bodyEl.setTop("");
34655         this.bodyEl.setLeft("");
34656         this.bodyEl.show();
34657     },
34658
34659     /**
34660      * Set the tooltip for the tab.
34661      * @param {String} tooltip The tab's tooltip
34662      */
34663     setTooltip : function(text){
34664         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34665             this.textEl.dom.qtip = text;
34666             this.textEl.dom.removeAttribute('title');
34667         }else{
34668             this.textEl.dom.title = text;
34669         }
34670     },
34671
34672     onTabClick : function(e){
34673         e.preventDefault();
34674         this.tabPanel.activate(this.id);
34675     },
34676
34677     onTabMouseDown : function(e){
34678         e.preventDefault();
34679         this.tabPanel.activate(this.id);
34680     },
34681 /*
34682     getWidth : function(){
34683         return this.inner.getWidth();
34684     },
34685
34686     setWidth : function(width){
34687         var iwidth = width - this.pnode.getPadding("lr");
34688         this.inner.setWidth(iwidth);
34689         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34690         this.pnode.setWidth(width);
34691     },
34692 */
34693     /**
34694      * Show or hide the tab
34695      * @param {Boolean} hidden True to hide or false to show.
34696      */
34697     setHidden : function(hidden){
34698         this.hidden = hidden;
34699         this.pnode.setStyle("display", hidden ? "none" : "");
34700     },
34701
34702     /**
34703      * Returns true if this tab is "hidden"
34704      * @return {Boolean}
34705      */
34706     isHidden : function(){
34707         return this.hidden;
34708     },
34709
34710     /**
34711      * Returns the text for this tab
34712      * @return {String}
34713      */
34714     getText : function(){
34715         return this.text;
34716     },
34717     /*
34718     autoSize : function(){
34719         //this.el.beginMeasure();
34720         this.textEl.setWidth(1);
34721         /*
34722          *  #2804 [new] Tabs in Roojs
34723          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34724          */
34725         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34726         //this.el.endMeasure();
34727     //},
34728
34729     /**
34730      * Sets the text for the tab (Note: this also sets the tooltip text)
34731      * @param {String} text The tab's text and tooltip
34732      */
34733     setText : function(text){
34734         this.text = text;
34735         this.textEl.update(text);
34736         this.setTooltip(text);
34737         //if(!this.tabPanel.resizeTabs){
34738         //    this.autoSize();
34739         //}
34740     },
34741     /**
34742      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34743      */
34744     activate : function(){
34745         this.tabPanel.activate(this.id);
34746     },
34747
34748     /**
34749      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34750      */
34751     disable : function(){
34752         if(this.tabPanel.active != this){
34753             this.disabled = true;
34754             this.pnode.addClass("disabled");
34755         }
34756     },
34757
34758     /**
34759      * Enables this TabPanelItem if it was previously disabled.
34760      */
34761     enable : function(){
34762         this.disabled = false;
34763         this.pnode.removeClass("disabled");
34764     },
34765
34766     /**
34767      * Sets the content for this TabPanelItem.
34768      * @param {String} content The content
34769      * @param {Boolean} loadScripts true to look for and load scripts
34770      */
34771     setContent : function(content, loadScripts){
34772         this.bodyEl.update(content, loadScripts);
34773     },
34774
34775     /**
34776      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
34777      * @return {Roo.UpdateManager} The UpdateManager
34778      */
34779     getUpdateManager : function(){
34780         return this.bodyEl.getUpdateManager();
34781     },
34782
34783     /**
34784      * Set a URL to be used to load the content for this TabPanelItem.
34785      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
34786      * @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)
34787      * @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)
34788      * @return {Roo.UpdateManager} The UpdateManager
34789      */
34790     setUrl : function(url, params, loadOnce){
34791         if(this.refreshDelegate){
34792             this.un('activate', this.refreshDelegate);
34793         }
34794         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34795         this.on("activate", this.refreshDelegate);
34796         return this.bodyEl.getUpdateManager();
34797     },
34798
34799     /** @private */
34800     _handleRefresh : function(url, params, loadOnce){
34801         if(!loadOnce || !this.loaded){
34802             var updater = this.bodyEl.getUpdateManager();
34803             updater.update(url, params, this._setLoaded.createDelegate(this));
34804         }
34805     },
34806
34807     /**
34808      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
34809      *   Will fail silently if the setUrl method has not been called.
34810      *   This does not activate the panel, just updates its content.
34811      */
34812     refresh : function(){
34813         if(this.refreshDelegate){
34814            this.loaded = false;
34815            this.refreshDelegate();
34816         }
34817     },
34818
34819     /** @private */
34820     _setLoaded : function(){
34821         this.loaded = true;
34822     },
34823
34824     /** @private */
34825     closeClick : function(e){
34826         var o = {};
34827         e.stopEvent();
34828         this.fireEvent("beforeclose", this, o);
34829         if(o.cancel !== true){
34830             this.tabPanel.removeTab(this.id);
34831         }
34832     },
34833     /**
34834      * The text displayed in the tooltip for the close icon.
34835      * @type String
34836      */
34837     closeText : "Close this tab"
34838 });