roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  * 
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  * 
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394     Roo.bootstrap.Body.superclass.constructor.call(this, config);
395     this.el = Roo.get(document.body);
396     if (this.cls && this.cls.length) {
397         Roo.get(document.body).addClass(this.cls);
398     }
399 };
400
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
402     
403     is_body : true,// just to make sure it's constructed?
404     
405         autoCreate : {
406         cls: 'container'
407     },
408     onRender : function(ct, position)
409     {
410        /* Roo.log("Roo.bootstrap.Body - onRender");
411         if (this.cls && this.cls.length) {
412             Roo.get(document.body).addClass(this.cls);
413         }
414         // style??? xttr???
415         */
416     }
417     
418     
419  
420    
421 });
422
423  /*
424  * - LGPL
425  *
426  * button group
427  * 
428  */
429
430
431 /**
432  * @class Roo.bootstrap.ButtonGroup
433  * @extends Roo.bootstrap.Component
434  * Bootstrap ButtonGroup class
435  * @cfg {String} size lg | sm | xs (default empty normal)
436  * @cfg {String} align vertical | justified  (default none)
437  * @cfg {String} direction up | down (default down)
438  * @cfg {Boolean} toolbar false | true
439  * @cfg {Boolean} btn true | false
440  * 
441  * 
442  * @constructor
443  * Create a new Input
444  * @param {Object} config The config object
445  */
446
447 Roo.bootstrap.ButtonGroup = function(config){
448     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
449 };
450
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
452     
453     size: '',
454     align: '',
455     direction: '',
456     toolbar: false,
457     btn: true,
458
459     getAutoCreate : function(){
460         var cfg = {
461             cls: 'btn-group',
462             html : null
463         };
464         
465         cfg.html = this.html || cfg.html;
466         
467         if (this.toolbar) {
468             cfg = {
469                 cls: 'btn-toolbar',
470                 html: null
471             };
472             
473             return cfg;
474         }
475         
476         if (['vertical','justified'].indexOf(this.align)!==-1) {
477             cfg.cls = 'btn-group-' + this.align;
478             
479             if (this.align == 'justified') {
480                 console.log(this.items);
481             }
482         }
483         
484         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485             cfg.cls += ' btn-group-' + this.size;
486         }
487         
488         if (this.direction == 'up') {
489             cfg.cls += ' dropup' ;
490         }
491         
492         return cfg;
493     }
494    
495 });
496
497  /*
498  * - LGPL
499  *
500  * button
501  * 
502  */
503
504 /**
505  * @class Roo.bootstrap.Button
506  * @extends Roo.bootstrap.Component
507  * Bootstrap Button class
508  * @cfg {String} html The button content
509  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
510  * @cfg {String} size ( lg | sm | xs)
511  * @cfg {String} tag ( a | input | submit)
512  * @cfg {String} href empty or href
513  * @cfg {Boolean} disabled default false;
514  * @cfg {Boolean} isClose default false;
515  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
516  * @cfg {String} badge text for badge
517  * @cfg {String} theme default 
518  * @cfg {Boolean} inverse 
519  * @cfg {Boolean} toggle 
520  * @cfg {String} ontext text for on toggle state
521  * @cfg {String} offtext text for off toggle state
522  * @cfg {Boolean} defaulton 
523  * @cfg {Boolean} preventDefault  default true
524  * @cfg {Boolean} removeClass remove the standard class..
525  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
526  * 
527  * @constructor
528  * Create a new button
529  * @param {Object} config The config object
530  */
531
532
533 Roo.bootstrap.Button = function(config){
534     Roo.bootstrap.Button.superclass.constructor.call(this, config);
535     this.addEvents({
536         // raw events
537         /**
538          * @event click
539          * When a butotn is pressed
540          * @param {Roo.bootstrap.Button} this
541          * @param {Roo.EventObject} e
542          */
543         "click" : true,
544          /**
545          * @event toggle
546          * After the button has been toggles
547          * @param {Roo.EventObject} e
548          * @param {boolean} pressed (also available as button.pressed)
549          */
550         "toggle" : true
551     });
552 };
553
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
555     html: false,
556     active: false,
557     weight: '',
558     size: '',
559     tag: 'button',
560     href: '',
561     disabled: false,
562     isClose: false,
563     glyphicon: '',
564     badge: '',
565     theme: 'default',
566     inverse: false,
567     
568     toggle: false,
569     ontext: 'ON',
570     offtext: 'OFF',
571     defaulton: true,
572     preventDefault: true,
573     removeClass: false,
574     name: false,
575     target: false,
576     
577     
578     pressed : null,
579      
580     
581     getAutoCreate : function(){
582         
583         var cfg = {
584             tag : 'button',
585             cls : 'roo-button',
586             html: ''
587         };
588         
589         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
591             this.tag = 'button';
592         } else {
593             cfg.tag = this.tag;
594         }
595         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
596         
597         if (this.toggle == true) {
598             cfg={
599                 tag: 'div',
600                 cls: 'slider-frame roo-button',
601                 cn: [
602                     {
603                         tag: 'span',
604                         'data-on-text':'ON',
605                         'data-off-text':'OFF',
606                         cls: 'slider-button',
607                         html: this.offtext
608                     }
609                 ]
610             };
611             
612             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613                 cfg.cls += ' '+this.weight;
614             }
615             
616             return cfg;
617         }
618         
619         if (this.isClose) {
620             cfg.cls += ' close';
621             
622             cfg["aria-hidden"] = true;
623             
624             cfg.html = "&times;";
625             
626             return cfg;
627         }
628         
629          
630         if (this.theme==='default') {
631             cfg.cls = 'btn roo-button';
632             
633             //if (this.parentType != 'Navbar') {
634             this.weight = this.weight.length ?  this.weight : 'default';
635             //}
636             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637                 
638                 cfg.cls += ' btn-' + this.weight;
639             }
640         } else if (this.theme==='glow') {
641             
642             cfg.tag = 'a';
643             cfg.cls = 'btn-glow roo-button';
644             
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' ' + this.weight;
648             }
649         }
650    
651         
652         if (this.inverse) {
653             this.cls += ' inverse';
654         }
655         
656         
657         if (this.active) {
658             cfg.cls += ' active';
659         }
660         
661         if (this.disabled) {
662             cfg.disabled = 'disabled';
663         }
664         
665         if (this.items) {
666             Roo.log('changing to ul' );
667             cfg.tag = 'ul';
668             this.glyphicon = 'caret';
669         }
670         
671         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
672          
673         //gsRoo.log(this.parentType);
674         if (this.parentType === 'Navbar' && !this.parent().bar) {
675             Roo.log('changing to li?');
676             
677             cfg.tag = 'li';
678             
679             cfg.cls = '';
680             cfg.cn =  [{
681                 tag : 'a',
682                 cls : 'roo-button',
683                 html : this.html,
684                 href : this.href || '#'
685             }];
686             if (this.menu) {
687                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
688                 cfg.cls += ' dropdown';
689             }   
690             
691             delete cfg.html;
692             
693         }
694         
695        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
696         
697         if (this.glyphicon) {
698             cfg.html = ' ' + cfg.html;
699             
700             cfg.cn = [
701                 {
702                     tag: 'span',
703                     cls: 'glyphicon glyphicon-' + this.glyphicon
704                 }
705             ];
706         }
707         
708         if (this.badge) {
709             cfg.html += ' ';
710             
711             cfg.tag = 'a';
712             
713 //            cfg.cls='btn roo-button';
714             
715             cfg.href=this.href;
716             
717             var value = cfg.html;
718             
719             if(this.glyphicon){
720                 value = {
721                             tag: 'span',
722                             cls: 'glyphicon glyphicon-' + this.glyphicon,
723                             html: this.html
724                         };
725                 
726             }
727             
728             cfg.cn = [
729                 value,
730                 {
731                     tag: 'span',
732                     cls: 'badge',
733                     html: this.badge
734                 }
735             ];
736             
737             cfg.html='';
738         }
739         
740         if (this.menu) {
741             cfg.cls += ' dropdown';
742             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
743         }
744         
745         if (cfg.tag !== 'a' && this.href !== '') {
746             throw "Tag must be a to set href.";
747         } else if (this.href.length > 0) {
748             cfg.href = this.href;
749         }
750         
751         if(this.removeClass){
752             cfg.cls = '';
753         }
754         
755         if(this.target){
756             cfg.target = this.target;
757         }
758         
759         return cfg;
760     },
761     initEvents: function() {
762        // Roo.log('init events?');
763 //        Roo.log(this.el.dom);
764         // add the menu...
765         
766         if (typeof (this.menu) != 'undefined') {
767             this.menu.parentType = this.xtype;
768             this.menu.triggerEl = this.el;
769             this.addxtype(Roo.apply({}, this.menu));
770         }
771
772
773        if (this.el.hasClass('roo-button')) {
774             this.el.on('click', this.onClick, this);
775        } else {
776             this.el.select('.roo-button').on('click', this.onClick, this);
777        }
778        
779        if(this.removeClass){
780            this.el.on('click', this.onClick, this);
781        }
782        
783        this.el.enableDisplayMode();
784         
785     },
786     onClick : function(e)
787     {
788         if (this.disabled) {
789             return;
790         }
791         
792         
793         Roo.log('button on click ');
794         if(this.preventDefault){
795             e.preventDefault();
796         }
797         if (this.pressed === true || this.pressed === false) {
798             this.pressed = !this.pressed;
799             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800             this.fireEvent('toggle', this, e, this.pressed);
801         }
802         
803         
804         this.fireEvent('click', this, e);
805     },
806     
807     /**
808      * Enables this button
809      */
810     enable : function()
811     {
812         this.disabled = false;
813         this.el.removeClass('disabled');
814     },
815     
816     /**
817      * Disable this button
818      */
819     disable : function()
820     {
821         this.disabled = true;
822         this.el.addClass('disabled');
823     },
824      /**
825      * sets the active state on/off, 
826      * @param {Boolean} state (optional) Force a particular state
827      */
828     setActive : function(v) {
829         
830         this.el[v ? 'addClass' : 'removeClass']('active');
831     },
832      /**
833      * toggles the current active state 
834      */
835     toggleActive : function()
836     {
837        var active = this.el.hasClass('active');
838        this.setActive(!active);
839        
840         
841     },
842     setText : function(str)
843     {
844         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
845     },
846     getText : function()
847     {
848         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
849     },
850     hide: function() {
851        
852      
853         this.el.hide();   
854     },
855     show: function() {
856        
857         this.el.show();   
858     }
859     
860     
861 });
862
863  /*
864  * - LGPL
865  *
866  * column
867  * 
868  */
869
870 /**
871  * @class Roo.bootstrap.Column
872  * @extends Roo.bootstrap.Component
873  * Bootstrap Column class
874  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
882  *
883  * 
884  * @cfg {Boolean} hidden (true|false) hide the element
885  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886  * @cfg {String} fa (ban|check|...) font awesome icon
887  * @cfg {Number} fasize (1|2|....) font awsome size
888
889  * @cfg {String} icon (info-sign|check|...) glyphicon name
890
891  * @cfg {String} html content of column.
892  * 
893  * @constructor
894  * Create a new Column
895  * @param {Object} config The config object
896  */
897
898 Roo.bootstrap.Column = function(config){
899     Roo.bootstrap.Column.superclass.constructor.call(this, config);
900 };
901
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
903     
904     xs: false,
905     sm: false,
906     md: false,
907     lg: false,
908     xsoff: false,
909     smoff: false,
910     mdoff: false,
911     lgoff: false,
912     html: '',
913     offset: 0,
914     alert: false,
915     fa: false,
916     icon : false,
917     hidden : false,
918     fasize : 1,
919     
920     getAutoCreate : function(){
921         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
922         
923         cfg = {
924             tag: 'div',
925             cls: 'column'
926         };
927         
928         var settings=this;
929         ['xs','sm','md','lg'].map(function(size){
930             //Roo.log( size + ':' + settings[size]);
931             
932             if (settings[size+'off'] !== false) {
933                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
934             }
935             
936             if (settings[size] === false) {
937                 return;
938             }
939             
940             if (!settings[size]) { // 0 = hidden
941                 cfg.cls += ' hidden-' + size;
942                 return;
943             }
944             cfg.cls += ' col-' + size + '-' + settings[size];
945             
946         });
947         
948         if (this.hidden) {
949             cfg.cls += ' hidden';
950         }
951         
952         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953             cfg.cls +=' alert alert-' + this.alert;
954         }
955         
956         
957         if (this.html.length) {
958             cfg.html = this.html;
959         }
960         if (this.fa) {
961             var fasize = '';
962             if (this.fasize > 1) {
963                 fasize = ' fa-' + this.fasize + 'x';
964             }
965             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
966             
967             
968         }
969         if (this.icon) {
970             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
971         }
972         
973         return cfg;
974     }
975    
976 });
977
978  
979
980  /*
981  * - LGPL
982  *
983  * page container.
984  * 
985  */
986
987
988 /**
989  * @class Roo.bootstrap.Container
990  * @extends Roo.bootstrap.Component
991  * Bootstrap Container class
992  * @cfg {Boolean} jumbotron is it a jumbotron element
993  * @cfg {String} html content of element
994  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
996  * @cfg {String} header content of header (for panel)
997  * @cfg {String} footer content of footer (for panel)
998  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999  * @cfg {String} tag (header|aside|section) type of HTML tag.
1000  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001  * @cfg {String} fa font awesome icon
1002  * @cfg {String} icon (info-sign|check|...) glyphicon name
1003  * @cfg {Boolean} hidden (true|false) hide the element
1004  * @cfg {Boolean} expandable (true|false) default false
1005  * @cfg {Boolean} expanded (true|false) default true
1006  * @cfg {String} rheader contet on the right of header
1007  * @cfg {Boolean} clickable (true|false) default false
1008
1009  *     
1010  * @constructor
1011  * Create a new Container
1012  * @param {Object} config The config object
1013  */
1014
1015 Roo.bootstrap.Container = function(config){
1016     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020          /**
1021          * @event expand
1022          * After the panel has been expand
1023          * 
1024          * @param {Roo.bootstrap.Container} this
1025          */
1026         "expand" : true,
1027         /**
1028          * @event collapse
1029          * After the panel has been collapsed
1030          * 
1031          * @param {Roo.bootstrap.Container} this
1032          */
1033         "collapse" : true,
1034         /**
1035          * @event click
1036          * When a element is chick
1037          * @param {Roo.bootstrap.Container} this
1038          * @param {Roo.EventObject} e
1039          */
1040         "click" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1045     
1046     jumbotron : false,
1047     well: '',
1048     panel : '',
1049     header: '',
1050     footer : '',
1051     sticky: '',
1052     tag : false,
1053     alert : false,
1054     fa: false,
1055     icon : false,
1056     expandable : false,
1057     rheader : '',
1058     expanded : true,
1059     clickable: false,
1060   
1061      
1062     getChildContainer : function() {
1063         
1064         if(!this.el){
1065             return false;
1066         }
1067         
1068         if (this.panel.length) {
1069             return this.el.select('.panel-body',true).first();
1070         }
1071         
1072         return this.el;
1073     },
1074     
1075     
1076     getAutoCreate : function(){
1077         
1078         var cfg = {
1079             tag : this.tag || 'div',
1080             html : '',
1081             cls : ''
1082         };
1083         if (this.jumbotron) {
1084             cfg.cls = 'jumbotron';
1085         }
1086         
1087         
1088         
1089         // - this is applied by the parent..
1090         //if (this.cls) {
1091         //    cfg.cls = this.cls + '';
1092         //}
1093         
1094         if (this.sticky.length) {
1095             
1096             var bd = Roo.get(document.body);
1097             if (!bd.hasClass('bootstrap-sticky')) {
1098                 bd.addClass('bootstrap-sticky');
1099                 Roo.select('html',true).setStyle('height', '100%');
1100             }
1101              
1102             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1103         }
1104         
1105         
1106         if (this.well.length) {
1107             switch (this.well) {
1108                 case 'lg':
1109                 case 'sm':
1110                     cfg.cls +=' well well-' +this.well;
1111                     break;
1112                 default:
1113                     cfg.cls +=' well';
1114                     break;
1115             }
1116         }
1117         
1118         if (this.hidden) {
1119             cfg.cls += ' hidden';
1120         }
1121         
1122         
1123         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124             cfg.cls +=' alert alert-' + this.alert;
1125         }
1126         
1127         var body = cfg;
1128         
1129         if (this.panel.length) {
1130             cfg.cls += ' panel panel-' + this.panel;
1131             cfg.cn = [];
1132             if (this.header.length) {
1133                 
1134                 var h = [];
1135                 
1136                 if(this.expandable){
1137                     
1138                     cfg.cls = cfg.cls + ' expandable';
1139                     
1140                     h.push({
1141                         tag: 'i',
1142                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1143                     });
1144                     
1145                 }
1146                 
1147                 h.push(
1148                     {
1149                         tag: 'span',
1150                         cls : 'panel-title',
1151                         html : (this.expandable ? '&nbsp;' : '') + this.header
1152                     },
1153                     {
1154                         tag: 'span',
1155                         cls: 'panel-header-right',
1156                         html: this.rheader
1157                     }
1158                 );
1159                 
1160                 cfg.cn.push({
1161                     cls : 'panel-heading',
1162                     style : this.expandable ? 'cursor: pointer' : '',
1163                     cn : h
1164                 });
1165                 
1166             }
1167             
1168             body = false;
1169             cfg.cn.push({
1170                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1171                 html : this.html
1172             });
1173             
1174             
1175             if (this.footer.length) {
1176                 cfg.cn.push({
1177                     cls : 'panel-footer',
1178                     html : this.footer
1179                     
1180                 });
1181             }
1182             
1183         }
1184         
1185         if (body) {
1186             body.html = this.html || cfg.html;
1187             // prefix with the icons..
1188             if (this.fa) {
1189                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1190             }
1191             if (this.icon) {
1192                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1193             }
1194             
1195             
1196         }
1197         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198             cfg.cls =  'container';
1199         }
1200         
1201         return cfg;
1202     },
1203     
1204     initEvents: function() 
1205     {
1206         if(this.expandable){
1207             var headerEl = this.headerEl();
1208         
1209             if(headerEl){
1210                 headerEl.on('click', this.onToggleClick, this);
1211             }
1212         }
1213         
1214         if(this.clickable){
1215             this.el.on('click', this.onClick, this);
1216         }
1217         
1218     },
1219     
1220     onToggleClick : function()
1221     {
1222         var headerEl = this.headerEl();
1223         
1224         if(!headerEl){
1225             return;
1226         }
1227         
1228         if(this.expanded){
1229             this.collapse();
1230             return;
1231         }
1232         
1233         this.expand();
1234     },
1235     
1236     expand : function()
1237     {
1238         if(this.fireEvent('expand', this)) {
1239             
1240             this.expanded = true;
1241             
1242             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1243             
1244             this.el.select('.panel-body',true).first().removeClass('hide');
1245             
1246             var toggleEl = this.toggleEl();
1247
1248             if(!toggleEl){
1249                 return;
1250             }
1251
1252             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1253         }
1254         
1255     },
1256     
1257     collapse : function()
1258     {
1259         if(this.fireEvent('collapse', this)) {
1260             
1261             this.expanded = false;
1262             
1263             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264             this.el.select('.panel-body',true).first().addClass('hide');
1265         
1266             var toggleEl = this.toggleEl();
1267
1268             if(!toggleEl){
1269                 return;
1270             }
1271
1272             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1273         }
1274     },
1275     
1276     toggleEl : function()
1277     {
1278         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1279             return;
1280         }
1281         
1282         return this.el.select('.panel-heading .fa',true).first();
1283     },
1284     
1285     headerEl : function()
1286     {
1287         if(!this.el || !this.panel.length || !this.header.length){
1288             return;
1289         }
1290         
1291         return this.el.select('.panel-heading',true).first()
1292     },
1293     
1294     titleEl : function()
1295     {
1296         if(!this.el || !this.panel.length || !this.header.length){
1297             return;
1298         }
1299         
1300         return this.el.select('.panel-title',true).first();
1301     },
1302     
1303     setTitle : function(v)
1304     {
1305         var titleEl = this.titleEl();
1306         
1307         if(!titleEl){
1308             return;
1309         }
1310         
1311         titleEl.dom.innerHTML = v;
1312     },
1313     
1314     getTitle : function()
1315     {
1316         
1317         var titleEl = this.titleEl();
1318         
1319         if(!titleEl){
1320             return '';
1321         }
1322         
1323         return titleEl.dom.innerHTML;
1324     },
1325     
1326     setRightTitle : function(v)
1327     {
1328         var t = this.el.select('.panel-header-right',true).first();
1329         
1330         if(!t){
1331             return;
1332         }
1333         
1334         t.dom.innerHTML = v;
1335     },
1336     
1337     onClick : function(e)
1338     {
1339         e.preventDefault();
1340         
1341         this.fireEvent('click', this, e);
1342     }
1343    
1344 });
1345
1346  /*
1347  * - LGPL
1348  *
1349  * image
1350  * 
1351  */
1352
1353
1354 /**
1355  * @class Roo.bootstrap.Img
1356  * @extends Roo.bootstrap.Component
1357  * Bootstrap Img class
1358  * @cfg {Boolean} imgResponsive false | true
1359  * @cfg {String} border rounded | circle | thumbnail
1360  * @cfg {String} src image source
1361  * @cfg {String} alt image alternative text
1362  * @cfg {String} href a tag href
1363  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364  * @cfg {String} xsUrl xs image source
1365  * @cfg {String} smUrl sm image source
1366  * @cfg {String} mdUrl md image source
1367  * @cfg {String} lgUrl lg image source
1368  * 
1369  * @constructor
1370  * Create a new Input
1371  * @param {Object} config The config object
1372  */
1373
1374 Roo.bootstrap.Img = function(config){
1375     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1376     
1377     this.addEvents({
1378         // img events
1379         /**
1380          * @event click
1381          * The img click event for the img.
1382          * @param {Roo.EventObject} e
1383          */
1384         "click" : true
1385     });
1386 };
1387
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1389     
1390     imgResponsive: true,
1391     border: '',
1392     src: 'about:blank',
1393     href: false,
1394     target: false,
1395     xsUrl: '',
1396     smUrl: '',
1397     mdUrl: '',
1398     lgUrl: '',
1399
1400     getAutoCreate : function()
1401     {   
1402         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403             return this.createSingleImg();
1404         }
1405         
1406         var cfg = {
1407             tag: 'div',
1408             cls: 'roo-image-responsive-group',
1409             cn: []
1410         };
1411         var _this = this;
1412         
1413         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1414             
1415             if(!_this[size + 'Url']){
1416                 return;
1417             }
1418             
1419             var img = {
1420                 tag: 'img',
1421                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422                 html: _this.html || cfg.html,
1423                 src: _this[size + 'Url']
1424             };
1425             
1426             img.cls += ' roo-image-responsive-' + size;
1427             
1428             var s = ['xs', 'sm', 'md', 'lg'];
1429             
1430             s.splice(s.indexOf(size), 1);
1431             
1432             Roo.each(s, function(ss){
1433                 img.cls += ' hidden-' + ss;
1434             });
1435             
1436             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437                 cfg.cls += ' img-' + _this.border;
1438             }
1439             
1440             if(_this.alt){
1441                 cfg.alt = _this.alt;
1442             }
1443             
1444             if(_this.href){
1445                 var a = {
1446                     tag: 'a',
1447                     href: _this.href,
1448                     cn: [
1449                         img
1450                     ]
1451                 };
1452
1453                 if(this.target){
1454                     a.target = _this.target;
1455                 }
1456             }
1457             
1458             cfg.cn.push((_this.href) ? a : img);
1459             
1460         });
1461         
1462         return cfg;
1463     },
1464     
1465     createSingleImg : function()
1466     {
1467         var cfg = {
1468             tag: 'img',
1469             cls: (this.imgResponsive) ? 'img-responsive' : '',
1470             html : null,
1471             src : 'about:blank'  // just incase src get's set to undefined?!?
1472         };
1473         
1474         cfg.html = this.html || cfg.html;
1475         
1476         cfg.src = this.src || cfg.src;
1477         
1478         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479             cfg.cls += ' img-' + this.border;
1480         }
1481         
1482         if(this.alt){
1483             cfg.alt = this.alt;
1484         }
1485         
1486         if(this.href){
1487             var a = {
1488                 tag: 'a',
1489                 href: this.href,
1490                 cn: [
1491                     cfg
1492                 ]
1493             };
1494             
1495             if(this.target){
1496                 a.target = this.target;
1497             }
1498             
1499         }
1500         
1501         return (this.href) ? a : cfg;
1502     },
1503     
1504     initEvents: function() 
1505     {
1506         if(!this.href){
1507             this.el.on('click', this.onClick, this);
1508         }
1509         
1510     },
1511     
1512     onClick : function(e)
1513     {
1514         Roo.log('img onclick');
1515         this.fireEvent('click', this, e);
1516     }
1517    
1518 });
1519
1520  /*
1521  * - LGPL
1522  *
1523  * image
1524  * 
1525  */
1526
1527
1528 /**
1529  * @class Roo.bootstrap.Link
1530  * @extends Roo.bootstrap.Component
1531  * Bootstrap Link Class
1532  * @cfg {String} alt image alternative text
1533  * @cfg {String} href a tag href
1534  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535  * @cfg {String} html the content of the link.
1536  * @cfg {String} anchor name for the anchor link
1537  * @cfg {String} fa - favicon
1538
1539  * @cfg {Boolean} preventDefault (true | false) default false
1540
1541  * 
1542  * @constructor
1543  * Create a new Input
1544  * @param {Object} config The config object
1545  */
1546
1547 Roo.bootstrap.Link = function(config){
1548     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1549     
1550     this.addEvents({
1551         // img events
1552         /**
1553          * @event click
1554          * The img click event for the img.
1555          * @param {Roo.EventObject} e
1556          */
1557         "click" : true
1558     });
1559 };
1560
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1562     
1563     href: false,
1564     target: false,
1565     preventDefault: false,
1566     anchor : false,
1567     alt : false,
1568     fa: false,
1569
1570
1571     getAutoCreate : function()
1572     {
1573         var html = this.html || '';
1574         
1575         if (this.fa !== false) {
1576             html = '<i class="fa fa-' + this.fa + '"></i>';
1577         }
1578         var cfg = {
1579             tag: 'a'
1580         };
1581         // anchor's do not require html/href...
1582         if (this.anchor === false) {
1583             cfg.html = html;
1584             cfg.href = this.href || '#';
1585         } else {
1586             cfg.name = this.anchor;
1587             if (this.html !== false || this.fa !== false) {
1588                 cfg.html = html;
1589             }
1590             if (this.href !== false) {
1591                 cfg.href = this.href;
1592             }
1593         }
1594         
1595         if(this.alt !== false){
1596             cfg.alt = this.alt;
1597         }
1598         
1599         
1600         if(this.target !== false) {
1601             cfg.target = this.target;
1602         }
1603         
1604         return cfg;
1605     },
1606     
1607     initEvents: function() {
1608         
1609         if(!this.href || this.preventDefault){
1610             this.el.on('click', this.onClick, this);
1611         }
1612     },
1613     
1614     onClick : function(e)
1615     {
1616         if(this.preventDefault){
1617             e.preventDefault();
1618         }
1619         //Roo.log('img onclick');
1620         this.fireEvent('click', this, e);
1621     }
1622    
1623 });
1624
1625  /*
1626  * - LGPL
1627  *
1628  * header
1629  * 
1630  */
1631
1632 /**
1633  * @class Roo.bootstrap.Header
1634  * @extends Roo.bootstrap.Component
1635  * Bootstrap Header class
1636  * @cfg {String} html content of header
1637  * @cfg {Number} level (1|2|3|4|5|6) default 1
1638  * 
1639  * @constructor
1640  * Create a new Header
1641  * @param {Object} config The config object
1642  */
1643
1644
1645 Roo.bootstrap.Header  = function(config){
1646     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1650     
1651     //href : false,
1652     html : false,
1653     level : 1,
1654     
1655     
1656     
1657     getAutoCreate : function(){
1658         
1659         
1660         
1661         var cfg = {
1662             tag: 'h' + (1 *this.level),
1663             html: this.html || ''
1664         } ;
1665         
1666         return cfg;
1667     }
1668    
1669 });
1670
1671  
1672
1673  /*
1674  * Based on:
1675  * Ext JS Library 1.1.1
1676  * Copyright(c) 2006-2007, Ext JS, LLC.
1677  *
1678  * Originally Released Under LGPL - original licence link has changed is not relivant.
1679  *
1680  * Fork - LGPL
1681  * <script type="text/javascript">
1682  */
1683  
1684 /**
1685  * @class Roo.bootstrap.MenuMgr
1686  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1687  * @singleton
1688  */
1689 Roo.bootstrap.MenuMgr = function(){
1690    var menus, active, groups = {}, attached = false, lastShow = new Date();
1691
1692    // private - called when first menu is created
1693    function init(){
1694        menus = {};
1695        active = new Roo.util.MixedCollection();
1696        Roo.get(document).addKeyListener(27, function(){
1697            if(active.length > 0){
1698                hideAll();
1699            }
1700        });
1701    }
1702
1703    // private
1704    function hideAll(){
1705        if(active && active.length > 0){
1706            var c = active.clone();
1707            c.each(function(m){
1708                m.hide();
1709            });
1710        }
1711    }
1712
1713    // private
1714    function onHide(m){
1715        active.remove(m);
1716        if(active.length < 1){
1717            Roo.get(document).un("mouseup", onMouseDown);
1718             
1719            attached = false;
1720        }
1721    }
1722
1723    // private
1724    function onShow(m){
1725        var last = active.last();
1726        lastShow = new Date();
1727        active.add(m);
1728        if(!attached){
1729           Roo.get(document).on("mouseup", onMouseDown);
1730            
1731            attached = true;
1732        }
1733        if(m.parentMenu){
1734           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735           m.parentMenu.activeChild = m;
1736        }else if(last && last.isVisible()){
1737           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1738        }
1739    }
1740
1741    // private
1742    function onBeforeHide(m){
1743        if(m.activeChild){
1744            m.activeChild.hide();
1745        }
1746        if(m.autoHideTimer){
1747            clearTimeout(m.autoHideTimer);
1748            delete m.autoHideTimer;
1749        }
1750    }
1751
1752    // private
1753    function onBeforeShow(m){
1754        var pm = m.parentMenu;
1755        if(!pm && !m.allowOtherMenus){
1756            hideAll();
1757        }else if(pm && pm.activeChild && active != m){
1758            pm.activeChild.hide();
1759        }
1760    }
1761
1762    // private this should really trigger on mouseup..
1763    function onMouseDown(e){
1764         Roo.log("on Mouse Up");
1765         
1766         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767             Roo.log("MenuManager hideAll");
1768             hideAll();
1769             e.stopEvent();
1770         }
1771         
1772         
1773    }
1774
1775    // private
1776    function onBeforeCheck(mi, state){
1777        if(state){
1778            var g = groups[mi.group];
1779            for(var i = 0, l = g.length; i < l; i++){
1780                if(g[i] != mi){
1781                    g[i].setChecked(false);
1782                }
1783            }
1784        }
1785    }
1786
1787    return {
1788
1789        /**
1790         * Hides all menus that are currently visible
1791         */
1792        hideAll : function(){
1793             hideAll();  
1794        },
1795
1796        // private
1797        register : function(menu){
1798            if(!menus){
1799                init();
1800            }
1801            menus[menu.id] = menu;
1802            menu.on("beforehide", onBeforeHide);
1803            menu.on("hide", onHide);
1804            menu.on("beforeshow", onBeforeShow);
1805            menu.on("show", onShow);
1806            var g = menu.group;
1807            if(g && menu.events["checkchange"]){
1808                if(!groups[g]){
1809                    groups[g] = [];
1810                }
1811                groups[g].push(menu);
1812                menu.on("checkchange", onCheck);
1813            }
1814        },
1815
1816         /**
1817          * Returns a {@link Roo.menu.Menu} object
1818          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819          * be used to generate and return a new Menu instance.
1820          */
1821        get : function(menu){
1822            if(typeof menu == "string"){ // menu id
1823                return menus[menu];
1824            }else if(menu.events){  // menu instance
1825                return menu;
1826            }
1827            /*else if(typeof menu.length == 'number'){ // array of menu items?
1828                return new Roo.bootstrap.Menu({items:menu});
1829            }else{ // otherwise, must be a config
1830                return new Roo.bootstrap.Menu(menu);
1831            }
1832            */
1833            return false;
1834        },
1835
1836        // private
1837        unregister : function(menu){
1838            delete menus[menu.id];
1839            menu.un("beforehide", onBeforeHide);
1840            menu.un("hide", onHide);
1841            menu.un("beforeshow", onBeforeShow);
1842            menu.un("show", onShow);
1843            var g = menu.group;
1844            if(g && menu.events["checkchange"]){
1845                groups[g].remove(menu);
1846                menu.un("checkchange", onCheck);
1847            }
1848        },
1849
1850        // private
1851        registerCheckable : function(menuItem){
1852            var g = menuItem.group;
1853            if(g){
1854                if(!groups[g]){
1855                    groups[g] = [];
1856                }
1857                groups[g].push(menuItem);
1858                menuItem.on("beforecheckchange", onBeforeCheck);
1859            }
1860        },
1861
1862        // private
1863        unregisterCheckable : function(menuItem){
1864            var g = menuItem.group;
1865            if(g){
1866                groups[g].remove(menuItem);
1867                menuItem.un("beforecheckchange", onBeforeCheck);
1868            }
1869        }
1870    };
1871 }();/*
1872  * - LGPL
1873  *
1874  * menu
1875  * 
1876  */
1877
1878 /**
1879  * @class Roo.bootstrap.Menu
1880  * @extends Roo.bootstrap.Component
1881  * Bootstrap Menu class - container for MenuItems
1882  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1884  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1885  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1886  * 
1887  * @constructor
1888  * Create a new Menu
1889  * @param {Object} config The config object
1890  */
1891
1892
1893 Roo.bootstrap.Menu = function(config){
1894     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895     if (this.registerMenu && this.type != 'treeview')  {
1896         Roo.bootstrap.MenuMgr.register(this);
1897     }
1898     this.addEvents({
1899         /**
1900          * @event beforeshow
1901          * Fires before this menu is displayed
1902          * @param {Roo.menu.Menu} this
1903          */
1904         beforeshow : true,
1905         /**
1906          * @event beforehide
1907          * Fires before this menu is hidden
1908          * @param {Roo.menu.Menu} this
1909          */
1910         beforehide : true,
1911         /**
1912          * @event show
1913          * Fires after this menu is displayed
1914          * @param {Roo.menu.Menu} this
1915          */
1916         show : true,
1917         /**
1918          * @event hide
1919          * Fires after this menu is hidden
1920          * @param {Roo.menu.Menu} this
1921          */
1922         hide : true,
1923         /**
1924          * @event click
1925          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926          * @param {Roo.menu.Menu} this
1927          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928          * @param {Roo.EventObject} e
1929          */
1930         click : true,
1931         /**
1932          * @event mouseover
1933          * Fires when the mouse is hovering over this menu
1934          * @param {Roo.menu.Menu} this
1935          * @param {Roo.EventObject} e
1936          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1937          */
1938         mouseover : true,
1939         /**
1940          * @event mouseout
1941          * Fires when the mouse exits this menu
1942          * @param {Roo.menu.Menu} this
1943          * @param {Roo.EventObject} e
1944          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1945          */
1946         mouseout : true,
1947         /**
1948          * @event itemclick
1949          * Fires when a menu item contained in this menu is clicked
1950          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951          * @param {Roo.EventObject} e
1952          */
1953         itemclick: true
1954     });
1955     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1956 };
1957
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1959     
1960    /// html : false,
1961     //align : '',
1962     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1963     type: false,
1964     /**
1965      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1966      */
1967     registerMenu : true,
1968     
1969     menuItems :false, // stores the menu items..
1970     
1971     hidden:true,
1972         
1973     parentMenu : false,
1974     
1975     stopEvent : true,
1976     
1977     isLink : false,
1978     
1979     getChildContainer : function() {
1980         return this.el;  
1981     },
1982     
1983     getAutoCreate : function(){
1984          
1985         //if (['right'].indexOf(this.align)!==-1) {
1986         //    cfg.cn[1].cls += ' pull-right'
1987         //}
1988         
1989         
1990         var cfg = {
1991             tag : 'ul',
1992             cls : 'dropdown-menu' ,
1993             style : 'z-index:1000'
1994             
1995         };
1996         
1997         if (this.type === 'submenu') {
1998             cfg.cls = 'submenu active';
1999         }
2000         if (this.type === 'treeview') {
2001             cfg.cls = 'treeview-menu';
2002         }
2003         
2004         return cfg;
2005     },
2006     initEvents : function() {
2007         
2008        // Roo.log("ADD event");
2009        // Roo.log(this.triggerEl.dom);
2010         
2011         this.triggerEl.on('click', this.onTriggerClick, this);
2012         
2013         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2014         
2015         this.triggerEl.addClass('dropdown-toggle');
2016         
2017         if (Roo.isTouch) {
2018             this.el.on('touchstart'  , this.onTouch, this);
2019         }
2020         this.el.on('click' , this.onClick, this);
2021
2022         this.el.on("mouseover", this.onMouseOver, this);
2023         this.el.on("mouseout", this.onMouseOut, this);
2024         
2025     },
2026     
2027     findTargetItem : function(e)
2028     {
2029         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2030         if(!t){
2031             return false;
2032         }
2033         //Roo.log(t);         Roo.log(t.id);
2034         if(t && t.id){
2035             //Roo.log(this.menuitems);
2036             return this.menuitems.get(t.id);
2037             
2038             //return this.items.get(t.menuItemId);
2039         }
2040         
2041         return false;
2042     },
2043     
2044     onTouch : function(e) 
2045     {
2046         Roo.log("menu.onTouch");
2047         //e.stopEvent(); this make the user popdown broken
2048         this.onClick(e);
2049     },
2050     
2051     onClick : function(e)
2052     {
2053         Roo.log("menu.onClick");
2054         
2055         var t = this.findTargetItem(e);
2056         if(!t || t.isContainer){
2057             return;
2058         }
2059         Roo.log(e);
2060         /*
2061         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2062             if(t == this.activeItem && t.shouldDeactivate(e)){
2063                 this.activeItem.deactivate();
2064                 delete this.activeItem;
2065                 return;
2066             }
2067             if(t.canActivate){
2068                 this.setActiveItem(t, true);
2069             }
2070             return;
2071             
2072             
2073         }
2074         */
2075        
2076         Roo.log('pass click event');
2077         
2078         t.onClick(e);
2079         
2080         this.fireEvent("click", this, t, e);
2081         
2082         var _this = this;
2083         
2084         (function() { _this.hide(); }).defer(500);
2085     },
2086     
2087     onMouseOver : function(e){
2088         var t  = this.findTargetItem(e);
2089         //Roo.log(t);
2090         //if(t){
2091         //    if(t.canActivate && !t.disabled){
2092         //        this.setActiveItem(t, true);
2093         //    }
2094         //}
2095         
2096         this.fireEvent("mouseover", this, e, t);
2097     },
2098     isVisible : function(){
2099         return !this.hidden;
2100     },
2101      onMouseOut : function(e){
2102         var t  = this.findTargetItem(e);
2103         
2104         //if(t ){
2105         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2106         //        this.activeItem.deactivate();
2107         //        delete this.activeItem;
2108         //    }
2109         //}
2110         this.fireEvent("mouseout", this, e, t);
2111     },
2112     
2113     
2114     /**
2115      * Displays this menu relative to another element
2116      * @param {String/HTMLElement/Roo.Element} element The element to align to
2117      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118      * the element (defaults to this.defaultAlign)
2119      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2120      */
2121     show : function(el, pos, parentMenu){
2122         this.parentMenu = parentMenu;
2123         if(!this.el){
2124             this.render();
2125         }
2126         this.fireEvent("beforeshow", this);
2127         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2128     },
2129      /**
2130      * Displays this menu at a specific xy position
2131      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2133      */
2134     showAt : function(xy, parentMenu, /* private: */_e){
2135         this.parentMenu = parentMenu;
2136         if(!this.el){
2137             this.render();
2138         }
2139         if(_e !== false){
2140             this.fireEvent("beforeshow", this);
2141             //xy = this.el.adjustForConstraints(xy);
2142         }
2143         
2144         //this.el.show();
2145         this.hideMenuItems();
2146         this.hidden = false;
2147         this.triggerEl.addClass('open');
2148         
2149         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2151         }
2152         
2153         if(this.el.getStyle('top').slice(-1) != "%"){
2154             this.el.setXY(xy);
2155         }
2156         
2157         this.focus();
2158         this.fireEvent("show", this);
2159     },
2160     
2161     focus : function(){
2162         return;
2163         if(!this.hidden){
2164             this.doFocus.defer(50, this);
2165         }
2166     },
2167
2168     doFocus : function(){
2169         if(!this.hidden){
2170             this.focusEl.focus();
2171         }
2172     },
2173
2174     /**
2175      * Hides this menu and optionally all parent menus
2176      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2177      */
2178     hide : function(deep)
2179     {
2180         
2181         this.hideMenuItems();
2182         if(this.el && this.isVisible()){
2183             this.fireEvent("beforehide", this);
2184             if(this.activeItem){
2185                 this.activeItem.deactivate();
2186                 this.activeItem = null;
2187             }
2188             this.triggerEl.removeClass('open');;
2189             this.hidden = true;
2190             this.fireEvent("hide", this);
2191         }
2192         if(deep === true && this.parentMenu){
2193             this.parentMenu.hide(true);
2194         }
2195     },
2196     
2197     onTriggerClick : function(e)
2198     {
2199         Roo.log('trigger click');
2200         
2201         var target = e.getTarget();
2202         
2203         Roo.log(target.nodeName.toLowerCase());
2204         
2205         if(target.nodeName.toLowerCase() === 'i'){
2206             e.preventDefault();
2207         }
2208         
2209     },
2210     
2211     onTriggerPress  : function(e)
2212     {
2213         Roo.log('trigger press');
2214         //Roo.log(e.getTarget());
2215        // Roo.log(this.triggerEl.dom);
2216        
2217         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2218         var pel = Roo.get(e.getTarget());
2219         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2220             Roo.log('is treeview or dropdown?');
2221             return;
2222         }
2223         
2224         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2225             return;
2226         }
2227         
2228         if (this.isVisible()) {
2229             Roo.log('hide');
2230             this.hide();
2231         } else {
2232             Roo.log('show');
2233             this.show(this.triggerEl, false, false);
2234         }
2235         
2236         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2237             e.stopEvent();
2238         }
2239         
2240     },
2241        
2242     
2243     hideMenuItems : function()
2244     {
2245         Roo.log("hide Menu Items");
2246         if (!this.el) { 
2247             return;
2248         }
2249         //$(backdrop).remove()
2250         this.el.select('.open',true).each(function(aa) {
2251             
2252             aa.removeClass('open');
2253           //var parent = getParent($(this))
2254           //var relatedTarget = { relatedTarget: this }
2255           
2256            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2257           //if (e.isDefaultPrevented()) return
2258            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2259         });
2260     },
2261     addxtypeChild : function (tree, cntr) {
2262         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2263           
2264         this.menuitems.add(comp);
2265         return comp;
2266
2267     },
2268     getEl : function()
2269     {
2270         Roo.log(this.el);
2271         return this.el;
2272     }
2273 });
2274
2275  
2276  /*
2277  * - LGPL
2278  *
2279  * menu item
2280  * 
2281  */
2282
2283
2284 /**
2285  * @class Roo.bootstrap.MenuItem
2286  * @extends Roo.bootstrap.Component
2287  * Bootstrap MenuItem class
2288  * @cfg {String} html the menu label
2289  * @cfg {String} href the link
2290  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2291  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2292  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2293  * @cfg {String} fa favicon to show on left of menu item.
2294  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2295  * 
2296  * 
2297  * @constructor
2298  * Create a new MenuItem
2299  * @param {Object} config The config object
2300  */
2301
2302
2303 Roo.bootstrap.MenuItem = function(config){
2304     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2305     this.addEvents({
2306         // raw events
2307         /**
2308          * @event click
2309          * The raw click event for the entire grid.
2310          * @param {Roo.bootstrap.MenuItem} this
2311          * @param {Roo.EventObject} e
2312          */
2313         "click" : true
2314     });
2315 };
2316
2317 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2318     
2319     href : false,
2320     html : false,
2321     preventDefault: true,
2322     isContainer : false,
2323     active : false,
2324     fa: false,
2325     
2326     getAutoCreate : function(){
2327         
2328         if(this.isContainer){
2329             return {
2330                 tag: 'li',
2331                 cls: 'dropdown-menu-item'
2332             };
2333         }
2334         var ctag = {
2335             tag: 'span',
2336             html: 'Link'
2337         };
2338         
2339         var anc = {
2340             tag : 'a',
2341             href : '#',
2342             cn : [  ]
2343         };
2344         
2345         if (this.fa !== false) {
2346             anc.cn.push({
2347                 tag : 'i',
2348                 cls : 'fa fa-' + this.fa
2349             });
2350         }
2351         
2352         anc.cn.push(ctag);
2353         
2354         
2355         var cfg= {
2356             tag: 'li',
2357             cls: 'dropdown-menu-item',
2358             cn: [ anc ]
2359         };
2360         if (this.parent().type == 'treeview') {
2361             cfg.cls = 'treeview-menu';
2362         }
2363         if (this.active) {
2364             cfg.cls += ' active';
2365         }
2366         
2367         
2368         
2369         anc.href = this.href || cfg.cn[0].href ;
2370         ctag.html = this.html || cfg.cn[0].html ;
2371         return cfg;
2372     },
2373     
2374     initEvents: function()
2375     {
2376         if (this.parent().type == 'treeview') {
2377             this.el.select('a').on('click', this.onClick, this);
2378         }
2379         if (this.menu) {
2380             this.menu.parentType = this.xtype;
2381             this.menu.triggerEl = this.el;
2382             this.menu = this.addxtype(Roo.apply({}, this.menu));
2383         }
2384         
2385     },
2386     onClick : function(e)
2387     {
2388         Roo.log('item on click ');
2389         //if(this.preventDefault){
2390         //    e.preventDefault();
2391         //}
2392         //this.parent().hideMenuItems();
2393         
2394         this.fireEvent('click', this, e);
2395     },
2396     getEl : function()
2397     {
2398         return this.el;
2399     } 
2400 });
2401
2402  
2403
2404  /*
2405  * - LGPL
2406  *
2407  * menu separator
2408  * 
2409  */
2410
2411
2412 /**
2413  * @class Roo.bootstrap.MenuSeparator
2414  * @extends Roo.bootstrap.Component
2415  * Bootstrap MenuSeparator class
2416  * 
2417  * @constructor
2418  * Create a new MenuItem
2419  * @param {Object} config The config object
2420  */
2421
2422
2423 Roo.bootstrap.MenuSeparator = function(config){
2424     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2428     
2429     getAutoCreate : function(){
2430         var cfg = {
2431             cls: 'divider',
2432             tag : 'li'
2433         };
2434         
2435         return cfg;
2436     }
2437    
2438 });
2439
2440  
2441
2442  
2443 /*
2444 * Licence: LGPL
2445 */
2446
2447 /**
2448  * @class Roo.bootstrap.Modal
2449  * @extends Roo.bootstrap.Component
2450  * Bootstrap Modal class
2451  * @cfg {String} title Title of dialog
2452  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2453  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2454  * @cfg {Boolean} specificTitle default false
2455  * @cfg {Array} buttons Array of buttons or standard button set..
2456  * @cfg {String} buttonPosition (left|right|center) default right
2457  * @cfg {Boolean} animate default true
2458  * @cfg {Boolean} allow_close default true
2459  * @cfg {Boolean} fitwindow default true
2460  * 
2461  * 
2462  * @constructor
2463  * Create a new Modal Dialog
2464  * @param {Object} config The config object
2465  */
2466
2467 Roo.bootstrap.Modal = function(config){
2468     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2469     this.addEvents({
2470         // raw events
2471         /**
2472          * @event btnclick
2473          * The raw btnclick event for the button
2474          * @param {Roo.EventObject} e
2475          */
2476         "btnclick" : true
2477     });
2478     this.buttons = this.buttons || [];
2479      
2480     if (this.tmpl) {
2481         this.tmpl = Roo.factory(this.tmpl);
2482     }
2483     
2484 };
2485
2486 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2487     
2488     title : 'test dialog',
2489    
2490     buttons : false,
2491     
2492     // set on load...
2493      
2494     html: false,
2495     
2496     tmp: false,
2497     
2498     specificTitle: false,
2499     
2500     buttonPosition: 'right',
2501     
2502     allow_close : true,
2503     
2504     animate : true,
2505     
2506     fitwindow: false,
2507     
2508     
2509      // private
2510     dialogEl: false,
2511     bodyEl:  false,
2512     footerEl:  false,
2513     titleEl:  false,
2514     closeEl:  false,
2515     
2516     
2517     onRender : function(ct, position)
2518     {
2519         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2520      
2521         if(!this.el){
2522             var cfg = Roo.apply({},  this.getAutoCreate());
2523             cfg.id = Roo.id();
2524             //if(!cfg.name){
2525             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2526             //}
2527             //if (!cfg.name.length) {
2528             //    delete cfg.name;
2529            // }
2530             if (this.cls) {
2531                 cfg.cls += ' ' + this.cls;
2532             }
2533             if (this.style) {
2534                 cfg.style = this.style;
2535             }
2536             this.el = Roo.get(document.body).createChild(cfg, position);
2537         }
2538         //var type = this.el.dom.type;
2539         
2540         
2541         if(this.tabIndex !== undefined){
2542             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2543         }
2544         
2545         this.dialogEl = this.el.select('.modal-dialog',true).first();
2546         this.bodyEl = this.el.select('.modal-body',true).first();
2547         this.closeEl = this.el.select('.modal-header .close', true).first();
2548         this.footerEl = this.el.select('.modal-footer',true).first();
2549         this.titleEl = this.el.select('.modal-title',true).first();
2550         
2551         
2552          
2553         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2554         this.maskEl.enableDisplayMode("block");
2555         this.maskEl.hide();
2556         //this.el.addClass("x-dlg-modal");
2557     
2558         if (this.buttons.length) {
2559             Roo.each(this.buttons, function(bb) {
2560                 var b = Roo.apply({}, bb);
2561                 b.xns = b.xns || Roo.bootstrap;
2562                 b.xtype = b.xtype || 'Button';
2563                 if (typeof(b.listeners) == 'undefined') {
2564                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2565                 }
2566                 
2567                 var btn = Roo.factory(b);
2568                 
2569                 btn.render(this.el.select('.modal-footer div').first());
2570                 
2571             },this);
2572         }
2573         // render the children.
2574         var nitems = [];
2575         
2576         if(typeof(this.items) != 'undefined'){
2577             var items = this.items;
2578             delete this.items;
2579
2580             for(var i =0;i < items.length;i++) {
2581                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2582             }
2583         }
2584         
2585         this.items = nitems;
2586         
2587         // where are these used - they used to be body/close/footer
2588         
2589        
2590         this.initEvents();
2591         //this.el.addClass([this.fieldClass, this.cls]);
2592         
2593     },
2594     
2595     getAutoCreate : function(){
2596         
2597         
2598         var bdy = {
2599                 cls : 'modal-body',
2600                 html : this.html || ''
2601         };
2602         
2603         var title = {
2604             tag: 'h4',
2605             cls : 'modal-title',
2606             html : this.title
2607         };
2608         
2609         if(this.specificTitle){
2610             title = this.title;
2611             
2612         };
2613         
2614         var header = [];
2615         if (this.allow_close) {
2616             header.push({
2617                 tag: 'button',
2618                 cls : 'close',
2619                 html : '&times'
2620             });
2621         }
2622         header.push(title);
2623         
2624         var modal = {
2625             cls: "modal",
2626             style : 'display: none',
2627             cn : [
2628                 {
2629                     cls: "modal-dialog",
2630                     cn : [
2631                         {
2632                             cls : "modal-content",
2633                             cn : [
2634                                 {
2635                                     cls : 'modal-header',
2636                                     cn : header
2637                                 },
2638                                 bdy,
2639                                 {
2640                                     cls : 'modal-footer',
2641                                     cn : [
2642                                         {
2643                                             tag: 'div',
2644                                             cls: 'btn-' + this.buttonPosition
2645                                         }
2646                                     ]
2647                                     
2648                                 }
2649                                 
2650                                 
2651                             ]
2652                             
2653                         }
2654                     ]
2655                         
2656                 }
2657             ]
2658         };
2659         
2660         if(this.animate){
2661             modal.cls += ' fade';
2662         }
2663         
2664         return modal;
2665           
2666     },
2667     getChildContainer : function() {
2668          
2669          return this.bodyEl;
2670         
2671     },
2672     getButtonContainer : function() {
2673          return this.el.select('.modal-footer div',true).first();
2674         
2675     },
2676     initEvents : function()
2677     {
2678         if (this.allow_close) {
2679             this.closeEl.on('click', this.hide, this);
2680         }
2681         Roo.EventManager.onWindowResize(this.resize, this, true);
2682         
2683  
2684     },
2685     
2686     resize : function()
2687     {
2688         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2689         if (this.fitwindow) {
2690             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2691             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2692             this.setSize(w,h)
2693         }
2694     },
2695     
2696     setSize : function(w,h)
2697     {
2698         if (!w && !h) {
2699             return;
2700         }
2701         this.resizeTo(w,h);
2702     },
2703     
2704     show : function() {
2705         
2706         if (!this.rendered) {
2707             this.render();
2708         }
2709         
2710         this.el.setStyle('display', 'block');
2711         
2712         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2713             var _this = this;
2714             (function(){
2715                 this.el.addClass('in');
2716             }).defer(50, this);
2717         }else{
2718             this.el.addClass('in');
2719             
2720         }
2721         
2722         // not sure how we can show data in here.. 
2723         //if (this.tmpl) {
2724         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2725         //}
2726         
2727         Roo.get(document.body).addClass("x-body-masked");
2728         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2729         this.maskEl.show();
2730         this.el.setStyle('zIndex', '10001');
2731        
2732         this.fireEvent('show', this);
2733         this.items.forEach(function(e) {
2734             e.layout ? e.layout() : false;
2735                 
2736         });
2737         this.resize();
2738         
2739         
2740         
2741     },
2742     hide : function()
2743     {
2744         this.maskEl.hide();
2745         Roo.get(document.body).removeClass("x-body-masked");
2746         this.el.removeClass('in');
2747         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2748         
2749         if(this.animate){ // why
2750             var _this = this;
2751             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2752         }else{
2753             this.el.setStyle('display', 'none');
2754         }
2755         
2756         this.fireEvent('hide', this);
2757     },
2758     
2759     addButton : function(str, cb)
2760     {
2761          
2762         
2763         var b = Roo.apply({}, { html : str } );
2764         b.xns = b.xns || Roo.bootstrap;
2765         b.xtype = b.xtype || 'Button';
2766         if (typeof(b.listeners) == 'undefined') {
2767             b.listeners = { click : cb.createDelegate(this)  };
2768         }
2769         
2770         var btn = Roo.factory(b);
2771            
2772         btn.render(this.el.select('.modal-footer div').first());
2773         
2774         return btn;   
2775        
2776     },
2777     
2778     setDefaultButton : function(btn)
2779     {
2780         //this.el.select('.modal-footer').()
2781     },
2782     diff : false,
2783     
2784     resizeTo: function(w,h)
2785     {
2786         // skip.. ?? why??
2787         
2788         this.dialogEl.setWidth(w);
2789         if (this.diff === false) {
2790             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2791         }
2792         
2793         this.bodyEl.setHeight(h-this.diff);
2794         
2795         
2796     },
2797     setContentSize  : function(w, h)
2798     {
2799         
2800     },
2801     onButtonClick: function(btn,e)
2802     {
2803         //Roo.log([a,b,c]);
2804         this.fireEvent('btnclick', btn.name, e);
2805     },
2806      /**
2807      * Set the title of the Dialog
2808      * @param {String} str new Title
2809      */
2810     setTitle: function(str) {
2811         this.titleEl.dom.innerHTML = str;    
2812     },
2813     /**
2814      * Set the body of the Dialog
2815      * @param {String} str new Title
2816      */
2817     setBody: function(str) {
2818         this.bodyEl.dom.innerHTML = str;    
2819     },
2820     /**
2821      * Set the body of the Dialog using the template
2822      * @param {Obj} data - apply this data to the template and replace the body contents.
2823      */
2824     applyBody: function(obj)
2825     {
2826         if (!this.tmpl) {
2827             Roo.log("Error - using apply Body without a template");
2828             //code
2829         }
2830         this.tmpl.overwrite(this.bodyEl, obj);
2831     }
2832     
2833 });
2834
2835
2836 Roo.apply(Roo.bootstrap.Modal,  {
2837     /**
2838          * Button config that displays a single OK button
2839          * @type Object
2840          */
2841         OK :  [{
2842             name : 'ok',
2843             weight : 'primary',
2844             html : 'OK'
2845         }], 
2846         /**
2847          * Button config that displays Yes and No buttons
2848          * @type Object
2849          */
2850         YESNO : [
2851             {
2852                 name  : 'no',
2853                 html : 'No'
2854             },
2855             {
2856                 name  :'yes',
2857                 weight : 'primary',
2858                 html : 'Yes'
2859             }
2860         ],
2861         
2862         /**
2863          * Button config that displays OK and Cancel buttons
2864          * @type Object
2865          */
2866         OKCANCEL : [
2867             {
2868                name : 'cancel',
2869                 html : 'Cancel'
2870             },
2871             {
2872                 name : 'ok',
2873                 weight : 'primary',
2874                 html : 'OK'
2875             }
2876         ],
2877         /**
2878          * Button config that displays Yes, No and Cancel buttons
2879          * @type Object
2880          */
2881         YESNOCANCEL : [
2882             {
2883                 name : 'yes',
2884                 weight : 'primary',
2885                 html : 'Yes'
2886             },
2887             {
2888                 name : 'no',
2889                 html : 'No'
2890             },
2891             {
2892                 name : 'cancel',
2893                 html : 'Cancel'
2894             }
2895         ]
2896 });
2897  
2898  /*
2899  * - LGPL
2900  *
2901  * messagebox - can be used as a replace
2902  * 
2903  */
2904 /**
2905  * @class Roo.MessageBox
2906  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2907  * Example usage:
2908  *<pre><code>
2909 // Basic alert:
2910 Roo.Msg.alert('Status', 'Changes saved successfully.');
2911
2912 // Prompt for user data:
2913 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2914     if (btn == 'ok'){
2915         // process text value...
2916     }
2917 });
2918
2919 // Show a dialog using config options:
2920 Roo.Msg.show({
2921    title:'Save Changes?',
2922    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2923    buttons: Roo.Msg.YESNOCANCEL,
2924    fn: processResult,
2925    animEl: 'elId'
2926 });
2927 </code></pre>
2928  * @singleton
2929  */
2930 Roo.bootstrap.MessageBox = function(){
2931     var dlg, opt, mask, waitTimer;
2932     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2933     var buttons, activeTextEl, bwidth;
2934
2935     
2936     // private
2937     var handleButton = function(button){
2938         dlg.hide();
2939         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2940     };
2941
2942     // private
2943     var handleHide = function(){
2944         if(opt && opt.cls){
2945             dlg.el.removeClass(opt.cls);
2946         }
2947         //if(waitTimer){
2948         //    Roo.TaskMgr.stop(waitTimer);
2949         //    waitTimer = null;
2950         //}
2951     };
2952
2953     // private
2954     var updateButtons = function(b){
2955         var width = 0;
2956         if(!b){
2957             buttons["ok"].hide();
2958             buttons["cancel"].hide();
2959             buttons["yes"].hide();
2960             buttons["no"].hide();
2961             //dlg.footer.dom.style.display = 'none';
2962             return width;
2963         }
2964         dlg.footerEl.dom.style.display = '';
2965         for(var k in buttons){
2966             if(typeof buttons[k] != "function"){
2967                 if(b[k]){
2968                     buttons[k].show();
2969                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2970                     width += buttons[k].el.getWidth()+15;
2971                 }else{
2972                     buttons[k].hide();
2973                 }
2974             }
2975         }
2976         return width;
2977     };
2978
2979     // private
2980     var handleEsc = function(d, k, e){
2981         if(opt && opt.closable !== false){
2982             dlg.hide();
2983         }
2984         if(e){
2985             e.stopEvent();
2986         }
2987     };
2988
2989     return {
2990         /**
2991          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2992          * @return {Roo.BasicDialog} The BasicDialog element
2993          */
2994         getDialog : function(){
2995            if(!dlg){
2996                 dlg = new Roo.bootstrap.Modal( {
2997                     //draggable: true,
2998                     //resizable:false,
2999                     //constraintoviewport:false,
3000                     //fixedcenter:true,
3001                     //collapsible : false,
3002                     //shim:true,
3003                     //modal: true,
3004                   //  width:400,
3005                   //  height:100,
3006                     //buttonAlign:"center",
3007                     closeClick : function(){
3008                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3009                             handleButton("no");
3010                         }else{
3011                             handleButton("cancel");
3012                         }
3013                     }
3014                 });
3015                 dlg.render();
3016                 dlg.on("hide", handleHide);
3017                 mask = dlg.mask;
3018                 //dlg.addKeyListener(27, handleEsc);
3019                 buttons = {};
3020                 this.buttons = buttons;
3021                 var bt = this.buttonText;
3022                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3023                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3024                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3025                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3026                 //Roo.log(buttons);
3027                 bodyEl = dlg.bodyEl.createChild({
3028
3029                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3030                         '<textarea class="roo-mb-textarea"></textarea>' +
3031                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3032                 });
3033                 msgEl = bodyEl.dom.firstChild;
3034                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3035                 textboxEl.enableDisplayMode();
3036                 textboxEl.addKeyListener([10,13], function(){
3037                     if(dlg.isVisible() && opt && opt.buttons){
3038                         if(opt.buttons.ok){
3039                             handleButton("ok");
3040                         }else if(opt.buttons.yes){
3041                             handleButton("yes");
3042                         }
3043                     }
3044                 });
3045                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3046                 textareaEl.enableDisplayMode();
3047                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3048                 progressEl.enableDisplayMode();
3049                 var pf = progressEl.dom.firstChild;
3050                 if (pf) {
3051                     pp = Roo.get(pf.firstChild);
3052                     pp.setHeight(pf.offsetHeight);
3053                 }
3054                 
3055             }
3056             return dlg;
3057         },
3058
3059         /**
3060          * Updates the message box body text
3061          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3062          * the XHTML-compliant non-breaking space character '&amp;#160;')
3063          * @return {Roo.MessageBox} This message box
3064          */
3065         updateText : function(text){
3066             if(!dlg.isVisible() && !opt.width){
3067                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3068             }
3069             msgEl.innerHTML = text || '&#160;';
3070       
3071             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3072             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3073             var w = Math.max(
3074                     Math.min(opt.width || cw , this.maxWidth), 
3075                     Math.max(opt.minWidth || this.minWidth, bwidth)
3076             );
3077             if(opt.prompt){
3078                 activeTextEl.setWidth(w);
3079             }
3080             if(dlg.isVisible()){
3081                 dlg.fixedcenter = false;
3082             }
3083             // to big, make it scroll. = But as usual stupid IE does not support
3084             // !important..
3085             
3086             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3087                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3088                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3089             } else {
3090                 bodyEl.dom.style.height = '';
3091                 bodyEl.dom.style.overflowY = '';
3092             }
3093             if (cw > w) {
3094                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3095             } else {
3096                 bodyEl.dom.style.overflowX = '';
3097             }
3098             
3099             dlg.setContentSize(w, bodyEl.getHeight());
3100             if(dlg.isVisible()){
3101                 dlg.fixedcenter = true;
3102             }
3103             return this;
3104         },
3105
3106         /**
3107          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3108          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3109          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3110          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3111          * @return {Roo.MessageBox} This message box
3112          */
3113         updateProgress : function(value, text){
3114             if(text){
3115                 this.updateText(text);
3116             }
3117             if (pp) { // weird bug on my firefox - for some reason this is not defined
3118                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3119             }
3120             return this;
3121         },        
3122
3123         /**
3124          * Returns true if the message box is currently displayed
3125          * @return {Boolean} True if the message box is visible, else false
3126          */
3127         isVisible : function(){
3128             return dlg && dlg.isVisible();  
3129         },
3130
3131         /**
3132          * Hides the message box if it is displayed
3133          */
3134         hide : function(){
3135             if(this.isVisible()){
3136                 dlg.hide();
3137             }  
3138         },
3139
3140         /**
3141          * Displays a new message box, or reinitializes an existing message box, based on the config options
3142          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3143          * The following config object properties are supported:
3144          * <pre>
3145 Property    Type             Description
3146 ----------  ---------------  ------------------------------------------------------------------------------------
3147 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3148                                    closes (defaults to undefined)
3149 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3150                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3151 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3152                                    progress and wait dialogs will ignore this property and always hide the
3153                                    close button as they can only be closed programmatically.
3154 cls               String           A custom CSS class to apply to the message box element
3155 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3156                                    displayed (defaults to 75)
3157 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3158                                    function will be btn (the name of the button that was clicked, if applicable,
3159                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3160                                    Progress and wait dialogs will ignore this option since they do not respond to
3161                                    user actions and can only be closed programmatically, so any required function
3162                                    should be called by the same code after it closes the dialog.
3163 icon              String           A CSS class that provides a background image to be used as an icon for
3164                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3165 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3166 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3167 modal             Boolean          False to allow user interaction with the page while the message box is
3168                                    displayed (defaults to true)
3169 msg               String           A string that will replace the existing message box body text (defaults
3170                                    to the XHTML-compliant non-breaking space character '&#160;')
3171 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3172 progress          Boolean          True to display a progress bar (defaults to false)
3173 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3174 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3175 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3176 title             String           The title text
3177 value             String           The string value to set into the active textbox element if displayed
3178 wait              Boolean          True to display a progress bar (defaults to false)
3179 width             Number           The width of the dialog in pixels
3180 </pre>
3181          *
3182          * Example usage:
3183          * <pre><code>
3184 Roo.Msg.show({
3185    title: 'Address',
3186    msg: 'Please enter your address:',
3187    width: 300,
3188    buttons: Roo.MessageBox.OKCANCEL,
3189    multiline: true,
3190    fn: saveAddress,
3191    animEl: 'addAddressBtn'
3192 });
3193 </code></pre>
3194          * @param {Object} config Configuration options
3195          * @return {Roo.MessageBox} This message box
3196          */
3197         show : function(options)
3198         {
3199             
3200             // this causes nightmares if you show one dialog after another
3201             // especially on callbacks..
3202              
3203             if(this.isVisible()){
3204                 
3205                 this.hide();
3206                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3207                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3208                 Roo.log("New Dialog Message:" +  options.msg )
3209                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3210                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3211                 
3212             }
3213             var d = this.getDialog();
3214             opt = options;
3215             d.setTitle(opt.title || "&#160;");
3216             d.closeEl.setDisplayed(opt.closable !== false);
3217             activeTextEl = textboxEl;
3218             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3219             if(opt.prompt){
3220                 if(opt.multiline){
3221                     textboxEl.hide();
3222                     textareaEl.show();
3223                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3224                         opt.multiline : this.defaultTextHeight);
3225                     activeTextEl = textareaEl;
3226                 }else{
3227                     textboxEl.show();
3228                     textareaEl.hide();
3229                 }
3230             }else{
3231                 textboxEl.hide();
3232                 textareaEl.hide();
3233             }
3234             progressEl.setDisplayed(opt.progress === true);
3235             this.updateProgress(0);
3236             activeTextEl.dom.value = opt.value || "";
3237             if(opt.prompt){
3238                 dlg.setDefaultButton(activeTextEl);
3239             }else{
3240                 var bs = opt.buttons;
3241                 var db = null;
3242                 if(bs && bs.ok){
3243                     db = buttons["ok"];
3244                 }else if(bs && bs.yes){
3245                     db = buttons["yes"];
3246                 }
3247                 dlg.setDefaultButton(db);
3248             }
3249             bwidth = updateButtons(opt.buttons);
3250             this.updateText(opt.msg);
3251             if(opt.cls){
3252                 d.el.addClass(opt.cls);
3253             }
3254             d.proxyDrag = opt.proxyDrag === true;
3255             d.modal = opt.modal !== false;
3256             d.mask = opt.modal !== false ? mask : false;
3257             if(!d.isVisible()){
3258                 // force it to the end of the z-index stack so it gets a cursor in FF
3259                 document.body.appendChild(dlg.el.dom);
3260                 d.animateTarget = null;
3261                 d.show(options.animEl);
3262             }
3263             return this;
3264         },
3265
3266         /**
3267          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3268          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3269          * and closing the message box when the process is complete.
3270          * @param {String} title The title bar text
3271          * @param {String} msg The message box body text
3272          * @return {Roo.MessageBox} This message box
3273          */
3274         progress : function(title, msg){
3275             this.show({
3276                 title : title,
3277                 msg : msg,
3278                 buttons: false,
3279                 progress:true,
3280                 closable:false,
3281                 minWidth: this.minProgressWidth,
3282                 modal : true
3283             });
3284             return this;
3285         },
3286
3287         /**
3288          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3289          * If a callback function is passed it will be called after the user clicks the button, and the
3290          * id of the button that was clicked will be passed as the only parameter to the callback
3291          * (could also be the top-right close button).
3292          * @param {String} title The title bar text
3293          * @param {String} msg The message box body text
3294          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3295          * @param {Object} scope (optional) The scope of the callback function
3296          * @return {Roo.MessageBox} This message box
3297          */
3298         alert : function(title, msg, fn, scope){
3299             this.show({
3300                 title : title,
3301                 msg : msg,
3302                 buttons: this.OK,
3303                 fn: fn,
3304                 scope : scope,
3305                 modal : true
3306             });
3307             return this;
3308         },
3309
3310         /**
3311          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3312          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3313          * You are responsible for closing the message box when the process is complete.
3314          * @param {String} msg The message box body text
3315          * @param {String} title (optional) The title bar text
3316          * @return {Roo.MessageBox} This message box
3317          */
3318         wait : function(msg, title){
3319             this.show({
3320                 title : title,
3321                 msg : msg,
3322                 buttons: false,
3323                 closable:false,
3324                 progress:true,
3325                 modal:true,
3326                 width:300,
3327                 wait:true
3328             });
3329             waitTimer = Roo.TaskMgr.start({
3330                 run: function(i){
3331                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3332                 },
3333                 interval: 1000
3334             });
3335             return this;
3336         },
3337
3338         /**
3339          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3340          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3341          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3342          * @param {String} title The title bar text
3343          * @param {String} msg The message box body text
3344          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3345          * @param {Object} scope (optional) The scope of the callback function
3346          * @return {Roo.MessageBox} This message box
3347          */
3348         confirm : function(title, msg, fn, scope){
3349             this.show({
3350                 title : title,
3351                 msg : msg,
3352                 buttons: this.YESNO,
3353                 fn: fn,
3354                 scope : scope,
3355                 modal : true
3356             });
3357             return this;
3358         },
3359
3360         /**
3361          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3362          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3363          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3364          * (could also be the top-right close button) and the text that was entered will be passed as the two
3365          * parameters to the callback.
3366          * @param {String} title The title bar text
3367          * @param {String} msg The message box body text
3368          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3369          * @param {Object} scope (optional) The scope of the callback function
3370          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3371          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3372          * @return {Roo.MessageBox} This message box
3373          */
3374         prompt : function(title, msg, fn, scope, multiline){
3375             this.show({
3376                 title : title,
3377                 msg : msg,
3378                 buttons: this.OKCANCEL,
3379                 fn: fn,
3380                 minWidth:250,
3381                 scope : scope,
3382                 prompt:true,
3383                 multiline: multiline,
3384                 modal : true
3385             });
3386             return this;
3387         },
3388
3389         /**
3390          * Button config that displays a single OK button
3391          * @type Object
3392          */
3393         OK : {ok:true},
3394         /**
3395          * Button config that displays Yes and No buttons
3396          * @type Object
3397          */
3398         YESNO : {yes:true, no:true},
3399         /**
3400          * Button config that displays OK and Cancel buttons
3401          * @type Object
3402          */
3403         OKCANCEL : {ok:true, cancel:true},
3404         /**
3405          * Button config that displays Yes, No and Cancel buttons
3406          * @type Object
3407          */
3408         YESNOCANCEL : {yes:true, no:true, cancel:true},
3409
3410         /**
3411          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3412          * @type Number
3413          */
3414         defaultTextHeight : 75,
3415         /**
3416          * The maximum width in pixels of the message box (defaults to 600)
3417          * @type Number
3418          */
3419         maxWidth : 600,
3420         /**
3421          * The minimum width in pixels of the message box (defaults to 100)
3422          * @type Number
3423          */
3424         minWidth : 100,
3425         /**
3426          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3427          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3428          * @type Number
3429          */
3430         minProgressWidth : 250,
3431         /**
3432          * An object containing the default button text strings that can be overriden for localized language support.
3433          * Supported properties are: ok, cancel, yes and no.
3434          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3435          * @type Object
3436          */
3437         buttonText : {
3438             ok : "OK",
3439             cancel : "Cancel",
3440             yes : "Yes",
3441             no : "No"
3442         }
3443     };
3444 }();
3445
3446 /**
3447  * Shorthand for {@link Roo.MessageBox}
3448  */
3449 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3450 Roo.Msg = Roo.Msg || Roo.MessageBox;
3451 /*
3452  * - LGPL
3453  *
3454  * navbar
3455  * 
3456  */
3457
3458 /**
3459  * @class Roo.bootstrap.Navbar
3460  * @extends Roo.bootstrap.Component
3461  * Bootstrap Navbar class
3462
3463  * @constructor
3464  * Create a new Navbar
3465  * @param {Object} config The config object
3466  */
3467
3468
3469 Roo.bootstrap.Navbar = function(config){
3470     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3471     this.addEvents({
3472         // raw events
3473         /**
3474          * @event beforetoggle
3475          * Fire before toggle the menu
3476          * @param {Roo.EventObject} e
3477          */
3478         "beforetoggle" : true
3479     });
3480 };
3481
3482 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3483     
3484     
3485    
3486     // private
3487     navItems : false,
3488     loadMask : false,
3489     
3490     
3491     getAutoCreate : function(){
3492         
3493         
3494         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3495         
3496     },
3497     
3498     initEvents :function ()
3499     {
3500         //Roo.log(this.el.select('.navbar-toggle',true));
3501         this.el.select('.navbar-toggle',true).on('click', function() {
3502             if(this.fireEvent('beforetoggle', this) !== false){
3503                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3504             }
3505             
3506         }, this);
3507         
3508         var mark = {
3509             tag: "div",
3510             cls:"x-dlg-mask"
3511         };
3512         
3513         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3514         
3515         var size = this.el.getSize();
3516         this.maskEl.setSize(size.width, size.height);
3517         this.maskEl.enableDisplayMode("block");
3518         this.maskEl.hide();
3519         
3520         if(this.loadMask){
3521             this.maskEl.show();
3522         }
3523     },
3524     
3525     
3526     getChildContainer : function()
3527     {
3528         if (this.el.select('.collapse').getCount()) {
3529             return this.el.select('.collapse',true).first();
3530         }
3531         
3532         return this.el;
3533     },
3534     
3535     mask : function()
3536     {
3537         this.maskEl.show();
3538     },
3539     
3540     unmask : function()
3541     {
3542         this.maskEl.hide();
3543     } 
3544     
3545     
3546     
3547     
3548 });
3549
3550
3551
3552  
3553
3554  /*
3555  * - LGPL
3556  *
3557  * navbar
3558  * 
3559  */
3560
3561 /**
3562  * @class Roo.bootstrap.NavSimplebar
3563  * @extends Roo.bootstrap.Navbar
3564  * Bootstrap Sidebar class
3565  *
3566  * @cfg {Boolean} inverse is inverted color
3567  * 
3568  * @cfg {String} type (nav | pills | tabs)
3569  * @cfg {Boolean} arrangement stacked | justified
3570  * @cfg {String} align (left | right) alignment
3571  * 
3572  * @cfg {Boolean} main (true|false) main nav bar? default false
3573  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3574  * 
3575  * @cfg {String} tag (header|footer|nav|div) default is nav 
3576
3577  * 
3578  * 
3579  * 
3580  * @constructor
3581  * Create a new Sidebar
3582  * @param {Object} config The config object
3583  */
3584
3585
3586 Roo.bootstrap.NavSimplebar = function(config){
3587     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3588 };
3589
3590 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3591     
3592     inverse: false,
3593     
3594     type: false,
3595     arrangement: '',
3596     align : false,
3597     
3598     
3599     
3600     main : false,
3601     
3602     
3603     tag : false,
3604     
3605     
3606     getAutoCreate : function(){
3607         
3608         
3609         var cfg = {
3610             tag : this.tag || 'div',
3611             cls : 'navbar'
3612         };
3613           
3614         
3615         cfg.cn = [
3616             {
3617                 cls: 'nav',
3618                 tag : 'ul'
3619             }
3620         ];
3621         
3622          
3623         this.type = this.type || 'nav';
3624         if (['tabs','pills'].indexOf(this.type)!==-1) {
3625             cfg.cn[0].cls += ' nav-' + this.type
3626         
3627         
3628         } else {
3629             if (this.type!=='nav') {
3630                 Roo.log('nav type must be nav/tabs/pills')
3631             }
3632             cfg.cn[0].cls += ' navbar-nav'
3633         }
3634         
3635         
3636         
3637         
3638         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3639             cfg.cn[0].cls += ' nav-' + this.arrangement;
3640         }
3641         
3642         
3643         if (this.align === 'right') {
3644             cfg.cn[0].cls += ' navbar-right';
3645         }
3646         
3647         if (this.inverse) {
3648             cfg.cls += ' navbar-inverse';
3649             
3650         }
3651         
3652         
3653         return cfg;
3654     
3655         
3656     }
3657     
3658     
3659     
3660 });
3661
3662
3663
3664  
3665
3666  
3667        /*
3668  * - LGPL
3669  *
3670  * navbar
3671  * 
3672  */
3673
3674 /**
3675  * @class Roo.bootstrap.NavHeaderbar
3676  * @extends Roo.bootstrap.NavSimplebar
3677  * Bootstrap Sidebar class
3678  *
3679  * @cfg {String} brand what is brand
3680  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3681  * @cfg {String} brand_href href of the brand
3682  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3683  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3684  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3685  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3686  * 
3687  * @constructor
3688  * Create a new Sidebar
3689  * @param {Object} config The config object
3690  */
3691
3692
3693 Roo.bootstrap.NavHeaderbar = function(config){
3694     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3695       
3696 };
3697
3698 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3699     
3700     position: '',
3701     brand: '',
3702     brand_href: false,
3703     srButton : true,
3704     autohide : false,
3705     desktopCenter : false,
3706    
3707     
3708     getAutoCreate : function(){
3709         
3710         var   cfg = {
3711             tag: this.nav || 'nav',
3712             cls: 'navbar',
3713             role: 'navigation',
3714             cn: []
3715         };
3716         
3717         var cn = cfg.cn;
3718         if (this.desktopCenter) {
3719             cn.push({cls : 'container', cn : []});
3720             cn = cn[0].cn;
3721         }
3722         
3723         if(this.srButton){
3724             cn.push({
3725                 tag: 'div',
3726                 cls: 'navbar-header',
3727                 cn: [
3728                     {
3729                         tag: 'button',
3730                         type: 'button',
3731                         cls: 'navbar-toggle',
3732                         'data-toggle': 'collapse',
3733                         cn: [
3734                             {
3735                                 tag: 'span',
3736                                 cls: 'sr-only',
3737                                 html: 'Toggle navigation'
3738                             },
3739                             {
3740                                 tag: 'span',
3741                                 cls: 'icon-bar'
3742                             },
3743                             {
3744                                 tag: 'span',
3745                                 cls: 'icon-bar'
3746                             },
3747                             {
3748                                 tag: 'span',
3749                                 cls: 'icon-bar'
3750                             }
3751                         ]
3752                     }
3753                 ]
3754             });
3755         }
3756         
3757         cn.push({
3758             tag: 'div',
3759             cls: 'collapse navbar-collapse',
3760             cn : []
3761         });
3762         
3763         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3764         
3765         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3766             cfg.cls += ' navbar-' + this.position;
3767             
3768             // tag can override this..
3769             
3770             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3771         }
3772         
3773         if (this.brand !== '') {
3774             cn[0].cn.push({
3775                 tag: 'a',
3776                 href: this.brand_href ? this.brand_href : '#',
3777                 cls: 'navbar-brand',
3778                 cn: [
3779                 this.brand
3780                 ]
3781             });
3782         }
3783         
3784         if(this.main){
3785             cfg.cls += ' main-nav';
3786         }
3787         
3788         
3789         return cfg;
3790
3791         
3792     },
3793     getHeaderChildContainer : function()
3794     {
3795         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3796             return this.el.select('.navbar-header',true).first();
3797         }
3798         
3799         return this.getChildContainer();
3800     },
3801     
3802     
3803     initEvents : function()
3804     {
3805         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3806         
3807         if (this.autohide) {
3808             
3809             var prevScroll = 0;
3810             var ft = this.el;
3811             
3812             Roo.get(document).on('scroll',function(e) {
3813                 var ns = Roo.get(document).getScroll().top;
3814                 var os = prevScroll;
3815                 prevScroll = ns;
3816                 
3817                 if(ns > os){
3818                     ft.removeClass('slideDown');
3819                     ft.addClass('slideUp');
3820                     return;
3821                 }
3822                 ft.removeClass('slideUp');
3823                 ft.addClass('slideDown');
3824                  
3825               
3826           },this);
3827         }
3828     }    
3829     
3830 });
3831
3832
3833
3834  
3835
3836  /*
3837  * - LGPL
3838  *
3839  * navbar
3840  * 
3841  */
3842
3843 /**
3844  * @class Roo.bootstrap.NavSidebar
3845  * @extends Roo.bootstrap.Navbar
3846  * Bootstrap Sidebar class
3847  * 
3848  * @constructor
3849  * Create a new Sidebar
3850  * @param {Object} config The config object
3851  */
3852
3853
3854 Roo.bootstrap.NavSidebar = function(config){
3855     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3856 };
3857
3858 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3859     
3860     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3861     
3862     getAutoCreate : function(){
3863         
3864         
3865         return  {
3866             tag: 'div',
3867             cls: 'sidebar sidebar-nav'
3868         };
3869     
3870         
3871     }
3872     
3873     
3874     
3875 });
3876
3877
3878
3879  
3880
3881  /*
3882  * - LGPL
3883  *
3884  * nav group
3885  * 
3886  */
3887
3888 /**
3889  * @class Roo.bootstrap.NavGroup
3890  * @extends Roo.bootstrap.Component
3891  * Bootstrap NavGroup class
3892  * @cfg {String} align (left|right)
3893  * @cfg {Boolean} inverse
3894  * @cfg {String} type (nav|pills|tab) default nav
3895  * @cfg {String} navId - reference Id for navbar.
3896
3897  * 
3898  * @constructor
3899  * Create a new nav group
3900  * @param {Object} config The config object
3901  */
3902
3903 Roo.bootstrap.NavGroup = function(config){
3904     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3905     this.navItems = [];
3906    
3907     Roo.bootstrap.NavGroup.register(this);
3908      this.addEvents({
3909         /**
3910              * @event changed
3911              * Fires when the active item changes
3912              * @param {Roo.bootstrap.NavGroup} this
3913              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3914              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3915          */
3916         'changed': true
3917      });
3918     
3919 };
3920
3921 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3922     
3923     align: '',
3924     inverse: false,
3925     form: false,
3926     type: 'nav',
3927     navId : '',
3928     // private
3929     
3930     navItems : false, 
3931     
3932     getAutoCreate : function()
3933     {
3934         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3935         
3936         cfg = {
3937             tag : 'ul',
3938             cls: 'nav' 
3939         };
3940         
3941         if (['tabs','pills'].indexOf(this.type)!==-1) {
3942             cfg.cls += ' nav-' + this.type
3943         } else {
3944             if (this.type!=='nav') {
3945                 Roo.log('nav type must be nav/tabs/pills')
3946             }
3947             cfg.cls += ' navbar-nav'
3948         }
3949         
3950         if (this.parent().sidebar) {
3951             cfg = {
3952                 tag: 'ul',
3953                 cls: 'dashboard-menu sidebar-menu'
3954             };
3955             
3956             return cfg;
3957         }
3958         
3959         if (this.form === true) {
3960             cfg = {
3961                 tag: 'form',
3962                 cls: 'navbar-form'
3963             };
3964             
3965             if (this.align === 'right') {
3966                 cfg.cls += ' navbar-right';
3967             } else {
3968                 cfg.cls += ' navbar-left';
3969             }
3970         }
3971         
3972         if (this.align === 'right') {
3973             cfg.cls += ' navbar-right';
3974         }
3975         
3976         if (this.inverse) {
3977             cfg.cls += ' navbar-inverse';
3978             
3979         }
3980         
3981         
3982         return cfg;
3983     },
3984     /**
3985     * sets the active Navigation item
3986     * @param {Roo.bootstrap.NavItem} the new current navitem
3987     */
3988     setActiveItem : function(item)
3989     {
3990         var prev = false;
3991         Roo.each(this.navItems, function(v){
3992             if (v == item) {
3993                 return ;
3994             }
3995             if (v.isActive()) {
3996                 v.setActive(false, true);
3997                 prev = v;
3998                 
3999             }
4000             
4001         });
4002
4003         item.setActive(true, true);
4004         this.fireEvent('changed', this, item, prev);
4005         
4006         
4007     },
4008     /**
4009     * gets the active Navigation item
4010     * @return {Roo.bootstrap.NavItem} the current navitem
4011     */
4012     getActive : function()
4013     {
4014         
4015         var prev = false;
4016         Roo.each(this.navItems, function(v){
4017             
4018             if (v.isActive()) {
4019                 prev = v;
4020                 
4021             }
4022             
4023         });
4024         return prev;
4025     },
4026     
4027     indexOfNav : function()
4028     {
4029         
4030         var prev = false;
4031         Roo.each(this.navItems, function(v,i){
4032             
4033             if (v.isActive()) {
4034                 prev = i;
4035                 
4036             }
4037             
4038         });
4039         return prev;
4040     },
4041     /**
4042     * adds a Navigation item
4043     * @param {Roo.bootstrap.NavItem} the navitem to add
4044     */
4045     addItem : function(cfg)
4046     {
4047         var cn = new Roo.bootstrap.NavItem(cfg);
4048         this.register(cn);
4049         cn.parentId = this.id;
4050         cn.onRender(this.el, null);
4051         return cn;
4052     },
4053     /**
4054     * register a Navigation item
4055     * @param {Roo.bootstrap.NavItem} the navitem to add
4056     */
4057     register : function(item)
4058     {
4059         this.navItems.push( item);
4060         item.navId = this.navId;
4061     
4062     },
4063     
4064     /**
4065     * clear all the Navigation item
4066     */
4067    
4068     clearAll : function()
4069     {
4070         this.navItems = [];
4071         this.el.dom.innerHTML = '';
4072     },
4073     
4074     getNavItem: function(tabId)
4075     {
4076         var ret = false;
4077         Roo.each(this.navItems, function(e) {
4078             if (e.tabId == tabId) {
4079                ret =  e;
4080                return false;
4081             }
4082             return true;
4083             
4084         });
4085         return ret;
4086     },
4087     
4088     setActiveNext : function()
4089     {
4090         var i = this.indexOfNav(this.getActive());
4091         if (i > this.navItems.length) {
4092             return;
4093         }
4094         this.setActiveItem(this.navItems[i+1]);
4095     },
4096     setActivePrev : function()
4097     {
4098         var i = this.indexOfNav(this.getActive());
4099         if (i  < 1) {
4100             return;
4101         }
4102         this.setActiveItem(this.navItems[i-1]);
4103     },
4104     clearWasActive : function(except) {
4105         Roo.each(this.navItems, function(e) {
4106             if (e.tabId != except.tabId && e.was_active) {
4107                e.was_active = false;
4108                return false;
4109             }
4110             return true;
4111             
4112         });
4113     },
4114     getWasActive : function ()
4115     {
4116         var r = false;
4117         Roo.each(this.navItems, function(e) {
4118             if (e.was_active) {
4119                r = e;
4120                return false;
4121             }
4122             return true;
4123             
4124         });
4125         return r;
4126     }
4127     
4128     
4129 });
4130
4131  
4132 Roo.apply(Roo.bootstrap.NavGroup, {
4133     
4134     groups: {},
4135      /**
4136     * register a Navigation Group
4137     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4138     */
4139     register : function(navgrp)
4140     {
4141         this.groups[navgrp.navId] = navgrp;
4142         
4143     },
4144     /**
4145     * fetch a Navigation Group based on the navigation ID
4146     * @param {string} the navgroup to add
4147     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4148     */
4149     get: function(navId) {
4150         if (typeof(this.groups[navId]) == 'undefined') {
4151             return false;
4152             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4153         }
4154         return this.groups[navId] ;
4155     }
4156     
4157     
4158     
4159 });
4160
4161  /*
4162  * - LGPL
4163  *
4164  * row
4165  * 
4166  */
4167
4168 /**
4169  * @class Roo.bootstrap.NavItem
4170  * @extends Roo.bootstrap.Component
4171  * Bootstrap Navbar.NavItem class
4172  * @cfg {String} href  link to
4173  * @cfg {String} html content of button
4174  * @cfg {String} badge text inside badge
4175  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4176  * @cfg {String} glyphicon name of glyphicon
4177  * @cfg {String} icon name of font awesome icon
4178  * @cfg {Boolean} active Is item active
4179  * @cfg {Boolean} disabled Is item disabled
4180  
4181  * @cfg {Boolean} preventDefault (true | false) default false
4182  * @cfg {String} tabId the tab that this item activates.
4183  * @cfg {String} tagtype (a|span) render as a href or span?
4184  * @cfg {Boolean} animateRef (true|false) link to element default false  
4185   
4186  * @constructor
4187  * Create a new Navbar Item
4188  * @param {Object} config The config object
4189  */
4190 Roo.bootstrap.NavItem = function(config){
4191     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4192     this.addEvents({
4193         // raw events
4194         /**
4195          * @event click
4196          * The raw click event for the entire grid.
4197          * @param {Roo.EventObject} e
4198          */
4199         "click" : true,
4200          /**
4201             * @event changed
4202             * Fires when the active item active state changes
4203             * @param {Roo.bootstrap.NavItem} this
4204             * @param {boolean} state the new state
4205              
4206          */
4207         'changed': true,
4208         /**
4209             * @event scrollto
4210             * Fires when scroll to element
4211             * @param {Roo.bootstrap.NavItem} this
4212             * @param {Object} options
4213             * @param {Roo.EventObject} e
4214              
4215          */
4216         'scrollto': true
4217     });
4218    
4219 };
4220
4221 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4222     
4223     href: false,
4224     html: '',
4225     badge: '',
4226     icon: false,
4227     glyphicon: false,
4228     active: false,
4229     preventDefault : false,
4230     tabId : false,
4231     tagtype : 'a',
4232     disabled : false,
4233     animateRef : false,
4234     was_active : false,
4235     
4236     getAutoCreate : function(){
4237          
4238         var cfg = {
4239             tag: 'li',
4240             cls: 'nav-item'
4241             
4242         };
4243         
4244         if (this.active) {
4245             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4246         }
4247         if (this.disabled) {
4248             cfg.cls += ' disabled';
4249         }
4250         
4251         if (this.href || this.html || this.glyphicon || this.icon) {
4252             cfg.cn = [
4253                 {
4254                     tag: this.tagtype,
4255                     href : this.href || "#",
4256                     html: this.html || ''
4257                 }
4258             ];
4259             
4260             if (this.icon) {
4261                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4262             }
4263
4264             if(this.glyphicon) {
4265                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4266             }
4267             
4268             if (this.menu) {
4269                 
4270                 cfg.cn[0].html += " <span class='caret'></span>";
4271              
4272             }
4273             
4274             if (this.badge !== '') {
4275                  
4276                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4277             }
4278         }
4279         
4280         
4281         
4282         return cfg;
4283     },
4284     initEvents: function() 
4285     {
4286         if (typeof (this.menu) != 'undefined') {
4287             this.menu.parentType = this.xtype;
4288             this.menu.triggerEl = this.el;
4289             this.menu = this.addxtype(Roo.apply({}, this.menu));
4290         }
4291         
4292         this.el.select('a',true).on('click', this.onClick, this);
4293         
4294         if(this.tagtype == 'span'){
4295             this.el.select('span',true).on('click', this.onClick, this);
4296         }
4297        
4298         // at this point parent should be available..
4299         this.parent().register(this);
4300     },
4301     
4302     onClick : function(e)
4303     {
4304         if (e.getTarget('.dropdown-menu-item')) {
4305             // did you click on a menu itemm.... - then don't trigger onclick..
4306             return;
4307         }
4308         
4309         if(
4310                 this.preventDefault || 
4311                 this.href == '#' 
4312         ){
4313             Roo.log("NavItem - prevent Default?");
4314             e.preventDefault();
4315         }
4316         
4317         if (this.disabled) {
4318             return;
4319         }
4320         
4321         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4322         if (tg && tg.transition) {
4323             Roo.log("waiting for the transitionend");
4324             return;
4325         }
4326         
4327         
4328         
4329         //Roo.log("fire event clicked");
4330         if(this.fireEvent('click', this, e) === false){
4331             return;
4332         };
4333         
4334         if(this.tagtype == 'span'){
4335             return;
4336         }
4337         
4338         //Roo.log(this.href);
4339         var ael = this.el.select('a',true).first();
4340         //Roo.log(ael);
4341         
4342         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4343             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4344             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4345                 return; // ignore... - it's a 'hash' to another page.
4346             }
4347             Roo.log("NavItem - prevent Default?");
4348             e.preventDefault();
4349             this.scrollToElement(e);
4350         }
4351         
4352         
4353         var p =  this.parent();
4354    
4355         if (['tabs','pills'].indexOf(p.type)!==-1) {
4356             if (typeof(p.setActiveItem) !== 'undefined') {
4357                 p.setActiveItem(this);
4358             }
4359         }
4360         
4361         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4362         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4363             // remove the collapsed menu expand...
4364             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4365         }
4366     },
4367     
4368     isActive: function () {
4369         return this.active
4370     },
4371     setActive : function(state, fire, is_was_active)
4372     {
4373         if (this.active && !state && this.navId) {
4374             this.was_active = true;
4375             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4376             if (nv) {
4377                 nv.clearWasActive(this);
4378             }
4379             
4380         }
4381         this.active = state;
4382         
4383         if (!state ) {
4384             this.el.removeClass('active');
4385         } else if (!this.el.hasClass('active')) {
4386             this.el.addClass('active');
4387         }
4388         if (fire) {
4389             this.fireEvent('changed', this, state);
4390         }
4391         
4392         // show a panel if it's registered and related..
4393         
4394         if (!this.navId || !this.tabId || !state || is_was_active) {
4395             return;
4396         }
4397         
4398         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4399         if (!tg) {
4400             return;
4401         }
4402         var pan = tg.getPanelByName(this.tabId);
4403         if (!pan) {
4404             return;
4405         }
4406         // if we can not flip to new panel - go back to old nav highlight..
4407         if (false == tg.showPanel(pan)) {
4408             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4409             if (nv) {
4410                 var onav = nv.getWasActive();
4411                 if (onav) {
4412                     onav.setActive(true, false, true);
4413                 }
4414             }
4415             
4416         }
4417         
4418         
4419         
4420     },
4421      // this should not be here...
4422     setDisabled : function(state)
4423     {
4424         this.disabled = state;
4425         if (!state ) {
4426             this.el.removeClass('disabled');
4427         } else if (!this.el.hasClass('disabled')) {
4428             this.el.addClass('disabled');
4429         }
4430         
4431     },
4432     
4433     /**
4434      * Fetch the element to display the tooltip on.
4435      * @return {Roo.Element} defaults to this.el
4436      */
4437     tooltipEl : function()
4438     {
4439         return this.el.select('' + this.tagtype + '', true).first();
4440     },
4441     
4442     scrollToElement : function(e)
4443     {
4444         var c = document.body;
4445         
4446         /*
4447          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4448          */
4449         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4450             c = document.documentElement;
4451         }
4452         
4453         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4454         
4455         if(!target){
4456             return;
4457         }
4458
4459         var o = target.calcOffsetsTo(c);
4460         
4461         var options = {
4462             target : target,
4463             value : o[1]
4464         };
4465         
4466         this.fireEvent('scrollto', this, options, e);
4467         
4468         Roo.get(c).scrollTo('top', options.value, true);
4469         
4470         return;
4471     }
4472 });
4473  
4474
4475  /*
4476  * - LGPL
4477  *
4478  * sidebar item
4479  *
4480  *  li
4481  *    <span> icon </span>
4482  *    <span> text </span>
4483  *    <span>badge </span>
4484  */
4485
4486 /**
4487  * @class Roo.bootstrap.NavSidebarItem
4488  * @extends Roo.bootstrap.NavItem
4489  * Bootstrap Navbar.NavSidebarItem class
4490  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4491  * {bool} open is the menu open
4492  * @constructor
4493  * Create a new Navbar Button
4494  * @param {Object} config The config object
4495  */
4496 Roo.bootstrap.NavSidebarItem = function(config){
4497     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4498     this.addEvents({
4499         // raw events
4500         /**
4501          * @event click
4502          * The raw click event for the entire grid.
4503          * @param {Roo.EventObject} e
4504          */
4505         "click" : true,
4506          /**
4507             * @event changed
4508             * Fires when the active item active state changes
4509             * @param {Roo.bootstrap.NavSidebarItem} this
4510             * @param {boolean} state the new state
4511              
4512          */
4513         'changed': true
4514     });
4515    
4516 };
4517
4518 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4519     
4520     badgeWeight : 'default',
4521     
4522     open: false,
4523     
4524     getAutoCreate : function(){
4525         
4526         
4527         var a = {
4528                 tag: 'a',
4529                 href : this.href || '#',
4530                 cls: '',
4531                 html : '',
4532                 cn : []
4533         };
4534         var cfg = {
4535             tag: 'li',
4536             cls: '',
4537             cn: [ a ]
4538         };
4539         var span = {
4540             tag: 'span',
4541             html : this.html || ''
4542         };
4543         
4544         
4545         if (this.active) {
4546             cfg.cls += ' active';
4547         }
4548         
4549         if (this.disabled) {
4550             cfg.cls += ' disabled';
4551         }
4552         if (this.open) {
4553             cfg.cls += ' open x-open';
4554         }
4555         // left icon..
4556         if (this.glyphicon || this.icon) {
4557             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4558             a.cn.push({ tag : 'i', cls : c }) ;
4559         }
4560         // html..
4561         a.cn.push(span);
4562         // then badge..
4563         if (this.badge !== '') {
4564             
4565             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4566         }
4567         // fi
4568         if (this.menu) {
4569             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4570             a.cls += 'dropdown-toggle treeview' ;
4571         }
4572         
4573         return cfg;
4574          
4575            
4576     },
4577     
4578     initEvents : function()
4579     { 
4580         if (typeof (this.menu) != 'undefined') {
4581             this.menu.parentType = this.xtype;
4582             this.menu.triggerEl = this.el;
4583             this.menu = this.addxtype(Roo.apply({}, this.menu));
4584         }
4585         
4586         this.el.on('click', this.onClick, this);
4587        
4588     
4589         if(this.badge !== ''){
4590  
4591             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4592         }
4593         
4594     },
4595     
4596     onClick : function(e)
4597     {
4598         if(this.disabled){
4599             e.preventDefault();
4600             return;
4601         }
4602         
4603         if(this.preventDefault){
4604             e.preventDefault();
4605         }
4606         
4607         this.fireEvent('click', this);
4608     },
4609     
4610     disable : function()
4611     {
4612         this.setDisabled(true);
4613     },
4614     
4615     enable : function()
4616     {
4617         this.setDisabled(false);
4618     },
4619     
4620     setDisabled : function(state)
4621     {
4622         if(this.disabled == state){
4623             return;
4624         }
4625         
4626         this.disabled = state;
4627         
4628         if (state) {
4629             this.el.addClass('disabled');
4630             return;
4631         }
4632         
4633         this.el.removeClass('disabled');
4634         
4635         return;
4636     },
4637     
4638     setActive : function(state)
4639     {
4640         if(this.active == state){
4641             return;
4642         }
4643         
4644         this.active = state;
4645         
4646         if (state) {
4647             this.el.addClass('active');
4648             return;
4649         }
4650         
4651         this.el.removeClass('active');
4652         
4653         return;
4654     },
4655     
4656     isActive: function () 
4657     {
4658         return this.active;
4659     },
4660     
4661     setBadge : function(str)
4662     {
4663         if(!this.badgeEl){
4664             return;
4665         }
4666         
4667         this.badgeEl.dom.innerHTML = str;
4668     }
4669     
4670    
4671      
4672  
4673 });
4674  
4675
4676  /*
4677  * - LGPL
4678  *
4679  * row
4680  * 
4681  */
4682
4683 /**
4684  * @class Roo.bootstrap.Row
4685  * @extends Roo.bootstrap.Component
4686  * Bootstrap Row class (contains columns...)
4687  * 
4688  * @constructor
4689  * Create a new Row
4690  * @param {Object} config The config object
4691  */
4692
4693 Roo.bootstrap.Row = function(config){
4694     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4695 };
4696
4697 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4698     
4699     getAutoCreate : function(){
4700        return {
4701             cls: 'row clearfix'
4702        };
4703     }
4704     
4705     
4706 });
4707
4708  
4709
4710  /*
4711  * - LGPL
4712  *
4713  * element
4714  * 
4715  */
4716
4717 /**
4718  * @class Roo.bootstrap.Element
4719  * @extends Roo.bootstrap.Component
4720  * Bootstrap Element class
4721  * @cfg {String} html contents of the element
4722  * @cfg {String} tag tag of the element
4723  * @cfg {String} cls class of the element
4724  * @cfg {Boolean} preventDefault (true|false) default false
4725  * @cfg {Boolean} clickable (true|false) default false
4726  * 
4727  * @constructor
4728  * Create a new Element
4729  * @param {Object} config The config object
4730  */
4731
4732 Roo.bootstrap.Element = function(config){
4733     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4734     
4735     this.addEvents({
4736         // raw events
4737         /**
4738          * @event click
4739          * When a element is chick
4740          * @param {Roo.bootstrap.Element} this
4741          * @param {Roo.EventObject} e
4742          */
4743         "click" : true
4744     });
4745 };
4746
4747 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4748     
4749     tag: 'div',
4750     cls: '',
4751     html: '',
4752     preventDefault: false, 
4753     clickable: false,
4754     
4755     getAutoCreate : function(){
4756         
4757         var cfg = {
4758             tag: this.tag,
4759             cls: this.cls,
4760             html: this.html
4761         };
4762         
4763         return cfg;
4764     },
4765     
4766     initEvents: function() 
4767     {
4768         Roo.bootstrap.Element.superclass.initEvents.call(this);
4769         
4770         if(this.clickable){
4771             this.el.on('click', this.onClick, this);
4772         }
4773         
4774     },
4775     
4776     onClick : function(e)
4777     {
4778         if(this.preventDefault){
4779             e.preventDefault();
4780         }
4781         
4782         this.fireEvent('click', this, e);
4783     },
4784     
4785     getValue : function()
4786     {
4787         return this.el.dom.innerHTML;
4788     },
4789     
4790     setValue : function(value)
4791     {
4792         this.el.dom.innerHTML = value;
4793     }
4794    
4795 });
4796
4797  
4798
4799  /*
4800  * - LGPL
4801  *
4802  * pagination
4803  * 
4804  */
4805
4806 /**
4807  * @class Roo.bootstrap.Pagination
4808  * @extends Roo.bootstrap.Component
4809  * Bootstrap Pagination class
4810  * @cfg {String} size xs | sm | md | lg
4811  * @cfg {Boolean} inverse false | true
4812  * 
4813  * @constructor
4814  * Create a new Pagination
4815  * @param {Object} config The config object
4816  */
4817
4818 Roo.bootstrap.Pagination = function(config){
4819     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4820 };
4821
4822 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4823     
4824     cls: false,
4825     size: false,
4826     inverse: false,
4827     
4828     getAutoCreate : function(){
4829         var cfg = {
4830             tag: 'ul',
4831                 cls: 'pagination'
4832         };
4833         if (this.inverse) {
4834             cfg.cls += ' inverse';
4835         }
4836         if (this.html) {
4837             cfg.html=this.html;
4838         }
4839         if (this.cls) {
4840             cfg.cls += " " + this.cls;
4841         }
4842         return cfg;
4843     }
4844    
4845 });
4846
4847  
4848
4849  /*
4850  * - LGPL
4851  *
4852  * Pagination item
4853  * 
4854  */
4855
4856
4857 /**
4858  * @class Roo.bootstrap.PaginationItem
4859  * @extends Roo.bootstrap.Component
4860  * Bootstrap PaginationItem class
4861  * @cfg {String} html text
4862  * @cfg {String} href the link
4863  * @cfg {Boolean} preventDefault (true | false) default true
4864  * @cfg {Boolean} active (true | false) default false
4865  * @cfg {Boolean} disabled default false
4866  * 
4867  * 
4868  * @constructor
4869  * Create a new PaginationItem
4870  * @param {Object} config The config object
4871  */
4872
4873
4874 Roo.bootstrap.PaginationItem = function(config){
4875     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4876     this.addEvents({
4877         // raw events
4878         /**
4879          * @event click
4880          * The raw click event for the entire grid.
4881          * @param {Roo.EventObject} e
4882          */
4883         "click" : true
4884     });
4885 };
4886
4887 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4888     
4889     href : false,
4890     html : false,
4891     preventDefault: true,
4892     active : false,
4893     cls : false,
4894     disabled: false,
4895     
4896     getAutoCreate : function(){
4897         var cfg= {
4898             tag: 'li',
4899             cn: [
4900                 {
4901                     tag : 'a',
4902                     href : this.href ? this.href : '#',
4903                     html : this.html ? this.html : ''
4904                 }
4905             ]
4906         };
4907         
4908         if(this.cls){
4909             cfg.cls = this.cls;
4910         }
4911         
4912         if(this.disabled){
4913             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4914         }
4915         
4916         if(this.active){
4917             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4918         }
4919         
4920         return cfg;
4921     },
4922     
4923     initEvents: function() {
4924         
4925         this.el.on('click', this.onClick, this);
4926         
4927     },
4928     onClick : function(e)
4929     {
4930         Roo.log('PaginationItem on click ');
4931         if(this.preventDefault){
4932             e.preventDefault();
4933         }
4934         
4935         if(this.disabled){
4936             return;
4937         }
4938         
4939         this.fireEvent('click', this, e);
4940     }
4941    
4942 });
4943
4944  
4945
4946  /*
4947  * - LGPL
4948  *
4949  * slider
4950  * 
4951  */
4952
4953
4954 /**
4955  * @class Roo.bootstrap.Slider
4956  * @extends Roo.bootstrap.Component
4957  * Bootstrap Slider class
4958  *    
4959  * @constructor
4960  * Create a new Slider
4961  * @param {Object} config The config object
4962  */
4963
4964 Roo.bootstrap.Slider = function(config){
4965     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4966 };
4967
4968 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4969     
4970     getAutoCreate : function(){
4971         
4972         var cfg = {
4973             tag: 'div',
4974             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4975             cn: [
4976                 {
4977                     tag: 'a',
4978                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4979                 }
4980             ]
4981         };
4982         
4983         return cfg;
4984     }
4985    
4986 });
4987
4988  /*
4989  * Based on:
4990  * Ext JS Library 1.1.1
4991  * Copyright(c) 2006-2007, Ext JS, LLC.
4992  *
4993  * Originally Released Under LGPL - original licence link has changed is not relivant.
4994  *
4995  * Fork - LGPL
4996  * <script type="text/javascript">
4997  */
4998  
4999
5000 /**
5001  * @class Roo.grid.ColumnModel
5002  * @extends Roo.util.Observable
5003  * This is the default implementation of a ColumnModel used by the Grid. It defines
5004  * the columns in the grid.
5005  * <br>Usage:<br>
5006  <pre><code>
5007  var colModel = new Roo.grid.ColumnModel([
5008         {header: "Ticker", width: 60, sortable: true, locked: true},
5009         {header: "Company Name", width: 150, sortable: true},
5010         {header: "Market Cap.", width: 100, sortable: true},
5011         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5012         {header: "Employees", width: 100, sortable: true, resizable: false}
5013  ]);
5014  </code></pre>
5015  * <p>
5016  
5017  * The config options listed for this class are options which may appear in each
5018  * individual column definition.
5019  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5020  * @constructor
5021  * @param {Object} config An Array of column config objects. See this class's
5022  * config objects for details.
5023 */
5024 Roo.grid.ColumnModel = function(config){
5025         /**
5026      * The config passed into the constructor
5027      */
5028     this.config = config;
5029     this.lookup = {};
5030
5031     // if no id, create one
5032     // if the column does not have a dataIndex mapping,
5033     // map it to the order it is in the config
5034     for(var i = 0, len = config.length; i < len; i++){
5035         var c = config[i];
5036         if(typeof c.dataIndex == "undefined"){
5037             c.dataIndex = i;
5038         }
5039         if(typeof c.renderer == "string"){
5040             c.renderer = Roo.util.Format[c.renderer];
5041         }
5042         if(typeof c.id == "undefined"){
5043             c.id = Roo.id();
5044         }
5045         if(c.editor && c.editor.xtype){
5046             c.editor  = Roo.factory(c.editor, Roo.grid);
5047         }
5048         if(c.editor && c.editor.isFormField){
5049             c.editor = new Roo.grid.GridEditor(c.editor);
5050         }
5051         this.lookup[c.id] = c;
5052     }
5053
5054     /**
5055      * The width of columns which have no width specified (defaults to 100)
5056      * @type Number
5057      */
5058     this.defaultWidth = 100;
5059
5060     /**
5061      * Default sortable of columns which have no sortable specified (defaults to false)
5062      * @type Boolean
5063      */
5064     this.defaultSortable = false;
5065
5066     this.addEvents({
5067         /**
5068              * @event widthchange
5069              * Fires when the width of a column changes.
5070              * @param {ColumnModel} this
5071              * @param {Number} columnIndex The column index
5072              * @param {Number} newWidth The new width
5073              */
5074             "widthchange": true,
5075         /**
5076              * @event headerchange
5077              * Fires when the text of a header changes.
5078              * @param {ColumnModel} this
5079              * @param {Number} columnIndex The column index
5080              * @param {Number} newText The new header text
5081              */
5082             "headerchange": true,
5083         /**
5084              * @event hiddenchange
5085              * Fires when a column is hidden or "unhidden".
5086              * @param {ColumnModel} this
5087              * @param {Number} columnIndex The column index
5088              * @param {Boolean} hidden true if hidden, false otherwise
5089              */
5090             "hiddenchange": true,
5091             /**
5092          * @event columnmoved
5093          * Fires when a column is moved.
5094          * @param {ColumnModel} this
5095          * @param {Number} oldIndex
5096          * @param {Number} newIndex
5097          */
5098         "columnmoved" : true,
5099         /**
5100          * @event columlockchange
5101          * Fires when a column's locked state is changed
5102          * @param {ColumnModel} this
5103          * @param {Number} colIndex
5104          * @param {Boolean} locked true if locked
5105          */
5106         "columnlockchange" : true
5107     });
5108     Roo.grid.ColumnModel.superclass.constructor.call(this);
5109 };
5110 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5111     /**
5112      * @cfg {String} header The header text to display in the Grid view.
5113      */
5114     /**
5115      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5116      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5117      * specified, the column's index is used as an index into the Record's data Array.
5118      */
5119     /**
5120      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5121      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5122      */
5123     /**
5124      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5125      * Defaults to the value of the {@link #defaultSortable} property.
5126      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5127      */
5128     /**
5129      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5130      */
5131     /**
5132      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5133      */
5134     /**
5135      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5136      */
5137     /**
5138      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5139      */
5140     /**
5141      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5142      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5143      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5144      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5145      */
5146        /**
5147      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5148      */
5149     /**
5150      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5151      */
5152     /**
5153      * @cfg {String} cursor (Optional)
5154      */
5155     /**
5156      * @cfg {String} tooltip (Optional)
5157      */
5158     /**
5159      * @cfg {Number} xs (Optional)
5160      */
5161     /**
5162      * @cfg {Number} sm (Optional)
5163      */
5164     /**
5165      * @cfg {Number} md (Optional)
5166      */
5167     /**
5168      * @cfg {Number} lg (Optional)
5169      */
5170     /**
5171      * Returns the id of the column at the specified index.
5172      * @param {Number} index The column index
5173      * @return {String} the id
5174      */
5175     getColumnId : function(index){
5176         return this.config[index].id;
5177     },
5178
5179     /**
5180      * Returns the column for a specified id.
5181      * @param {String} id The column id
5182      * @return {Object} the column
5183      */
5184     getColumnById : function(id){
5185         return this.lookup[id];
5186     },
5187
5188     
5189     /**
5190      * Returns the column for a specified dataIndex.
5191      * @param {String} dataIndex The column dataIndex
5192      * @return {Object|Boolean} the column or false if not found
5193      */
5194     getColumnByDataIndex: function(dataIndex){
5195         var index = this.findColumnIndex(dataIndex);
5196         return index > -1 ? this.config[index] : false;
5197     },
5198     
5199     /**
5200      * Returns the index for a specified column id.
5201      * @param {String} id The column id
5202      * @return {Number} the index, or -1 if not found
5203      */
5204     getIndexById : function(id){
5205         for(var i = 0, len = this.config.length; i < len; i++){
5206             if(this.config[i].id == id){
5207                 return i;
5208             }
5209         }
5210         return -1;
5211     },
5212     
5213     /**
5214      * Returns the index for a specified column dataIndex.
5215      * @param {String} dataIndex The column dataIndex
5216      * @return {Number} the index, or -1 if not found
5217      */
5218     
5219     findColumnIndex : function(dataIndex){
5220         for(var i = 0, len = this.config.length; i < len; i++){
5221             if(this.config[i].dataIndex == dataIndex){
5222                 return i;
5223             }
5224         }
5225         return -1;
5226     },
5227     
5228     
5229     moveColumn : function(oldIndex, newIndex){
5230         var c = this.config[oldIndex];
5231         this.config.splice(oldIndex, 1);
5232         this.config.splice(newIndex, 0, c);
5233         this.dataMap = null;
5234         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5235     },
5236
5237     isLocked : function(colIndex){
5238         return this.config[colIndex].locked === true;
5239     },
5240
5241     setLocked : function(colIndex, value, suppressEvent){
5242         if(this.isLocked(colIndex) == value){
5243             return;
5244         }
5245         this.config[colIndex].locked = value;
5246         if(!suppressEvent){
5247             this.fireEvent("columnlockchange", this, colIndex, value);
5248         }
5249     },
5250
5251     getTotalLockedWidth : function(){
5252         var totalWidth = 0;
5253         for(var i = 0; i < this.config.length; i++){
5254             if(this.isLocked(i) && !this.isHidden(i)){
5255                 this.totalWidth += this.getColumnWidth(i);
5256             }
5257         }
5258         return totalWidth;
5259     },
5260
5261     getLockedCount : function(){
5262         for(var i = 0, len = this.config.length; i < len; i++){
5263             if(!this.isLocked(i)){
5264                 return i;
5265             }
5266         }
5267         
5268         return this.config.length;
5269     },
5270
5271     /**
5272      * Returns the number of columns.
5273      * @return {Number}
5274      */
5275     getColumnCount : function(visibleOnly){
5276         if(visibleOnly === true){
5277             var c = 0;
5278             for(var i = 0, len = this.config.length; i < len; i++){
5279                 if(!this.isHidden(i)){
5280                     c++;
5281                 }
5282             }
5283             return c;
5284         }
5285         return this.config.length;
5286     },
5287
5288     /**
5289      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5290      * @param {Function} fn
5291      * @param {Object} scope (optional)
5292      * @return {Array} result
5293      */
5294     getColumnsBy : function(fn, scope){
5295         var r = [];
5296         for(var i = 0, len = this.config.length; i < len; i++){
5297             var c = this.config[i];
5298             if(fn.call(scope||this, c, i) === true){
5299                 r[r.length] = c;
5300             }
5301         }
5302         return r;
5303     },
5304
5305     /**
5306      * Returns true if the specified column is sortable.
5307      * @param {Number} col The column index
5308      * @return {Boolean}
5309      */
5310     isSortable : function(col){
5311         if(typeof this.config[col].sortable == "undefined"){
5312             return this.defaultSortable;
5313         }
5314         return this.config[col].sortable;
5315     },
5316
5317     /**
5318      * Returns the rendering (formatting) function defined for the column.
5319      * @param {Number} col The column index.
5320      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5321      */
5322     getRenderer : function(col){
5323         if(!this.config[col].renderer){
5324             return Roo.grid.ColumnModel.defaultRenderer;
5325         }
5326         return this.config[col].renderer;
5327     },
5328
5329     /**
5330      * Sets the rendering (formatting) function for a column.
5331      * @param {Number} col The column index
5332      * @param {Function} fn The function to use to process the cell's raw data
5333      * to return HTML markup for the grid view. The render function is called with
5334      * the following parameters:<ul>
5335      * <li>Data value.</li>
5336      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5337      * <li>css A CSS style string to apply to the table cell.</li>
5338      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5339      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5340      * <li>Row index</li>
5341      * <li>Column index</li>
5342      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5343      */
5344     setRenderer : function(col, fn){
5345         this.config[col].renderer = fn;
5346     },
5347
5348     /**
5349      * Returns the width for the specified column.
5350      * @param {Number} col The column index
5351      * @return {Number}
5352      */
5353     getColumnWidth : function(col){
5354         return this.config[col].width * 1 || this.defaultWidth;
5355     },
5356
5357     /**
5358      * Sets the width for a column.
5359      * @param {Number} col The column index
5360      * @param {Number} width The new width
5361      */
5362     setColumnWidth : function(col, width, suppressEvent){
5363         this.config[col].width = width;
5364         this.totalWidth = null;
5365         if(!suppressEvent){
5366              this.fireEvent("widthchange", this, col, width);
5367         }
5368     },
5369
5370     /**
5371      * Returns the total width of all columns.
5372      * @param {Boolean} includeHidden True to include hidden column widths
5373      * @return {Number}
5374      */
5375     getTotalWidth : function(includeHidden){
5376         if(!this.totalWidth){
5377             this.totalWidth = 0;
5378             for(var i = 0, len = this.config.length; i < len; i++){
5379                 if(includeHidden || !this.isHidden(i)){
5380                     this.totalWidth += this.getColumnWidth(i);
5381                 }
5382             }
5383         }
5384         return this.totalWidth;
5385     },
5386
5387     /**
5388      * Returns the header for the specified column.
5389      * @param {Number} col The column index
5390      * @return {String}
5391      */
5392     getColumnHeader : function(col){
5393         return this.config[col].header;
5394     },
5395
5396     /**
5397      * Sets the header for a column.
5398      * @param {Number} col The column index
5399      * @param {String} header The new header
5400      */
5401     setColumnHeader : function(col, header){
5402         this.config[col].header = header;
5403         this.fireEvent("headerchange", this, col, header);
5404     },
5405
5406     /**
5407      * Returns the tooltip for the specified column.
5408      * @param {Number} col The column index
5409      * @return {String}
5410      */
5411     getColumnTooltip : function(col){
5412             return this.config[col].tooltip;
5413     },
5414     /**
5415      * Sets the tooltip for a column.
5416      * @param {Number} col The column index
5417      * @param {String} tooltip The new tooltip
5418      */
5419     setColumnTooltip : function(col, tooltip){
5420             this.config[col].tooltip = tooltip;
5421     },
5422
5423     /**
5424      * Returns the dataIndex for the specified column.
5425      * @param {Number} col The column index
5426      * @return {Number}
5427      */
5428     getDataIndex : function(col){
5429         return this.config[col].dataIndex;
5430     },
5431
5432     /**
5433      * Sets the dataIndex for a column.
5434      * @param {Number} col The column index
5435      * @param {Number} dataIndex The new dataIndex
5436      */
5437     setDataIndex : function(col, dataIndex){
5438         this.config[col].dataIndex = dataIndex;
5439     },
5440
5441     
5442     
5443     /**
5444      * Returns true if the cell is editable.
5445      * @param {Number} colIndex The column index
5446      * @param {Number} rowIndex The row index - this is nto actually used..?
5447      * @return {Boolean}
5448      */
5449     isCellEditable : function(colIndex, rowIndex){
5450         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5451     },
5452
5453     /**
5454      * Returns the editor defined for the cell/column.
5455      * return false or null to disable editing.
5456      * @param {Number} colIndex The column index
5457      * @param {Number} rowIndex The row index
5458      * @return {Object}
5459      */
5460     getCellEditor : function(colIndex, rowIndex){
5461         return this.config[colIndex].editor;
5462     },
5463
5464     /**
5465      * Sets if a column is editable.
5466      * @param {Number} col The column index
5467      * @param {Boolean} editable True if the column is editable
5468      */
5469     setEditable : function(col, editable){
5470         this.config[col].editable = editable;
5471     },
5472
5473
5474     /**
5475      * Returns true if the column is hidden.
5476      * @param {Number} colIndex The column index
5477      * @return {Boolean}
5478      */
5479     isHidden : function(colIndex){
5480         return this.config[colIndex].hidden;
5481     },
5482
5483
5484     /**
5485      * Returns true if the column width cannot be changed
5486      */
5487     isFixed : function(colIndex){
5488         return this.config[colIndex].fixed;
5489     },
5490
5491     /**
5492      * Returns true if the column can be resized
5493      * @return {Boolean}
5494      */
5495     isResizable : function(colIndex){
5496         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5497     },
5498     /**
5499      * Sets if a column is hidden.
5500      * @param {Number} colIndex The column index
5501      * @param {Boolean} hidden True if the column is hidden
5502      */
5503     setHidden : function(colIndex, hidden){
5504         this.config[colIndex].hidden = hidden;
5505         this.totalWidth = null;
5506         this.fireEvent("hiddenchange", this, colIndex, hidden);
5507     },
5508
5509     /**
5510      * Sets the editor for a column.
5511      * @param {Number} col The column index
5512      * @param {Object} editor The editor object
5513      */
5514     setEditor : function(col, editor){
5515         this.config[col].editor = editor;
5516     }
5517 });
5518
5519 Roo.grid.ColumnModel.defaultRenderer = function(value){
5520         if(typeof value == "string" && value.length < 1){
5521             return "&#160;";
5522         }
5523         return value;
5524 };
5525
5526 // Alias for backwards compatibility
5527 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5528 /*
5529  * Based on:
5530  * Ext JS Library 1.1.1
5531  * Copyright(c) 2006-2007, Ext JS, LLC.
5532  *
5533  * Originally Released Under LGPL - original licence link has changed is not relivant.
5534  *
5535  * Fork - LGPL
5536  * <script type="text/javascript">
5537  */
5538  
5539 /**
5540  * @class Roo.LoadMask
5541  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5542  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5543  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5544  * element's UpdateManager load indicator and will be destroyed after the initial load.
5545  * @constructor
5546  * Create a new LoadMask
5547  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5548  * @param {Object} config The config object
5549  */
5550 Roo.LoadMask = function(el, config){
5551     this.el = Roo.get(el);
5552     Roo.apply(this, config);
5553     if(this.store){
5554         this.store.on('beforeload', this.onBeforeLoad, this);
5555         this.store.on('load', this.onLoad, this);
5556         this.store.on('loadexception', this.onLoadException, this);
5557         this.removeMask = false;
5558     }else{
5559         var um = this.el.getUpdateManager();
5560         um.showLoadIndicator = false; // disable the default indicator
5561         um.on('beforeupdate', this.onBeforeLoad, this);
5562         um.on('update', this.onLoad, this);
5563         um.on('failure', this.onLoad, this);
5564         this.removeMask = true;
5565     }
5566 };
5567
5568 Roo.LoadMask.prototype = {
5569     /**
5570      * @cfg {Boolean} removeMask
5571      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5572      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5573      */
5574     /**
5575      * @cfg {String} msg
5576      * The text to display in a centered loading message box (defaults to 'Loading...')
5577      */
5578     msg : 'Loading...',
5579     /**
5580      * @cfg {String} msgCls
5581      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5582      */
5583     msgCls : 'x-mask-loading',
5584
5585     /**
5586      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5587      * @type Boolean
5588      */
5589     disabled: false,
5590
5591     /**
5592      * Disables the mask to prevent it from being displayed
5593      */
5594     disable : function(){
5595        this.disabled = true;
5596     },
5597
5598     /**
5599      * Enables the mask so that it can be displayed
5600      */
5601     enable : function(){
5602         this.disabled = false;
5603     },
5604     
5605     onLoadException : function()
5606     {
5607         Roo.log(arguments);
5608         
5609         if (typeof(arguments[3]) != 'undefined') {
5610             Roo.MessageBox.alert("Error loading",arguments[3]);
5611         } 
5612         /*
5613         try {
5614             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5615                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5616             }   
5617         } catch(e) {
5618             
5619         }
5620         */
5621     
5622         
5623         
5624         this.el.unmask(this.removeMask);
5625     },
5626     // private
5627     onLoad : function()
5628     {
5629         this.el.unmask(this.removeMask);
5630     },
5631
5632     // private
5633     onBeforeLoad : function(){
5634         if(!this.disabled){
5635             this.el.mask(this.msg, this.msgCls);
5636         }
5637     },
5638
5639     // private
5640     destroy : function(){
5641         if(this.store){
5642             this.store.un('beforeload', this.onBeforeLoad, this);
5643             this.store.un('load', this.onLoad, this);
5644             this.store.un('loadexception', this.onLoadException, this);
5645         }else{
5646             var um = this.el.getUpdateManager();
5647             um.un('beforeupdate', this.onBeforeLoad, this);
5648             um.un('update', this.onLoad, this);
5649             um.un('failure', this.onLoad, this);
5650         }
5651     }
5652 };/*
5653  * - LGPL
5654  *
5655  * table
5656  * 
5657  */
5658
5659 /**
5660  * @class Roo.bootstrap.Table
5661  * @extends Roo.bootstrap.Component
5662  * Bootstrap Table class
5663  * @cfg {String} cls table class
5664  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5665  * @cfg {String} bgcolor Specifies the background color for a table
5666  * @cfg {Number} border Specifies whether the table cells should have borders or not
5667  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5668  * @cfg {Number} cellspacing Specifies the space between cells
5669  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5670  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5671  * @cfg {String} sortable Specifies that the table should be sortable
5672  * @cfg {String} summary Specifies a summary of the content of a table
5673  * @cfg {Number} width Specifies the width of a table
5674  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5675  * 
5676  * @cfg {boolean} striped Should the rows be alternative striped
5677  * @cfg {boolean} bordered Add borders to the table
5678  * @cfg {boolean} hover Add hover highlighting
5679  * @cfg {boolean} condensed Format condensed
5680  * @cfg {boolean} responsive Format condensed
5681  * @cfg {Boolean} loadMask (true|false) default false
5682  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5683  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5684  * @cfg {Boolean} rowSelection (true|false) default false
5685  * @cfg {Boolean} cellSelection (true|false) default false
5686  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5687  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5688  
5689  * 
5690  * @constructor
5691  * Create a new Table
5692  * @param {Object} config The config object
5693  */
5694
5695 Roo.bootstrap.Table = function(config){
5696     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5697     
5698   
5699     
5700     // BC...
5701     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5702     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5703     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5704     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5705     
5706     
5707     if (this.sm) {
5708         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5709         this.sm = this.selModel;
5710         this.sm.xmodule = this.xmodule || false;
5711     }
5712     if (this.cm && typeof(this.cm.config) == 'undefined') {
5713         this.colModel = new Roo.grid.ColumnModel(this.cm);
5714         this.cm = this.colModel;
5715         this.cm.xmodule = this.xmodule || false;
5716     }
5717     if (this.store) {
5718         this.store= Roo.factory(this.store, Roo.data);
5719         this.ds = this.store;
5720         this.ds.xmodule = this.xmodule || false;
5721          
5722     }
5723     if (this.footer && this.store) {
5724         this.footer.dataSource = this.ds;
5725         this.footer = Roo.factory(this.footer);
5726     }
5727     
5728     /** @private */
5729     this.addEvents({
5730         /**
5731          * @event cellclick
5732          * Fires when a cell is clicked
5733          * @param {Roo.bootstrap.Table} this
5734          * @param {Roo.Element} el
5735          * @param {Number} rowIndex
5736          * @param {Number} columnIndex
5737          * @param {Roo.EventObject} e
5738          */
5739         "cellclick" : true,
5740         /**
5741          * @event celldblclick
5742          * Fires when a cell is double clicked
5743          * @param {Roo.bootstrap.Table} this
5744          * @param {Roo.Element} el
5745          * @param {Number} rowIndex
5746          * @param {Number} columnIndex
5747          * @param {Roo.EventObject} e
5748          */
5749         "celldblclick" : true,
5750         /**
5751          * @event rowclick
5752          * Fires when a row is clicked
5753          * @param {Roo.bootstrap.Table} this
5754          * @param {Roo.Element} el
5755          * @param {Number} rowIndex
5756          * @param {Roo.EventObject} e
5757          */
5758         "rowclick" : true,
5759         /**
5760          * @event rowdblclick
5761          * Fires when a row is double clicked
5762          * @param {Roo.bootstrap.Table} this
5763          * @param {Roo.Element} el
5764          * @param {Number} rowIndex
5765          * @param {Roo.EventObject} e
5766          */
5767         "rowdblclick" : true,
5768         /**
5769          * @event mouseover
5770          * Fires when a mouseover occur
5771          * @param {Roo.bootstrap.Table} this
5772          * @param {Roo.Element} el
5773          * @param {Number} rowIndex
5774          * @param {Number} columnIndex
5775          * @param {Roo.EventObject} e
5776          */
5777         "mouseover" : true,
5778         /**
5779          * @event mouseout
5780          * Fires when a mouseout occur
5781          * @param {Roo.bootstrap.Table} this
5782          * @param {Roo.Element} el
5783          * @param {Number} rowIndex
5784          * @param {Number} columnIndex
5785          * @param {Roo.EventObject} e
5786          */
5787         "mouseout" : true,
5788         /**
5789          * @event rowclass
5790          * Fires when a row is rendered, so you can change add a style to it.
5791          * @param {Roo.bootstrap.Table} this
5792          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5793          */
5794         'rowclass' : true,
5795           /**
5796          * @event rowsrendered
5797          * Fires when all the  rows have been rendered
5798          * @param {Roo.bootstrap.Table} this
5799          */
5800         'rowsrendered' : true,
5801         /**
5802          * @event contextmenu
5803          * The raw contextmenu event for the entire grid.
5804          * @param {Roo.EventObject} e
5805          */
5806         "contextmenu" : true,
5807         /**
5808          * @event rowcontextmenu
5809          * Fires when a row is right clicked
5810          * @param {Roo.bootstrap.Table} this
5811          * @param {Number} rowIndex
5812          * @param {Roo.EventObject} e
5813          */
5814         "rowcontextmenu" : true,
5815         /**
5816          * @event cellcontextmenu
5817          * Fires when a cell is right clicked
5818          * @param {Roo.bootstrap.Table} this
5819          * @param {Number} rowIndex
5820          * @param {Number} cellIndex
5821          * @param {Roo.EventObject} e
5822          */
5823          "cellcontextmenu" : true,
5824          /**
5825          * @event headercontextmenu
5826          * Fires when a header is right clicked
5827          * @param {Roo.bootstrap.Table} this
5828          * @param {Number} columnIndex
5829          * @param {Roo.EventObject} e
5830          */
5831         "headercontextmenu" : true
5832     });
5833 };
5834
5835 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5836     
5837     cls: false,
5838     align: false,
5839     bgcolor: false,
5840     border: false,
5841     cellpadding: false,
5842     cellspacing: false,
5843     frame: false,
5844     rules: false,
5845     sortable: false,
5846     summary: false,
5847     width: false,
5848     striped : false,
5849     scrollBody : false,
5850     bordered: false,
5851     hover:  false,
5852     condensed : false,
5853     responsive : false,
5854     sm : false,
5855     cm : false,
5856     store : false,
5857     loadMask : false,
5858     footerShow : true,
5859     headerShow : true,
5860   
5861     rowSelection : false,
5862     cellSelection : false,
5863     layout : false,
5864     
5865     // Roo.Element - the tbody
5866     mainBody: false,
5867     // Roo.Element - thead element
5868     mainHead: false,
5869     
5870     container: false, // used by gridpanel...
5871     
5872     getAutoCreate : function()
5873     {
5874         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5875         
5876         cfg = {
5877             tag: 'table',
5878             cls : 'table',
5879             cn : []
5880         };
5881         if (this.scrollBody) {
5882             cfg.cls += ' table-body-fixed';
5883         }    
5884         if (this.striped) {
5885             cfg.cls += ' table-striped';
5886         }
5887         
5888         if (this.hover) {
5889             cfg.cls += ' table-hover';
5890         }
5891         if (this.bordered) {
5892             cfg.cls += ' table-bordered';
5893         }
5894         if (this.condensed) {
5895             cfg.cls += ' table-condensed';
5896         }
5897         if (this.responsive) {
5898             cfg.cls += ' table-responsive';
5899         }
5900         
5901         if (this.cls) {
5902             cfg.cls+=  ' ' +this.cls;
5903         }
5904         
5905         // this lot should be simplifed...
5906         
5907         if (this.align) {
5908             cfg.align=this.align;
5909         }
5910         if (this.bgcolor) {
5911             cfg.bgcolor=this.bgcolor;
5912         }
5913         if (this.border) {
5914             cfg.border=this.border;
5915         }
5916         if (this.cellpadding) {
5917             cfg.cellpadding=this.cellpadding;
5918         }
5919         if (this.cellspacing) {
5920             cfg.cellspacing=this.cellspacing;
5921         }
5922         if (this.frame) {
5923             cfg.frame=this.frame;
5924         }
5925         if (this.rules) {
5926             cfg.rules=this.rules;
5927         }
5928         if (this.sortable) {
5929             cfg.sortable=this.sortable;
5930         }
5931         if (this.summary) {
5932             cfg.summary=this.summary;
5933         }
5934         if (this.width) {
5935             cfg.width=this.width;
5936         }
5937         if (this.layout) {
5938             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5939         }
5940         
5941         if(this.store || this.cm){
5942             if(this.headerShow){
5943                 cfg.cn.push(this.renderHeader());
5944             }
5945             
5946             cfg.cn.push(this.renderBody());
5947             
5948             if(this.footerShow){
5949                 cfg.cn.push(this.renderFooter());
5950             }
5951             // where does this come from?
5952             //cfg.cls+=  ' TableGrid';
5953         }
5954         
5955         return { cn : [ cfg ] };
5956     },
5957     
5958     initEvents : function()
5959     {   
5960         if(!this.store || !this.cm){
5961             return;
5962         }
5963         
5964         //Roo.log('initEvents with ds!!!!');
5965         
5966         this.mainBody = this.el.select('tbody', true).first();
5967         this.mainHead = this.el.select('thead', true).first();
5968         
5969         
5970         
5971         var _this = this;
5972         
5973         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5974             e.on('click', _this.sort, _this);
5975         });
5976         
5977         this.el.on("click", this.onClick, this);
5978         this.el.on("dblclick", this.onDblClick, this);
5979         
5980         // why is this done????? = it breaks dialogs??
5981         //this.parent().el.setStyle('position', 'relative');
5982         
5983         
5984         if (this.footer) {
5985             this.footer.parentId = this.id;
5986             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5987         }
5988         
5989         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5990         
5991         this.store.on('load', this.onLoad, this);
5992         this.store.on('beforeload', this.onBeforeLoad, this);
5993         this.store.on('update', this.onUpdate, this);
5994         this.store.on('add', this.onAdd, this);
5995         
5996         this.el.on("contextmenu", this.onContextMenu, this);
5997         
5998         this.mainBody.on('scroll', this.onBodyScroll, this);
5999         
6000         
6001     },
6002     
6003     onContextMenu : function(e, t)
6004     {
6005         this.processEvent("contextmenu", e);
6006     },
6007     
6008     processEvent : function(name, e)
6009     {
6010         if (name != 'touchstart' ) {
6011             this.fireEvent(name, e);    
6012         }
6013         
6014         var t = e.getTarget();
6015         
6016         var cell = Roo.get(t);
6017         
6018         if(!cell){
6019             return;
6020         }
6021         
6022         if(cell.findParent('tfoot', false, true)){
6023             return;
6024         }
6025         
6026         if(cell.findParent('thead', false, true)){
6027             
6028             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6029                 cell = Roo.get(t).findParent('th', false, true);
6030                 if (!cell) {
6031                     Roo.log("failed to find th in thead?");
6032                     Roo.log(e.getTarget());
6033                     return;
6034                 }
6035             }
6036             
6037             var cellIndex = cell.dom.cellIndex;
6038             
6039             var ename = name == 'touchstart' ? 'click' : name;
6040             this.fireEvent("header" + ename, this, cellIndex, e);
6041             
6042             return;
6043         }
6044         
6045         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6046             cell = Roo.get(t).findParent('td', false, true);
6047             if (!cell) {
6048                 Roo.log("failed to find th in tbody?");
6049                 Roo.log(e.getTarget());
6050                 return;
6051             }
6052         }
6053         
6054         var row = cell.findParent('tr', false, true);
6055         var cellIndex = cell.dom.cellIndex;
6056         var rowIndex = row.dom.rowIndex - 1;
6057         
6058         if(row !== false){
6059             
6060             this.fireEvent("row" + name, this, rowIndex, e);
6061             
6062             if(cell !== false){
6063             
6064                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6065             }
6066         }
6067         
6068     },
6069     
6070     onMouseover : function(e, el)
6071     {
6072         var cell = Roo.get(el);
6073         
6074         if(!cell){
6075             return;
6076         }
6077         
6078         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6079             cell = cell.findParent('td', false, true);
6080         }
6081         
6082         var row = cell.findParent('tr', false, true);
6083         var cellIndex = cell.dom.cellIndex;
6084         var rowIndex = row.dom.rowIndex - 1; // start from 0
6085         
6086         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6087         
6088     },
6089     
6090     onMouseout : function(e, el)
6091     {
6092         var cell = Roo.get(el);
6093         
6094         if(!cell){
6095             return;
6096         }
6097         
6098         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6099             cell = cell.findParent('td', false, true);
6100         }
6101         
6102         var row = cell.findParent('tr', false, true);
6103         var cellIndex = cell.dom.cellIndex;
6104         var rowIndex = row.dom.rowIndex - 1; // start from 0
6105         
6106         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6107         
6108     },
6109     
6110     onClick : function(e, el)
6111     {
6112         var cell = Roo.get(el);
6113         
6114         if(!cell || (!this.cellSelection && !this.rowSelection)){
6115             return;
6116         }
6117         
6118         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6119             cell = cell.findParent('td', false, true);
6120         }
6121         
6122         if(!cell || typeof(cell) == 'undefined'){
6123             return;
6124         }
6125         
6126         var row = cell.findParent('tr', false, true);
6127         
6128         if(!row || typeof(row) == 'undefined'){
6129             return;
6130         }
6131         
6132         var cellIndex = cell.dom.cellIndex;
6133         var rowIndex = this.getRowIndex(row);
6134         
6135         // why??? - should these not be based on SelectionModel?
6136         if(this.cellSelection){
6137             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6138         }
6139         
6140         if(this.rowSelection){
6141             this.fireEvent('rowclick', this, row, rowIndex, e);
6142         }
6143         
6144         
6145     },
6146     
6147     onDblClick : function(e,el)
6148     {
6149         var cell = Roo.get(el);
6150         
6151         if(!cell || (!this.CellSelection && !this.RowSelection)){
6152             return;
6153         }
6154         
6155         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6156             cell = cell.findParent('td', false, true);
6157         }
6158         
6159         if(!cell || typeof(cell) == 'undefined'){
6160             return;
6161         }
6162         
6163         var row = cell.findParent('tr', false, true);
6164         
6165         if(!row || typeof(row) == 'undefined'){
6166             return;
6167         }
6168         
6169         var cellIndex = cell.dom.cellIndex;
6170         var rowIndex = this.getRowIndex(row);
6171         
6172         if(this.CellSelection){
6173             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6174         }
6175         
6176         if(this.RowSelection){
6177             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6178         }
6179     },
6180     
6181     sort : function(e,el)
6182     {
6183         var col = Roo.get(el);
6184         
6185         if(!col.hasClass('sortable')){
6186             return;
6187         }
6188         
6189         var sort = col.attr('sort');
6190         var dir = 'ASC';
6191         
6192         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6193             dir = 'DESC';
6194         }
6195         
6196         this.store.sortInfo = {field : sort, direction : dir};
6197         
6198         if (this.footer) {
6199             Roo.log("calling footer first");
6200             this.footer.onClick('first');
6201         } else {
6202         
6203             this.store.load({ params : { start : 0 } });
6204         }
6205     },
6206     
6207     renderHeader : function()
6208     {
6209         var header = {
6210             tag: 'thead',
6211             cn : []
6212         };
6213         
6214         var cm = this.cm;
6215         this.totalWidth = 0;
6216         
6217         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6218             
6219             var config = cm.config[i];
6220             
6221             var c = {
6222                 tag: 'th',
6223                 style : '',
6224                 html: cm.getColumnHeader(i)
6225             };
6226             
6227             var hh = '';
6228             
6229             if(typeof(config.sortable) != 'undefined' && config.sortable){
6230                 c.cls = 'sortable';
6231                 c.html = '<i class="glyphicon"></i>' + c.html;
6232             }
6233             
6234             if(typeof(config.lgHeader) != 'undefined'){
6235                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6236             }
6237             
6238             if(typeof(config.mdHeader) != 'undefined'){
6239                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6240             }
6241             
6242             if(typeof(config.smHeader) != 'undefined'){
6243                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6244             }
6245             
6246             if(typeof(config.xsHeader) != 'undefined'){
6247                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6248             }
6249             
6250             if(hh.length){
6251                 c.html = hh;
6252             }
6253             
6254             if(typeof(config.tooltip) != 'undefined'){
6255                 c.tooltip = config.tooltip;
6256             }
6257             
6258             if(typeof(config.colspan) != 'undefined'){
6259                 c.colspan = config.colspan;
6260             }
6261             
6262             if(typeof(config.hidden) != 'undefined' && config.hidden){
6263                 c.style += ' display:none;';
6264             }
6265             
6266             if(typeof(config.dataIndex) != 'undefined'){
6267                 c.sort = config.dataIndex;
6268             }
6269             
6270            
6271             
6272             if(typeof(config.align) != 'undefined' && config.align.length){
6273                 c.style += ' text-align:' + config.align + ';';
6274             }
6275             
6276             if(typeof(config.width) != 'undefined'){
6277                 c.style += ' width:' + config.width + 'px;';
6278                 this.totalWidth += config.width;
6279             } else {
6280                 this.totalWidth += 100; // assume minimum of 100 per column?
6281             }
6282             
6283             if(typeof(config.cls) != 'undefined'){
6284                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6285             }
6286             
6287             ['xs','sm','md','lg'].map(function(size){
6288                 
6289                 if(typeof(config[size]) == 'undefined'){
6290                     return;
6291                 }
6292                 
6293                 if (!config[size]) { // 0 = hidden
6294                     c.cls += ' hidden-' + size;
6295                     return;
6296                 }
6297                 
6298                 c.cls += ' col-' + size + '-' + config[size];
6299
6300             });
6301             
6302             header.cn.push(c)
6303         }
6304         
6305         return header;
6306     },
6307     
6308     renderBody : function()
6309     {
6310         var body = {
6311             tag: 'tbody',
6312             cn : [
6313                 {
6314                     tag: 'tr',
6315                     cn : [
6316                         {
6317                             tag : 'td',
6318                             colspan :  this.cm.getColumnCount()
6319                         }
6320                     ]
6321                 }
6322             ]
6323         };
6324         
6325         return body;
6326     },
6327     
6328     renderFooter : function()
6329     {
6330         var footer = {
6331             tag: 'tfoot',
6332             cn : [
6333                 {
6334                     tag: 'tr',
6335                     cn : [
6336                         {
6337                             tag : 'td',
6338                             colspan :  this.cm.getColumnCount()
6339                         }
6340                     ]
6341                 }
6342             ]
6343         };
6344         
6345         return footer;
6346     },
6347     
6348     
6349     
6350     onLoad : function()
6351     {
6352 //        Roo.log('ds onload');
6353         this.clear();
6354         
6355         var _this = this;
6356         var cm = this.cm;
6357         var ds = this.store;
6358         
6359         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6360             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6361             if (_this.store.sortInfo) {
6362                     
6363                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6364                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6365                 }
6366                 
6367                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6368                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6369                 }
6370             }
6371         });
6372         
6373         var tbody =  this.mainBody;
6374               
6375         if(ds.getCount() > 0){
6376             ds.data.each(function(d,rowIndex){
6377                 var row =  this.renderRow(cm, ds, rowIndex);
6378                 
6379                 tbody.createChild(row);
6380                 
6381                 var _this = this;
6382                 
6383                 if(row.cellObjects.length){
6384                     Roo.each(row.cellObjects, function(r){
6385                         _this.renderCellObject(r);
6386                     })
6387                 }
6388                 
6389             }, this);
6390         }
6391         
6392         Roo.each(this.el.select('tbody td', true).elements, function(e){
6393             e.on('mouseover', _this.onMouseover, _this);
6394         });
6395         
6396         Roo.each(this.el.select('tbody td', true).elements, function(e){
6397             e.on('mouseout', _this.onMouseout, _this);
6398         });
6399         this.fireEvent('rowsrendered', this);
6400         //if(this.loadMask){
6401         //    this.maskEl.hide();
6402         //}
6403         
6404         this.autoSize();
6405     },
6406     
6407     
6408     onUpdate : function(ds,record)
6409     {
6410         this.refreshRow(record);
6411     },
6412     
6413     onRemove : function(ds, record, index, isUpdate){
6414         if(isUpdate !== true){
6415             this.fireEvent("beforerowremoved", this, index, record);
6416         }
6417         var bt = this.mainBody.dom;
6418         
6419         var rows = this.el.select('tbody > tr', true).elements;
6420         
6421         if(typeof(rows[index]) != 'undefined'){
6422             bt.removeChild(rows[index].dom);
6423         }
6424         
6425 //        if(bt.rows[index]){
6426 //            bt.removeChild(bt.rows[index]);
6427 //        }
6428         
6429         if(isUpdate !== true){
6430             //this.stripeRows(index);
6431             //this.syncRowHeights(index, index);
6432             //this.layout();
6433             this.fireEvent("rowremoved", this, index, record);
6434         }
6435     },
6436     
6437     onAdd : function(ds, records, rowIndex)
6438     {
6439         //Roo.log('on Add called');
6440         // - note this does not handle multiple adding very well..
6441         var bt = this.mainBody.dom;
6442         for (var i =0 ; i < records.length;i++) {
6443             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6444             //Roo.log(records[i]);
6445             //Roo.log(this.store.getAt(rowIndex+i));
6446             this.insertRow(this.store, rowIndex + i, false);
6447             return;
6448         }
6449         
6450     },
6451     
6452     
6453     refreshRow : function(record){
6454         var ds = this.store, index;
6455         if(typeof record == 'number'){
6456             index = record;
6457             record = ds.getAt(index);
6458         }else{
6459             index = ds.indexOf(record);
6460         }
6461         this.insertRow(ds, index, true);
6462         this.onRemove(ds, record, index+1, true);
6463         //this.syncRowHeights(index, index);
6464         //this.layout();
6465         this.fireEvent("rowupdated", this, index, record);
6466     },
6467     
6468     insertRow : function(dm, rowIndex, isUpdate){
6469         
6470         if(!isUpdate){
6471             this.fireEvent("beforerowsinserted", this, rowIndex);
6472         }
6473             //var s = this.getScrollState();
6474         var row = this.renderRow(this.cm, this.store, rowIndex);
6475         // insert before rowIndex..
6476         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6477         
6478         var _this = this;
6479                 
6480         if(row.cellObjects.length){
6481             Roo.each(row.cellObjects, function(r){
6482                 _this.renderCellObject(r);
6483             })
6484         }
6485             
6486         if(!isUpdate){
6487             this.fireEvent("rowsinserted", this, rowIndex);
6488             //this.syncRowHeights(firstRow, lastRow);
6489             //this.stripeRows(firstRow);
6490             //this.layout();
6491         }
6492         
6493     },
6494     
6495     
6496     getRowDom : function(rowIndex)
6497     {
6498         var rows = this.el.select('tbody > tr', true).elements;
6499         
6500         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6501         
6502     },
6503     // returns the object tree for a tr..
6504   
6505     
6506     renderRow : function(cm, ds, rowIndex) 
6507     {
6508         
6509         var d = ds.getAt(rowIndex);
6510         
6511         var row = {
6512             tag : 'tr',
6513             cn : []
6514         };
6515             
6516         var cellObjects = [];
6517         
6518         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6519             var config = cm.config[i];
6520             
6521             var renderer = cm.getRenderer(i);
6522             var value = '';
6523             var id = false;
6524             
6525             if(typeof(renderer) !== 'undefined'){
6526                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6527             }
6528             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6529             // and are rendered into the cells after the row is rendered - using the id for the element.
6530             
6531             if(typeof(value) === 'object'){
6532                 id = Roo.id();
6533                 cellObjects.push({
6534                     container : id,
6535                     cfg : value 
6536                 })
6537             }
6538             
6539             var rowcfg = {
6540                 record: d,
6541                 rowIndex : rowIndex,
6542                 colIndex : i,
6543                 rowClass : ''
6544             };
6545
6546             this.fireEvent('rowclass', this, rowcfg);
6547             
6548             var td = {
6549                 tag: 'td',
6550                 cls : rowcfg.rowClass,
6551                 style: '',
6552                 html: (typeof(value) === 'object') ? '' : value
6553             };
6554             
6555             if (id) {
6556                 td.id = id;
6557             }
6558             
6559             if(typeof(config.colspan) != 'undefined'){
6560                 td.colspan = config.colspan;
6561             }
6562             
6563             if(typeof(config.hidden) != 'undefined' && config.hidden){
6564                 td.style += ' display:none;';
6565             }
6566             
6567             if(typeof(config.align) != 'undefined' && config.align.length){
6568                 td.style += ' text-align:' + config.align + ';';
6569             }
6570             
6571             if(typeof(config.width) != 'undefined'){
6572                 td.style += ' width:' +  config.width + 'px;';
6573             }
6574             
6575             if(typeof(config.cursor) != 'undefined'){
6576                 td.style += ' cursor:' +  config.cursor + ';';
6577             }
6578             
6579             if(typeof(config.cls) != 'undefined'){
6580                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6581             }
6582             
6583             ['xs','sm','md','lg'].map(function(size){
6584                 
6585                 if(typeof(config[size]) == 'undefined'){
6586                     return;
6587                 }
6588                 
6589                 if (!config[size]) { // 0 = hidden
6590                     td.cls += ' hidden-' + size;
6591                     return;
6592                 }
6593                 
6594                 td.cls += ' col-' + size + '-' + config[size];
6595
6596             });
6597              
6598             row.cn.push(td);
6599            
6600         }
6601         
6602         row.cellObjects = cellObjects;
6603         
6604         return row;
6605           
6606     },
6607     
6608     
6609     
6610     onBeforeLoad : function()
6611     {
6612         //Roo.log('ds onBeforeLoad');
6613         
6614         //this.clear();
6615         
6616         //if(this.loadMask){
6617         //    this.maskEl.show();
6618         //}
6619     },
6620      /**
6621      * Remove all rows
6622      */
6623     clear : function()
6624     {
6625         this.el.select('tbody', true).first().dom.innerHTML = '';
6626     },
6627     /**
6628      * Show or hide a row.
6629      * @param {Number} rowIndex to show or hide
6630      * @param {Boolean} state hide
6631      */
6632     setRowVisibility : function(rowIndex, state)
6633     {
6634         var bt = this.mainBody.dom;
6635         
6636         var rows = this.el.select('tbody > tr', true).elements;
6637         
6638         if(typeof(rows[rowIndex]) == 'undefined'){
6639             return;
6640         }
6641         rows[rowIndex].dom.style.display = state ? '' : 'none';
6642     },
6643     
6644     
6645     getSelectionModel : function(){
6646         if(!this.selModel){
6647             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6648         }
6649         return this.selModel;
6650     },
6651     /*
6652      * Render the Roo.bootstrap object from renderder
6653      */
6654     renderCellObject : function(r)
6655     {
6656         var _this = this;
6657         
6658         var t = r.cfg.render(r.container);
6659         
6660         if(r.cfg.cn){
6661             Roo.each(r.cfg.cn, function(c){
6662                 var child = {
6663                     container: t.getChildContainer(),
6664                     cfg: c
6665                 };
6666                 _this.renderCellObject(child);
6667             })
6668         }
6669     },
6670     
6671     getRowIndex : function(row)
6672     {
6673         var rowIndex = -1;
6674         
6675         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6676             if(el != row){
6677                 return;
6678             }
6679             
6680             rowIndex = index;
6681         });
6682         
6683         return rowIndex;
6684     },
6685      /**
6686      * Returns the grid's underlying element = used by panel.Grid
6687      * @return {Element} The element
6688      */
6689     getGridEl : function(){
6690         return this.el;
6691     },
6692      /**
6693      * Forces a resize - used by panel.Grid
6694      * @return {Element} The element
6695      */
6696     autoSize : function(){
6697         //var ctr = Roo.get(this.container.dom.parentElement);
6698         var ctr = Roo.get(this.el.dom);
6699         
6700         var thd = this.getGridEl().select('thead',true).first();
6701         var tbd = this.getGridEl().select('tbody', true).first();
6702         
6703         
6704         var cw = ctr.getWidth();
6705         
6706         if (tbd) {
6707             
6708             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6709             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6710             cw -= barsize;
6711         }
6712         cw = Math.max(cw, this.totalWidth);
6713         this.getGridEl().select('tr',true).setWidth(cw);
6714         
6715         return; // we doe not have a view in this design..
6716         
6717     },
6718     onBodyScroll: function()
6719     {
6720         
6721         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6722         this.mainHead.setStyle({
6723                     'position' : 'relative',
6724                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6725         });
6726         
6727         
6728     }
6729 });
6730
6731  
6732
6733  /*
6734  * - LGPL
6735  *
6736  * table cell
6737  * 
6738  */
6739
6740 /**
6741  * @class Roo.bootstrap.TableCell
6742  * @extends Roo.bootstrap.Component
6743  * Bootstrap TableCell class
6744  * @cfg {String} html cell contain text
6745  * @cfg {String} cls cell class
6746  * @cfg {String} tag cell tag (td|th) default td
6747  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6748  * @cfg {String} align Aligns the content in a cell
6749  * @cfg {String} axis Categorizes cells
6750  * @cfg {String} bgcolor Specifies the background color of a cell
6751  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6752  * @cfg {Number} colspan Specifies the number of columns a cell should span
6753  * @cfg {String} headers Specifies one or more header cells a cell is related to
6754  * @cfg {Number} height Sets the height of a cell
6755  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6756  * @cfg {Number} rowspan Sets the number of rows a cell should span
6757  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6758  * @cfg {String} valign Vertical aligns the content in a cell
6759  * @cfg {Number} width Specifies the width of a cell
6760  * 
6761  * @constructor
6762  * Create a new TableCell
6763  * @param {Object} config The config object
6764  */
6765
6766 Roo.bootstrap.TableCell = function(config){
6767     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6768 };
6769
6770 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6771     
6772     html: false,
6773     cls: false,
6774     tag: false,
6775     abbr: false,
6776     align: false,
6777     axis: false,
6778     bgcolor: false,
6779     charoff: false,
6780     colspan: false,
6781     headers: false,
6782     height: false,
6783     nowrap: false,
6784     rowspan: false,
6785     scope: false,
6786     valign: false,
6787     width: false,
6788     
6789     
6790     getAutoCreate : function(){
6791         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6792         
6793         cfg = {
6794             tag: 'td'
6795         };
6796         
6797         if(this.tag){
6798             cfg.tag = this.tag;
6799         }
6800         
6801         if (this.html) {
6802             cfg.html=this.html
6803         }
6804         if (this.cls) {
6805             cfg.cls=this.cls
6806         }
6807         if (this.abbr) {
6808             cfg.abbr=this.abbr
6809         }
6810         if (this.align) {
6811             cfg.align=this.align
6812         }
6813         if (this.axis) {
6814             cfg.axis=this.axis
6815         }
6816         if (this.bgcolor) {
6817             cfg.bgcolor=this.bgcolor
6818         }
6819         if (this.charoff) {
6820             cfg.charoff=this.charoff
6821         }
6822         if (this.colspan) {
6823             cfg.colspan=this.colspan
6824         }
6825         if (this.headers) {
6826             cfg.headers=this.headers
6827         }
6828         if (this.height) {
6829             cfg.height=this.height
6830         }
6831         if (this.nowrap) {
6832             cfg.nowrap=this.nowrap
6833         }
6834         if (this.rowspan) {
6835             cfg.rowspan=this.rowspan
6836         }
6837         if (this.scope) {
6838             cfg.scope=this.scope
6839         }
6840         if (this.valign) {
6841             cfg.valign=this.valign
6842         }
6843         if (this.width) {
6844             cfg.width=this.width
6845         }
6846         
6847         
6848         return cfg;
6849     }
6850    
6851 });
6852
6853  
6854
6855  /*
6856  * - LGPL
6857  *
6858  * table row
6859  * 
6860  */
6861
6862 /**
6863  * @class Roo.bootstrap.TableRow
6864  * @extends Roo.bootstrap.Component
6865  * Bootstrap TableRow class
6866  * @cfg {String} cls row class
6867  * @cfg {String} align Aligns the content in a table row
6868  * @cfg {String} bgcolor Specifies a background color for a table row
6869  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6870  * @cfg {String} valign Vertical aligns the content in a table row
6871  * 
6872  * @constructor
6873  * Create a new TableRow
6874  * @param {Object} config The config object
6875  */
6876
6877 Roo.bootstrap.TableRow = function(config){
6878     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6879 };
6880
6881 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6882     
6883     cls: false,
6884     align: false,
6885     bgcolor: false,
6886     charoff: false,
6887     valign: false,
6888     
6889     getAutoCreate : function(){
6890         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6891         
6892         cfg = {
6893             tag: 'tr'
6894         };
6895             
6896         if(this.cls){
6897             cfg.cls = this.cls;
6898         }
6899         if(this.align){
6900             cfg.align = this.align;
6901         }
6902         if(this.bgcolor){
6903             cfg.bgcolor = this.bgcolor;
6904         }
6905         if(this.charoff){
6906             cfg.charoff = this.charoff;
6907         }
6908         if(this.valign){
6909             cfg.valign = this.valign;
6910         }
6911         
6912         return cfg;
6913     }
6914    
6915 });
6916
6917  
6918
6919  /*
6920  * - LGPL
6921  *
6922  * table body
6923  * 
6924  */
6925
6926 /**
6927  * @class Roo.bootstrap.TableBody
6928  * @extends Roo.bootstrap.Component
6929  * Bootstrap TableBody class
6930  * @cfg {String} cls element class
6931  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6932  * @cfg {String} align Aligns the content inside the element
6933  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6934  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6935  * 
6936  * @constructor
6937  * Create a new TableBody
6938  * @param {Object} config The config object
6939  */
6940
6941 Roo.bootstrap.TableBody = function(config){
6942     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6943 };
6944
6945 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6946     
6947     cls: false,
6948     tag: false,
6949     align: false,
6950     charoff: false,
6951     valign: false,
6952     
6953     getAutoCreate : function(){
6954         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6955         
6956         cfg = {
6957             tag: 'tbody'
6958         };
6959             
6960         if (this.cls) {
6961             cfg.cls=this.cls
6962         }
6963         if(this.tag){
6964             cfg.tag = this.tag;
6965         }
6966         
6967         if(this.align){
6968             cfg.align = this.align;
6969         }
6970         if(this.charoff){
6971             cfg.charoff = this.charoff;
6972         }
6973         if(this.valign){
6974             cfg.valign = this.valign;
6975         }
6976         
6977         return cfg;
6978     }
6979     
6980     
6981 //    initEvents : function()
6982 //    {
6983 //        
6984 //        if(!this.store){
6985 //            return;
6986 //        }
6987 //        
6988 //        this.store = Roo.factory(this.store, Roo.data);
6989 //        this.store.on('load', this.onLoad, this);
6990 //        
6991 //        this.store.load();
6992 //        
6993 //    },
6994 //    
6995 //    onLoad: function () 
6996 //    {   
6997 //        this.fireEvent('load', this);
6998 //    }
6999 //    
7000 //   
7001 });
7002
7003  
7004
7005  /*
7006  * Based on:
7007  * Ext JS Library 1.1.1
7008  * Copyright(c) 2006-2007, Ext JS, LLC.
7009  *
7010  * Originally Released Under LGPL - original licence link has changed is not relivant.
7011  *
7012  * Fork - LGPL
7013  * <script type="text/javascript">
7014  */
7015
7016 // as we use this in bootstrap.
7017 Roo.namespace('Roo.form');
7018  /**
7019  * @class Roo.form.Action
7020  * Internal Class used to handle form actions
7021  * @constructor
7022  * @param {Roo.form.BasicForm} el The form element or its id
7023  * @param {Object} config Configuration options
7024  */
7025
7026  
7027  
7028 // define the action interface
7029 Roo.form.Action = function(form, options){
7030     this.form = form;
7031     this.options = options || {};
7032 };
7033 /**
7034  * Client Validation Failed
7035  * @const 
7036  */
7037 Roo.form.Action.CLIENT_INVALID = 'client';
7038 /**
7039  * Server Validation Failed
7040  * @const 
7041  */
7042 Roo.form.Action.SERVER_INVALID = 'server';
7043  /**
7044  * Connect to Server Failed
7045  * @const 
7046  */
7047 Roo.form.Action.CONNECT_FAILURE = 'connect';
7048 /**
7049  * Reading Data from Server Failed
7050  * @const 
7051  */
7052 Roo.form.Action.LOAD_FAILURE = 'load';
7053
7054 Roo.form.Action.prototype = {
7055     type : 'default',
7056     failureType : undefined,
7057     response : undefined,
7058     result : undefined,
7059
7060     // interface method
7061     run : function(options){
7062
7063     },
7064
7065     // interface method
7066     success : function(response){
7067
7068     },
7069
7070     // interface method
7071     handleResponse : function(response){
7072
7073     },
7074
7075     // default connection failure
7076     failure : function(response){
7077         
7078         this.response = response;
7079         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7080         this.form.afterAction(this, false);
7081     },
7082
7083     processResponse : function(response){
7084         this.response = response;
7085         if(!response.responseText){
7086             return true;
7087         }
7088         this.result = this.handleResponse(response);
7089         return this.result;
7090     },
7091
7092     // utility functions used internally
7093     getUrl : function(appendParams){
7094         var url = this.options.url || this.form.url || this.form.el.dom.action;
7095         if(appendParams){
7096             var p = this.getParams();
7097             if(p){
7098                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7099             }
7100         }
7101         return url;
7102     },
7103
7104     getMethod : function(){
7105         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7106     },
7107
7108     getParams : function(){
7109         var bp = this.form.baseParams;
7110         var p = this.options.params;
7111         if(p){
7112             if(typeof p == "object"){
7113                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7114             }else if(typeof p == 'string' && bp){
7115                 p += '&' + Roo.urlEncode(bp);
7116             }
7117         }else if(bp){
7118             p = Roo.urlEncode(bp);
7119         }
7120         return p;
7121     },
7122
7123     createCallback : function(){
7124         return {
7125             success: this.success,
7126             failure: this.failure,
7127             scope: this,
7128             timeout: (this.form.timeout*1000),
7129             upload: this.form.fileUpload ? this.success : undefined
7130         };
7131     }
7132 };
7133
7134 Roo.form.Action.Submit = function(form, options){
7135     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7136 };
7137
7138 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7139     type : 'submit',
7140
7141     haveProgress : false,
7142     uploadComplete : false,
7143     
7144     // uploadProgress indicator.
7145     uploadProgress : function()
7146     {
7147         if (!this.form.progressUrl) {
7148             return;
7149         }
7150         
7151         if (!this.haveProgress) {
7152             Roo.MessageBox.progress("Uploading", "Uploading");
7153         }
7154         if (this.uploadComplete) {
7155            Roo.MessageBox.hide();
7156            return;
7157         }
7158         
7159         this.haveProgress = true;
7160    
7161         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7162         
7163         var c = new Roo.data.Connection();
7164         c.request({
7165             url : this.form.progressUrl,
7166             params: {
7167                 id : uid
7168             },
7169             method: 'GET',
7170             success : function(req){
7171                //console.log(data);
7172                 var rdata = false;
7173                 var edata;
7174                 try  {
7175                    rdata = Roo.decode(req.responseText)
7176                 } catch (e) {
7177                     Roo.log("Invalid data from server..");
7178                     Roo.log(edata);
7179                     return;
7180                 }
7181                 if (!rdata || !rdata.success) {
7182                     Roo.log(rdata);
7183                     Roo.MessageBox.alert(Roo.encode(rdata));
7184                     return;
7185                 }
7186                 var data = rdata.data;
7187                 
7188                 if (this.uploadComplete) {
7189                    Roo.MessageBox.hide();
7190                    return;
7191                 }
7192                    
7193                 if (data){
7194                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7195                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7196                     );
7197                 }
7198                 this.uploadProgress.defer(2000,this);
7199             },
7200        
7201             failure: function(data) {
7202                 Roo.log('progress url failed ');
7203                 Roo.log(data);
7204             },
7205             scope : this
7206         });
7207            
7208     },
7209     
7210     
7211     run : function()
7212     {
7213         // run get Values on the form, so it syncs any secondary forms.
7214         this.form.getValues();
7215         
7216         var o = this.options;
7217         var method = this.getMethod();
7218         var isPost = method == 'POST';
7219         if(o.clientValidation === false || this.form.isValid()){
7220             
7221             if (this.form.progressUrl) {
7222                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7223                     (new Date() * 1) + '' + Math.random());
7224                     
7225             } 
7226             
7227             
7228             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7229                 form:this.form.el.dom,
7230                 url:this.getUrl(!isPost),
7231                 method: method,
7232                 params:isPost ? this.getParams() : null,
7233                 isUpload: this.form.fileUpload
7234             }));
7235             
7236             this.uploadProgress();
7237
7238         }else if (o.clientValidation !== false){ // client validation failed
7239             this.failureType = Roo.form.Action.CLIENT_INVALID;
7240             this.form.afterAction(this, false);
7241         }
7242     },
7243
7244     success : function(response)
7245     {
7246         this.uploadComplete= true;
7247         if (this.haveProgress) {
7248             Roo.MessageBox.hide();
7249         }
7250         
7251         
7252         var result = this.processResponse(response);
7253         if(result === true || result.success){
7254             this.form.afterAction(this, true);
7255             return;
7256         }
7257         if(result.errors){
7258             this.form.markInvalid(result.errors);
7259             this.failureType = Roo.form.Action.SERVER_INVALID;
7260         }
7261         this.form.afterAction(this, false);
7262     },
7263     failure : function(response)
7264     {
7265         this.uploadComplete= true;
7266         if (this.haveProgress) {
7267             Roo.MessageBox.hide();
7268         }
7269         
7270         this.response = response;
7271         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7272         this.form.afterAction(this, false);
7273     },
7274     
7275     handleResponse : function(response){
7276         if(this.form.errorReader){
7277             var rs = this.form.errorReader.read(response);
7278             var errors = [];
7279             if(rs.records){
7280                 for(var i = 0, len = rs.records.length; i < len; i++) {
7281                     var r = rs.records[i];
7282                     errors[i] = r.data;
7283                 }
7284             }
7285             if(errors.length < 1){
7286                 errors = null;
7287             }
7288             return {
7289                 success : rs.success,
7290                 errors : errors
7291             };
7292         }
7293         var ret = false;
7294         try {
7295             ret = Roo.decode(response.responseText);
7296         } catch (e) {
7297             ret = {
7298                 success: false,
7299                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7300                 errors : []
7301             };
7302         }
7303         return ret;
7304         
7305     }
7306 });
7307
7308
7309 Roo.form.Action.Load = function(form, options){
7310     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7311     this.reader = this.form.reader;
7312 };
7313
7314 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7315     type : 'load',
7316
7317     run : function(){
7318         
7319         Roo.Ajax.request(Roo.apply(
7320                 this.createCallback(), {
7321                     method:this.getMethod(),
7322                     url:this.getUrl(false),
7323                     params:this.getParams()
7324         }));
7325     },
7326
7327     success : function(response){
7328         
7329         var result = this.processResponse(response);
7330         if(result === true || !result.success || !result.data){
7331             this.failureType = Roo.form.Action.LOAD_FAILURE;
7332             this.form.afterAction(this, false);
7333             return;
7334         }
7335         this.form.clearInvalid();
7336         this.form.setValues(result.data);
7337         this.form.afterAction(this, true);
7338     },
7339
7340     handleResponse : function(response){
7341         if(this.form.reader){
7342             var rs = this.form.reader.read(response);
7343             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7344             return {
7345                 success : rs.success,
7346                 data : data
7347             };
7348         }
7349         return Roo.decode(response.responseText);
7350     }
7351 });
7352
7353 Roo.form.Action.ACTION_TYPES = {
7354     'load' : Roo.form.Action.Load,
7355     'submit' : Roo.form.Action.Submit
7356 };/*
7357  * - LGPL
7358  *
7359  * form
7360  * 
7361  */
7362
7363 /**
7364  * @class Roo.bootstrap.Form
7365  * @extends Roo.bootstrap.Component
7366  * Bootstrap Form class
7367  * @cfg {String} method  GET | POST (default POST)
7368  * @cfg {String} labelAlign top | left (default top)
7369  * @cfg {String} align left  | right - for navbars
7370  * @cfg {Boolean} loadMask load mask when submit (default true)
7371
7372  * 
7373  * @constructor
7374  * Create a new Form
7375  * @param {Object} config The config object
7376  */
7377
7378
7379 Roo.bootstrap.Form = function(config){
7380     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7381     this.addEvents({
7382         /**
7383          * @event clientvalidation
7384          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7385          * @param {Form} this
7386          * @param {Boolean} valid true if the form has passed client-side validation
7387          */
7388         clientvalidation: true,
7389         /**
7390          * @event beforeaction
7391          * Fires before any action is performed. Return false to cancel the action.
7392          * @param {Form} this
7393          * @param {Action} action The action to be performed
7394          */
7395         beforeaction: true,
7396         /**
7397          * @event actionfailed
7398          * Fires when an action fails.
7399          * @param {Form} this
7400          * @param {Action} action The action that failed
7401          */
7402         actionfailed : true,
7403         /**
7404          * @event actioncomplete
7405          * Fires when an action is completed.
7406          * @param {Form} this
7407          * @param {Action} action The action that completed
7408          */
7409         actioncomplete : true
7410     });
7411     
7412 };
7413
7414 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7415       
7416      /**
7417      * @cfg {String} method
7418      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7419      */
7420     method : 'POST',
7421     /**
7422      * @cfg {String} url
7423      * The URL to use for form actions if one isn't supplied in the action options.
7424      */
7425     /**
7426      * @cfg {Boolean} fileUpload
7427      * Set to true if this form is a file upload.
7428      */
7429      
7430     /**
7431      * @cfg {Object} baseParams
7432      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7433      */
7434       
7435     /**
7436      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7437      */
7438     timeout: 30,
7439     /**
7440      * @cfg {Sting} align (left|right) for navbar forms
7441      */
7442     align : 'left',
7443
7444     // private
7445     activeAction : null,
7446  
7447     /**
7448      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7449      * element by passing it or its id or mask the form itself by passing in true.
7450      * @type Mixed
7451      */
7452     waitMsgTarget : false,
7453     
7454     loadMask : true,
7455     
7456     getAutoCreate : function(){
7457         
7458         var cfg = {
7459             tag: 'form',
7460             method : this.method || 'POST',
7461             id : this.id || Roo.id(),
7462             cls : ''
7463         };
7464         if (this.parent().xtype.match(/^Nav/)) {
7465             cfg.cls = 'navbar-form navbar-' + this.align;
7466             
7467         }
7468         
7469         if (this.labelAlign == 'left' ) {
7470             cfg.cls += ' form-horizontal';
7471         }
7472         
7473         
7474         return cfg;
7475     },
7476     initEvents : function()
7477     {
7478         this.el.on('submit', this.onSubmit, this);
7479         // this was added as random key presses on the form where triggering form submit.
7480         this.el.on('keypress', function(e) {
7481             if (e.getCharCode() != 13) {
7482                 return true;
7483             }
7484             // we might need to allow it for textareas.. and some other items.
7485             // check e.getTarget().
7486             
7487             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7488                 return true;
7489             }
7490         
7491             Roo.log("keypress blocked");
7492             
7493             e.preventDefault();
7494             return false;
7495         });
7496         
7497     },
7498     // private
7499     onSubmit : function(e){
7500         e.stopEvent();
7501     },
7502     
7503      /**
7504      * Returns true if client-side validation on the form is successful.
7505      * @return Boolean
7506      */
7507     isValid : function(){
7508         var items = this.getItems();
7509         var valid = true;
7510         items.each(function(f){
7511            if(!f.validate()){
7512                valid = false;
7513                
7514            }
7515         });
7516         return valid;
7517     },
7518     /**
7519      * Returns true if any fields in this form have changed since their original load.
7520      * @return Boolean
7521      */
7522     isDirty : function(){
7523         var dirty = false;
7524         var items = this.getItems();
7525         items.each(function(f){
7526            if(f.isDirty()){
7527                dirty = true;
7528                return false;
7529            }
7530            return true;
7531         });
7532         return dirty;
7533     },
7534      /**
7535      * Performs a predefined action (submit or load) or custom actions you define on this form.
7536      * @param {String} actionName The name of the action type
7537      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7538      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7539      * accept other config options):
7540      * <pre>
7541 Property          Type             Description
7542 ----------------  ---------------  ----------------------------------------------------------------------------------
7543 url               String           The url for the action (defaults to the form's url)
7544 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7545 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7546 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7547                                    validate the form on the client (defaults to false)
7548      * </pre>
7549      * @return {BasicForm} this
7550      */
7551     doAction : function(action, options){
7552         if(typeof action == 'string'){
7553             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7554         }
7555         if(this.fireEvent('beforeaction', this, action) !== false){
7556             this.beforeAction(action);
7557             action.run.defer(100, action);
7558         }
7559         return this;
7560     },
7561     
7562     // private
7563     beforeAction : function(action){
7564         var o = action.options;
7565         
7566         if(this.loadMask){
7567             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7568         }
7569         // not really supported yet.. ??
7570         
7571         //if(this.waitMsgTarget === true){
7572         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7573         //}else if(this.waitMsgTarget){
7574         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7575         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7576         //}else {
7577         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7578        // }
7579          
7580     },
7581
7582     // private
7583     afterAction : function(action, success){
7584         this.activeAction = null;
7585         var o = action.options;
7586         
7587         //if(this.waitMsgTarget === true){
7588             this.el.unmask();
7589         //}else if(this.waitMsgTarget){
7590         //    this.waitMsgTarget.unmask();
7591         //}else{
7592         //    Roo.MessageBox.updateProgress(1);
7593         //    Roo.MessageBox.hide();
7594        // }
7595         // 
7596         if(success){
7597             if(o.reset){
7598                 this.reset();
7599             }
7600             Roo.callback(o.success, o.scope, [this, action]);
7601             this.fireEvent('actioncomplete', this, action);
7602             
7603         }else{
7604             
7605             // failure condition..
7606             // we have a scenario where updates need confirming.
7607             // eg. if a locking scenario exists..
7608             // we look for { errors : { needs_confirm : true }} in the response.
7609             if (
7610                 (typeof(action.result) != 'undefined')  &&
7611                 (typeof(action.result.errors) != 'undefined')  &&
7612                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7613            ){
7614                 var _t = this;
7615                 Roo.log("not supported yet");
7616                  /*
7617                 
7618                 Roo.MessageBox.confirm(
7619                     "Change requires confirmation",
7620                     action.result.errorMsg,
7621                     function(r) {
7622                         if (r != 'yes') {
7623                             return;
7624                         }
7625                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7626                     }
7627                     
7628                 );
7629                 */
7630                 
7631                 
7632                 return;
7633             }
7634             
7635             Roo.callback(o.failure, o.scope, [this, action]);
7636             // show an error message if no failed handler is set..
7637             if (!this.hasListener('actionfailed')) {
7638                 Roo.log("need to add dialog support");
7639                 /*
7640                 Roo.MessageBox.alert("Error",
7641                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7642                         action.result.errorMsg :
7643                         "Saving Failed, please check your entries or try again"
7644                 );
7645                 */
7646             }
7647             
7648             this.fireEvent('actionfailed', this, action);
7649         }
7650         
7651     },
7652     /**
7653      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7654      * @param {String} id The value to search for
7655      * @return Field
7656      */
7657     findField : function(id){
7658         var items = this.getItems();
7659         var field = items.get(id);
7660         if(!field){
7661              items.each(function(f){
7662                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7663                     field = f;
7664                     return false;
7665                 }
7666                 return true;
7667             });
7668         }
7669         return field || null;
7670     },
7671      /**
7672      * Mark fields in this form invalid in bulk.
7673      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7674      * @return {BasicForm} this
7675      */
7676     markInvalid : function(errors){
7677         if(errors instanceof Array){
7678             for(var i = 0, len = errors.length; i < len; i++){
7679                 var fieldError = errors[i];
7680                 var f = this.findField(fieldError.id);
7681                 if(f){
7682                     f.markInvalid(fieldError.msg);
7683                 }
7684             }
7685         }else{
7686             var field, id;
7687             for(id in errors){
7688                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7689                     field.markInvalid(errors[id]);
7690                 }
7691             }
7692         }
7693         //Roo.each(this.childForms || [], function (f) {
7694         //    f.markInvalid(errors);
7695         //});
7696         
7697         return this;
7698     },
7699
7700     /**
7701      * Set values for fields in this form in bulk.
7702      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7703      * @return {BasicForm} this
7704      */
7705     setValues : function(values){
7706         if(values instanceof Array){ // array of objects
7707             for(var i = 0, len = values.length; i < len; i++){
7708                 var v = values[i];
7709                 var f = this.findField(v.id);
7710                 if(f){
7711                     f.setValue(v.value);
7712                     if(this.trackResetOnLoad){
7713                         f.originalValue = f.getValue();
7714                     }
7715                 }
7716             }
7717         }else{ // object hash
7718             var field, id;
7719             for(id in values){
7720                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7721                     
7722                     if (field.setFromData && 
7723                         field.valueField && 
7724                         field.displayField &&
7725                         // combos' with local stores can 
7726                         // be queried via setValue()
7727                         // to set their value..
7728                         (field.store && !field.store.isLocal)
7729                         ) {
7730                         // it's a combo
7731                         var sd = { };
7732                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7733                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7734                         field.setFromData(sd);
7735                         
7736                     } else {
7737                         field.setValue(values[id]);
7738                     }
7739                     
7740                     
7741                     if(this.trackResetOnLoad){
7742                         field.originalValue = field.getValue();
7743                     }
7744                 }
7745             }
7746         }
7747          
7748         //Roo.each(this.childForms || [], function (f) {
7749         //    f.setValues(values);
7750         //});
7751                 
7752         return this;
7753     },
7754
7755     /**
7756      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7757      * they are returned as an array.
7758      * @param {Boolean} asString
7759      * @return {Object}
7760      */
7761     getValues : function(asString){
7762         //if (this.childForms) {
7763             // copy values from the child forms
7764         //    Roo.each(this.childForms, function (f) {
7765         //        this.setValues(f.getValues());
7766         //    }, this);
7767         //}
7768         
7769         
7770         
7771         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7772         if(asString === true){
7773             return fs;
7774         }
7775         return Roo.urlDecode(fs);
7776     },
7777     
7778     /**
7779      * Returns the fields in this form as an object with key/value pairs. 
7780      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7781      * @return {Object}
7782      */
7783     getFieldValues : function(with_hidden)
7784     {
7785         var items = this.getItems();
7786         var ret = {};
7787         items.each(function(f){
7788             if (!f.getName()) {
7789                 return;
7790             }
7791             var v = f.getValue();
7792             if (f.inputType =='radio') {
7793                 if (typeof(ret[f.getName()]) == 'undefined') {
7794                     ret[f.getName()] = ''; // empty..
7795                 }
7796                 
7797                 if (!f.el.dom.checked) {
7798                     return;
7799                     
7800                 }
7801                 v = f.el.dom.value;
7802                 
7803             }
7804             
7805             // not sure if this supported any more..
7806             if ((typeof(v) == 'object') && f.getRawValue) {
7807                 v = f.getRawValue() ; // dates..
7808             }
7809             // combo boxes where name != hiddenName...
7810             if (f.name != f.getName()) {
7811                 ret[f.name] = f.getRawValue();
7812             }
7813             ret[f.getName()] = v;
7814         });
7815         
7816         return ret;
7817     },
7818
7819     /**
7820      * Clears all invalid messages in this form.
7821      * @return {BasicForm} this
7822      */
7823     clearInvalid : function(){
7824         var items = this.getItems();
7825         
7826         items.each(function(f){
7827            f.clearInvalid();
7828         });
7829         
7830         
7831         
7832         return this;
7833     },
7834
7835     /**
7836      * Resets this form.
7837      * @return {BasicForm} this
7838      */
7839     reset : function(){
7840         var items = this.getItems();
7841         items.each(function(f){
7842             f.reset();
7843         });
7844         
7845         Roo.each(this.childForms || [], function (f) {
7846             f.reset();
7847         });
7848        
7849         
7850         return this;
7851     },
7852     getItems : function()
7853     {
7854         var r=new Roo.util.MixedCollection(false, function(o){
7855             return o.id || (o.id = Roo.id());
7856         });
7857         var iter = function(el) {
7858             if (el.inputEl) {
7859                 r.add(el);
7860             }
7861             if (!el.items) {
7862                 return;
7863             }
7864             Roo.each(el.items,function(e) {
7865                 iter(e);
7866             });
7867             
7868             
7869         };
7870         
7871         iter(this);
7872         return r;
7873         
7874         
7875         
7876         
7877     }
7878     
7879 });
7880
7881  
7882 /*
7883  * Based on:
7884  * Ext JS Library 1.1.1
7885  * Copyright(c) 2006-2007, Ext JS, LLC.
7886  *
7887  * Originally Released Under LGPL - original licence link has changed is not relivant.
7888  *
7889  * Fork - LGPL
7890  * <script type="text/javascript">
7891  */
7892 /**
7893  * @class Roo.form.VTypes
7894  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7895  * @singleton
7896  */
7897 Roo.form.VTypes = function(){
7898     // closure these in so they are only created once.
7899     var alpha = /^[a-zA-Z_]+$/;
7900     var alphanum = /^[a-zA-Z0-9_]+$/;
7901     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7902     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7903
7904     // All these messages and functions are configurable
7905     return {
7906         /**
7907          * The function used to validate email addresses
7908          * @param {String} value The email address
7909          */
7910         'email' : function(v){
7911             return email.test(v);
7912         },
7913         /**
7914          * The error text to display when the email validation function returns false
7915          * @type String
7916          */
7917         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7918         /**
7919          * The keystroke filter mask to be applied on email input
7920          * @type RegExp
7921          */
7922         'emailMask' : /[a-z0-9_\.\-@]/i,
7923
7924         /**
7925          * The function used to validate URLs
7926          * @param {String} value The URL
7927          */
7928         'url' : function(v){
7929             return url.test(v);
7930         },
7931         /**
7932          * The error text to display when the url validation function returns false
7933          * @type String
7934          */
7935         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7936         
7937         /**
7938          * The function used to validate alpha values
7939          * @param {String} value The value
7940          */
7941         'alpha' : function(v){
7942             return alpha.test(v);
7943         },
7944         /**
7945          * The error text to display when the alpha validation function returns false
7946          * @type String
7947          */
7948         'alphaText' : 'This field should only contain letters and _',
7949         /**
7950          * The keystroke filter mask to be applied on alpha input
7951          * @type RegExp
7952          */
7953         'alphaMask' : /[a-z_]/i,
7954
7955         /**
7956          * The function used to validate alphanumeric values
7957          * @param {String} value The value
7958          */
7959         'alphanum' : function(v){
7960             return alphanum.test(v);
7961         },
7962         /**
7963          * The error text to display when the alphanumeric validation function returns false
7964          * @type String
7965          */
7966         'alphanumText' : 'This field should only contain letters, numbers and _',
7967         /**
7968          * The keystroke filter mask to be applied on alphanumeric input
7969          * @type RegExp
7970          */
7971         'alphanumMask' : /[a-z0-9_]/i
7972     };
7973 }();/*
7974  * - LGPL
7975  *
7976  * Input
7977  * 
7978  */
7979
7980 /**
7981  * @class Roo.bootstrap.Input
7982  * @extends Roo.bootstrap.Component
7983  * Bootstrap Input class
7984  * @cfg {Boolean} disabled is it disabled
7985  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7986  * @cfg {String} name name of the input
7987  * @cfg {string} fieldLabel - the label associated
7988  * @cfg {string} placeholder - placeholder to put in text.
7989  * @cfg {string}  before - input group add on before
7990  * @cfg {string} after - input group add on after
7991  * @cfg {string} size - (lg|sm) or leave empty..
7992  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7993  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7994  * @cfg {Number} md colspan out of 12 for computer-sized screens
7995  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7996  * @cfg {string} value default value of the input
7997  * @cfg {Number} labelWidth set the width of label (0-12)
7998  * @cfg {String} labelAlign (top|left)
7999  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8000  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8001
8002  * @cfg {String} align (left|center|right) Default left
8003  * @cfg {Boolean} forceFeedback (true|false) Default false
8004  * 
8005  * 
8006  * 
8007  * 
8008  * @constructor
8009  * Create a new Input
8010  * @param {Object} config The config object
8011  */
8012
8013 Roo.bootstrap.Input = function(config){
8014     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8015    
8016         this.addEvents({
8017             /**
8018              * @event focus
8019              * Fires when this field receives input focus.
8020              * @param {Roo.form.Field} this
8021              */
8022             focus : true,
8023             /**
8024              * @event blur
8025              * Fires when this field loses input focus.
8026              * @param {Roo.form.Field} this
8027              */
8028             blur : true,
8029             /**
8030              * @event specialkey
8031              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8032              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8033              * @param {Roo.form.Field} this
8034              * @param {Roo.EventObject} e The event object
8035              */
8036             specialkey : true,
8037             /**
8038              * @event change
8039              * Fires just before the field blurs if the field value has changed.
8040              * @param {Roo.form.Field} this
8041              * @param {Mixed} newValue The new value
8042              * @param {Mixed} oldValue The original value
8043              */
8044             change : true,
8045             /**
8046              * @event invalid
8047              * Fires after the field has been marked as invalid.
8048              * @param {Roo.form.Field} this
8049              * @param {String} msg The validation message
8050              */
8051             invalid : true,
8052             /**
8053              * @event valid
8054              * Fires after the field has been validated with no errors.
8055              * @param {Roo.form.Field} this
8056              */
8057             valid : true,
8058              /**
8059              * @event keyup
8060              * Fires after the key up
8061              * @param {Roo.form.Field} this
8062              * @param {Roo.EventObject}  e The event Object
8063              */
8064             keyup : true
8065         });
8066 };
8067
8068 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8069      /**
8070      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8071       automatic validation (defaults to "keyup").
8072      */
8073     validationEvent : "keyup",
8074      /**
8075      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8076      */
8077     validateOnBlur : true,
8078     /**
8079      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8080      */
8081     validationDelay : 250,
8082      /**
8083      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8084      */
8085     focusClass : "x-form-focus",  // not needed???
8086     
8087        
8088     /**
8089      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8090      */
8091     invalidClass : "has-warning",
8092     
8093     /**
8094      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8095      */
8096     validClass : "has-success",
8097     
8098     /**
8099      * @cfg {Boolean} hasFeedback (true|false) default true
8100      */
8101     hasFeedback : true,
8102     
8103     /**
8104      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8105      */
8106     invalidFeedbackClass : "glyphicon-warning-sign",
8107     
8108     /**
8109      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8110      */
8111     validFeedbackClass : "glyphicon-ok",
8112     
8113     /**
8114      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8115      */
8116     selectOnFocus : false,
8117     
8118      /**
8119      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8120      */
8121     maskRe : null,
8122        /**
8123      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8124      */
8125     vtype : null,
8126     
8127       /**
8128      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8129      */
8130     disableKeyFilter : false,
8131     
8132        /**
8133      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8134      */
8135     disabled : false,
8136      /**
8137      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8138      */
8139     allowBlank : true,
8140     /**
8141      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8142      */
8143     blankText : "This field is required",
8144     
8145      /**
8146      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8147      */
8148     minLength : 0,
8149     /**
8150      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8151      */
8152     maxLength : Number.MAX_VALUE,
8153     /**
8154      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8155      */
8156     minLengthText : "The minimum length for this field is {0}",
8157     /**
8158      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8159      */
8160     maxLengthText : "The maximum length for this field is {0}",
8161   
8162     
8163     /**
8164      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8165      * If available, this function will be called only after the basic validators all return true, and will be passed the
8166      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8167      */
8168     validator : null,
8169     /**
8170      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8171      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8172      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8173      */
8174     regex : null,
8175     /**
8176      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8177      */
8178     regexText : "",
8179     
8180     autocomplete: false,
8181     
8182     
8183     fieldLabel : '',
8184     inputType : 'text',
8185     
8186     name : false,
8187     placeholder: false,
8188     before : false,
8189     after : false,
8190     size : false,
8191     hasFocus : false,
8192     preventMark: false,
8193     isFormField : true,
8194     value : '',
8195     labelWidth : 2,
8196     labelAlign : false,
8197     readOnly : false,
8198     align : false,
8199     formatedValue : false,
8200     forceFeedback : false,
8201     
8202     parentLabelAlign : function()
8203     {
8204         var parent = this;
8205         while (parent.parent()) {
8206             parent = parent.parent();
8207             if (typeof(parent.labelAlign) !='undefined') {
8208                 return parent.labelAlign;
8209             }
8210         }
8211         return 'left';
8212         
8213     },
8214     
8215     getAutoCreate : function(){
8216         
8217         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8218         
8219         var id = Roo.id();
8220         
8221         var cfg = {};
8222         
8223        
8224         
8225         if(this.inputType != 'hidden'){
8226             cfg.cls = 'form-group' //input-group
8227         }
8228         
8229         var input =  {
8230             tag: 'input',
8231             id : id,
8232             type : this.inputType,
8233             value : this.value,
8234             cls : 'form-control',
8235             placeholder : this.placeholder || '',
8236             autocomplete : this.autocomplete || 'new-password'
8237         };
8238         
8239         
8240         if(this.align){
8241             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8242         }
8243         
8244         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8245             input.maxLength = this.maxLength;
8246         }
8247         
8248         if (this.disabled) {
8249             input.disabled=true;
8250         }
8251         
8252         if (this.readOnly) {
8253             input.readonly=true;
8254         }
8255         
8256         if (this.name) {
8257             input.name = this.name;
8258         }
8259         if (this.size) {
8260             input.cls += ' input-' + this.size;
8261         }
8262         var settings=this;
8263         ['xs','sm','md','lg'].map(function(size){
8264             if (settings[size]) {
8265                 cfg.cls += ' col-' + size + '-' + settings[size];
8266             }
8267         });
8268         
8269         var inputblock = input;
8270         
8271         var feedback = {
8272             tag: 'span',
8273             cls: 'glyphicon form-control-feedback'
8274         };
8275             
8276         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8277             
8278             inputblock = {
8279                 cls : 'has-feedback',
8280                 cn :  [
8281                     input,
8282                     feedback
8283                 ] 
8284             };  
8285         }
8286         
8287         if (this.before || this.after) {
8288             
8289             inputblock = {
8290                 cls : 'input-group',
8291                 cn :  [] 
8292             };
8293             
8294             if (this.before && typeof(this.before) == 'string') {
8295                 
8296                 inputblock.cn.push({
8297                     tag :'span',
8298                     cls : 'roo-input-before input-group-addon',
8299                     html : this.before
8300                 });
8301             }
8302             if (this.before && typeof(this.before) == 'object') {
8303                 this.before = Roo.factory(this.before);
8304                 
8305                 inputblock.cn.push({
8306                     tag :'span',
8307                     cls : 'roo-input-before input-group-' +
8308                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8309                 });
8310             }
8311             
8312             inputblock.cn.push(input);
8313             
8314             if (this.after && typeof(this.after) == 'string') {
8315                 inputblock.cn.push({
8316                     tag :'span',
8317                     cls : 'roo-input-after input-group-addon',
8318                     html : this.after
8319                 });
8320             }
8321             if (this.after && typeof(this.after) == 'object') {
8322                 this.after = Roo.factory(this.after);
8323                 
8324                 inputblock.cn.push({
8325                     tag :'span',
8326                     cls : 'roo-input-after input-group-' +
8327                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8328                 });
8329             }
8330             
8331             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8332                 inputblock.cls += ' has-feedback';
8333                 inputblock.cn.push(feedback);
8334             }
8335         };
8336         
8337         if (align ==='left' && this.fieldLabel.length) {
8338                 
8339                 cfg.cn = [
8340                     
8341                     {
8342                         tag: 'label',
8343                         'for' :  id,
8344                         cls : 'control-label col-sm-' + this.labelWidth,
8345                         html : this.fieldLabel
8346                         
8347                     },
8348                     {
8349                         cls : "col-sm-" + (12 - this.labelWidth), 
8350                         cn: [
8351                             inputblock
8352                         ]
8353                     }
8354                     
8355                 ];
8356         } else if ( this.fieldLabel.length) {
8357                 
8358                  cfg.cn = [
8359                    
8360                     {
8361                         tag: 'label',
8362                         //cls : 'input-group-addon',
8363                         html : this.fieldLabel
8364                         
8365                     },
8366                     
8367                     inputblock
8368                     
8369                 ];
8370
8371         } else {
8372             
8373                 cfg.cn = [
8374                     
8375                         inputblock
8376                     
8377                 ];
8378                 
8379                 
8380         };
8381         
8382         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8383            cfg.cls += ' navbar-form';
8384         }
8385         if (this.parentType === 'NavGroup') {
8386            cfg.cls += ' navbar-form';
8387            cfg.tag = 'li';
8388         }
8389         return cfg;
8390         
8391     },
8392     /**
8393      * return the real input element.
8394      */
8395     inputEl: function ()
8396     {
8397         return this.el.select('input.form-control',true).first();
8398     },
8399     
8400     tooltipEl : function()
8401     {
8402         return this.inputEl();
8403     },
8404     
8405     setDisabled : function(v)
8406     {
8407         var i  = this.inputEl().dom;
8408         if (!v) {
8409             i.removeAttribute('disabled');
8410             return;
8411             
8412         }
8413         i.setAttribute('disabled','true');
8414     },
8415     initEvents : function()
8416     {
8417           
8418         this.inputEl().on("keydown" , this.fireKey,  this);
8419         this.inputEl().on("focus", this.onFocus,  this);
8420         this.inputEl().on("blur", this.onBlur,  this);
8421         
8422         this.inputEl().relayEvent('keyup', this);
8423  
8424         // reference to original value for reset
8425         this.originalValue = this.getValue();
8426         //Roo.form.TextField.superclass.initEvents.call(this);
8427         if(this.validationEvent == 'keyup'){
8428             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8429             this.inputEl().on('keyup', this.filterValidation, this);
8430         }
8431         else if(this.validationEvent !== false){
8432             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8433         }
8434         
8435         if(this.selectOnFocus){
8436             this.on("focus", this.preFocus, this);
8437             
8438         }
8439         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8440             this.inputEl().on("keypress", this.filterKeys, this);
8441         }
8442        /* if(this.grow){
8443             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8444             this.el.on("click", this.autoSize,  this);
8445         }
8446         */
8447         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8448             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8449         }
8450         
8451         if (typeof(this.before) == 'object') {
8452             this.before.render(this.el.select('.roo-input-before',true).first());
8453         }
8454         if (typeof(this.after) == 'object') {
8455             this.after.render(this.el.select('.roo-input-after',true).first());
8456         }
8457         
8458         
8459     },
8460     filterValidation : function(e){
8461         if(!e.isNavKeyPress()){
8462             this.validationTask.delay(this.validationDelay);
8463         }
8464     },
8465      /**
8466      * Validates the field value
8467      * @return {Boolean} True if the value is valid, else false
8468      */
8469     validate : function(){
8470         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8471         if(this.disabled || this.validateValue(this.getRawValue())){
8472             this.markValid();
8473             return true;
8474         }
8475         
8476         this.markInvalid();
8477         return false;
8478     },
8479     
8480     
8481     /**
8482      * Validates a value according to the field's validation rules and marks the field as invalid
8483      * if the validation fails
8484      * @param {Mixed} value The value to validate
8485      * @return {Boolean} True if the value is valid, else false
8486      */
8487     validateValue : function(value){
8488         if(value.length < 1)  { // if it's blank
8489             if(this.allowBlank){
8490                 return true;
8491             }
8492             return false;
8493         }
8494         
8495         if(value.length < this.minLength){
8496             return false;
8497         }
8498         if(value.length > this.maxLength){
8499             return false;
8500         }
8501         if(this.vtype){
8502             var vt = Roo.form.VTypes;
8503             if(!vt[this.vtype](value, this)){
8504                 return false;
8505             }
8506         }
8507         if(typeof this.validator == "function"){
8508             var msg = this.validator(value);
8509             if(msg !== true){
8510                 return false;
8511             }
8512         }
8513         
8514         if(this.regex && !this.regex.test(value)){
8515             return false;
8516         }
8517         
8518         return true;
8519     },
8520
8521     
8522     
8523      // private
8524     fireKey : function(e){
8525         //Roo.log('field ' + e.getKey());
8526         if(e.isNavKeyPress()){
8527             this.fireEvent("specialkey", this, e);
8528         }
8529     },
8530     focus : function (selectText){
8531         if(this.rendered){
8532             this.inputEl().focus();
8533             if(selectText === true){
8534                 this.inputEl().dom.select();
8535             }
8536         }
8537         return this;
8538     } ,
8539     
8540     onFocus : function(){
8541         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8542            // this.el.addClass(this.focusClass);
8543         }
8544         if(!this.hasFocus){
8545             this.hasFocus = true;
8546             this.startValue = this.getValue();
8547             this.fireEvent("focus", this);
8548         }
8549     },
8550     
8551     beforeBlur : Roo.emptyFn,
8552
8553     
8554     // private
8555     onBlur : function(){
8556         this.beforeBlur();
8557         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8558             //this.el.removeClass(this.focusClass);
8559         }
8560         this.hasFocus = false;
8561         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8562             this.validate();
8563         }
8564         var v = this.getValue();
8565         if(String(v) !== String(this.startValue)){
8566             this.fireEvent('change', this, v, this.startValue);
8567         }
8568         this.fireEvent("blur", this);
8569     },
8570     
8571     /**
8572      * Resets the current field value to the originally loaded value and clears any validation messages
8573      */
8574     reset : function(){
8575         this.setValue(this.originalValue);
8576         this.validate();
8577     },
8578      /**
8579      * Returns the name of the field
8580      * @return {Mixed} name The name field
8581      */
8582     getName: function(){
8583         return this.name;
8584     },
8585      /**
8586      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8587      * @return {Mixed} value The field value
8588      */
8589     getValue : function(){
8590         
8591         var v = this.inputEl().getValue();
8592         
8593         return v;
8594     },
8595     /**
8596      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8597      * @return {Mixed} value The field value
8598      */
8599     getRawValue : function(){
8600         var v = this.inputEl().getValue();
8601         
8602         return v;
8603     },
8604     
8605     /**
8606      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8607      * @param {Mixed} value The value to set
8608      */
8609     setRawValue : function(v){
8610         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8611     },
8612     
8613     selectText : function(start, end){
8614         var v = this.getRawValue();
8615         if(v.length > 0){
8616             start = start === undefined ? 0 : start;
8617             end = end === undefined ? v.length : end;
8618             var d = this.inputEl().dom;
8619             if(d.setSelectionRange){
8620                 d.setSelectionRange(start, end);
8621             }else if(d.createTextRange){
8622                 var range = d.createTextRange();
8623                 range.moveStart("character", start);
8624                 range.moveEnd("character", v.length-end);
8625                 range.select();
8626             }
8627         }
8628     },
8629     
8630     /**
8631      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8632      * @param {Mixed} value The value to set
8633      */
8634     setValue : function(v){
8635         this.value = v;
8636         if(this.rendered){
8637             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8638             this.validate();
8639         }
8640     },
8641     
8642     /*
8643     processValue : function(value){
8644         if(this.stripCharsRe){
8645             var newValue = value.replace(this.stripCharsRe, '');
8646             if(newValue !== value){
8647                 this.setRawValue(newValue);
8648                 return newValue;
8649             }
8650         }
8651         return value;
8652     },
8653   */
8654     preFocus : function(){
8655         
8656         if(this.selectOnFocus){
8657             this.inputEl().dom.select();
8658         }
8659     },
8660     filterKeys : function(e){
8661         var k = e.getKey();
8662         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8663             return;
8664         }
8665         var c = e.getCharCode(), cc = String.fromCharCode(c);
8666         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8667             return;
8668         }
8669         if(!this.maskRe.test(cc)){
8670             e.stopEvent();
8671         }
8672     },
8673      /**
8674      * Clear any invalid styles/messages for this field
8675      */
8676     clearInvalid : function(){
8677         
8678         if(!this.el || this.preventMark){ // not rendered
8679             return;
8680         }
8681         
8682         var label = this.el.select('label', true).first();
8683         var icon = this.el.select('i.fa-star', true).first();
8684         
8685         if(label && icon){
8686             icon.remove();
8687         }
8688         
8689         this.el.removeClass(this.invalidClass);
8690         
8691         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8692             
8693             var feedback = this.el.select('.form-control-feedback', true).first();
8694             
8695             if(feedback){
8696                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8697             }
8698             
8699         }
8700         
8701         this.fireEvent('valid', this);
8702     },
8703     
8704      /**
8705      * Mark this field as valid
8706      */
8707     markValid : function()
8708     {
8709         if(!this.el  || this.preventMark){ // not rendered
8710             return;
8711         }
8712         
8713         this.el.removeClass([this.invalidClass, this.validClass]);
8714         
8715         var feedback = this.el.select('.form-control-feedback', true).first();
8716             
8717         if(feedback){
8718             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8719         }
8720
8721         if(this.disabled || this.allowBlank){
8722             return;
8723         }
8724         
8725         var formGroup = this.el.findParent('.form-group', false, true);
8726         
8727         if(formGroup){
8728             
8729             var label = formGroup.select('label', true).first();
8730             var icon = formGroup.select('i.fa-star', true).first();
8731             
8732             if(label && icon){
8733                 icon.remove();
8734             }
8735         }
8736         
8737         this.el.addClass(this.validClass);
8738         
8739         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8740             
8741             var feedback = this.el.select('.form-control-feedback', true).first();
8742             
8743             if(feedback){
8744                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8745                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8746             }
8747             
8748         }
8749         
8750         this.fireEvent('valid', this);
8751     },
8752     
8753      /**
8754      * Mark this field as invalid
8755      * @param {String} msg The validation message
8756      */
8757     markInvalid : function(msg)
8758     {
8759         if(!this.el  || this.preventMark){ // not rendered
8760             return;
8761         }
8762         
8763         this.el.removeClass([this.invalidClass, this.validClass]);
8764         
8765         var feedback = this.el.select('.form-control-feedback', true).first();
8766             
8767         if(feedback){
8768             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8769         }
8770
8771         if(this.disabled || this.allowBlank){
8772             return;
8773         }
8774         
8775         var formGroup = this.el.findParent('.form-group', false, true);
8776         
8777         if(formGroup){
8778             var label = formGroup.select('label', true).first();
8779             var icon = formGroup.select('i.fa-star', true).first();
8780
8781             if(!this.getValue().length && label && !icon){
8782                 this.el.findParent('.form-group', false, true).createChild({
8783                     tag : 'i',
8784                     cls : 'text-danger fa fa-lg fa-star',
8785                     tooltip : 'This field is required',
8786                     style : 'margin-right:5px;'
8787                 }, label, true);
8788             }
8789         }
8790         
8791         
8792         this.el.addClass(this.invalidClass);
8793         
8794         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8795             
8796             var feedback = this.el.select('.form-control-feedback', true).first();
8797             
8798             if(feedback){
8799                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8800                 
8801                 if(this.getValue().length || this.forceFeedback){
8802                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8803                 }
8804                 
8805             }
8806             
8807         }
8808         
8809         this.fireEvent('invalid', this, msg);
8810     },
8811     // private
8812     SafariOnKeyDown : function(event)
8813     {
8814         // this is a workaround for a password hang bug on chrome/ webkit.
8815         
8816         var isSelectAll = false;
8817         
8818         if(this.inputEl().dom.selectionEnd > 0){
8819             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8820         }
8821         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8822             event.preventDefault();
8823             this.setValue('');
8824             return;
8825         }
8826         
8827         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8828             
8829             event.preventDefault();
8830             // this is very hacky as keydown always get's upper case.
8831             //
8832             var cc = String.fromCharCode(event.getCharCode());
8833             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8834             
8835         }
8836     },
8837     adjustWidth : function(tag, w){
8838         tag = tag.toLowerCase();
8839         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8840             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8841                 if(tag == 'input'){
8842                     return w + 2;
8843                 }
8844                 if(tag == 'textarea'){
8845                     return w-2;
8846                 }
8847             }else if(Roo.isOpera){
8848                 if(tag == 'input'){
8849                     return w + 2;
8850                 }
8851                 if(tag == 'textarea'){
8852                     return w-2;
8853                 }
8854             }
8855         }
8856         return w;
8857     }
8858     
8859 });
8860
8861  
8862 /*
8863  * - LGPL
8864  *
8865  * Input
8866  * 
8867  */
8868
8869 /**
8870  * @class Roo.bootstrap.TextArea
8871  * @extends Roo.bootstrap.Input
8872  * Bootstrap TextArea class
8873  * @cfg {Number} cols Specifies the visible width of a text area
8874  * @cfg {Number} rows Specifies the visible number of lines in a text area
8875  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8876  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8877  * @cfg {string} html text
8878  * 
8879  * @constructor
8880  * Create a new TextArea
8881  * @param {Object} config The config object
8882  */
8883
8884 Roo.bootstrap.TextArea = function(config){
8885     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8886    
8887 };
8888
8889 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8890      
8891     cols : false,
8892     rows : 5,
8893     readOnly : false,
8894     warp : 'soft',
8895     resize : false,
8896     value: false,
8897     html: false,
8898     
8899     getAutoCreate : function(){
8900         
8901         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8902         
8903         var id = Roo.id();
8904         
8905         var cfg = {};
8906         
8907         var input =  {
8908             tag: 'textarea',
8909             id : id,
8910             warp : this.warp,
8911             rows : this.rows,
8912             value : this.value || '',
8913             html: this.html || '',
8914             cls : 'form-control',
8915             placeholder : this.placeholder || '' 
8916             
8917         };
8918         
8919         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8920             input.maxLength = this.maxLength;
8921         }
8922         
8923         if(this.resize){
8924             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8925         }
8926         
8927         if(this.cols){
8928             input.cols = this.cols;
8929         }
8930         
8931         if (this.readOnly) {
8932             input.readonly = true;
8933         }
8934         
8935         if (this.name) {
8936             input.name = this.name;
8937         }
8938         
8939         if (this.size) {
8940             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8941         }
8942         
8943         var settings=this;
8944         ['xs','sm','md','lg'].map(function(size){
8945             if (settings[size]) {
8946                 cfg.cls += ' col-' + size + '-' + settings[size];
8947             }
8948         });
8949         
8950         var inputblock = input;
8951         
8952         if(this.hasFeedback && !this.allowBlank){
8953             
8954             var feedback = {
8955                 tag: 'span',
8956                 cls: 'glyphicon form-control-feedback'
8957             };
8958
8959             inputblock = {
8960                 cls : 'has-feedback',
8961                 cn :  [
8962                     input,
8963                     feedback
8964                 ] 
8965             };  
8966         }
8967         
8968         
8969         if (this.before || this.after) {
8970             
8971             inputblock = {
8972                 cls : 'input-group',
8973                 cn :  [] 
8974             };
8975             if (this.before) {
8976                 inputblock.cn.push({
8977                     tag :'span',
8978                     cls : 'input-group-addon',
8979                     html : this.before
8980                 });
8981             }
8982             
8983             inputblock.cn.push(input);
8984             
8985             if(this.hasFeedback && !this.allowBlank){
8986                 inputblock.cls += ' has-feedback';
8987                 inputblock.cn.push(feedback);
8988             }
8989             
8990             if (this.after) {
8991                 inputblock.cn.push({
8992                     tag :'span',
8993                     cls : 'input-group-addon',
8994                     html : this.after
8995                 });
8996             }
8997             
8998         }
8999         
9000         if (align ==='left' && this.fieldLabel.length) {
9001 //                Roo.log("left and has label");
9002                 cfg.cn = [
9003                     
9004                     {
9005                         tag: 'label',
9006                         'for' :  id,
9007                         cls : 'control-label col-sm-' + this.labelWidth,
9008                         html : this.fieldLabel
9009                         
9010                     },
9011                     {
9012                         cls : "col-sm-" + (12 - this.labelWidth), 
9013                         cn: [
9014                             inputblock
9015                         ]
9016                     }
9017                     
9018                 ];
9019         } else if ( this.fieldLabel.length) {
9020 //                Roo.log(" label");
9021                  cfg.cn = [
9022                    
9023                     {
9024                         tag: 'label',
9025                         //cls : 'input-group-addon',
9026                         html : this.fieldLabel
9027                         
9028                     },
9029                     
9030                     inputblock
9031                     
9032                 ];
9033
9034         } else {
9035             
9036 //                   Roo.log(" no label && no align");
9037                 cfg.cn = [
9038                     
9039                         inputblock
9040                     
9041                 ];
9042                 
9043                 
9044         }
9045         
9046         if (this.disabled) {
9047             input.disabled=true;
9048         }
9049         
9050         return cfg;
9051         
9052     },
9053     /**
9054      * return the real textarea element.
9055      */
9056     inputEl: function ()
9057     {
9058         return this.el.select('textarea.form-control',true).first();
9059     },
9060     
9061     /**
9062      * Clear any invalid styles/messages for this field
9063      */
9064     clearInvalid : function()
9065     {
9066         
9067         if(!this.el || this.preventMark){ // not rendered
9068             return;
9069         }
9070         
9071         var label = this.el.select('label', true).first();
9072         var icon = this.el.select('i.fa-star', true).first();
9073         
9074         if(label && icon){
9075             icon.remove();
9076         }
9077         
9078         this.el.removeClass(this.invalidClass);
9079         
9080         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9081             
9082             var feedback = this.el.select('.form-control-feedback', true).first();
9083             
9084             if(feedback){
9085                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9086             }
9087             
9088         }
9089         
9090         this.fireEvent('valid', this);
9091     },
9092     
9093      /**
9094      * Mark this field as valid
9095      */
9096     markValid : function()
9097     {
9098         if(!this.el  || this.preventMark){ // not rendered
9099             return;
9100         }
9101         
9102         this.el.removeClass([this.invalidClass, this.validClass]);
9103         
9104         var feedback = this.el.select('.form-control-feedback', true).first();
9105             
9106         if(feedback){
9107             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9108         }
9109
9110         if(this.disabled || this.allowBlank){
9111             return;
9112         }
9113         
9114         var label = this.el.select('label', true).first();
9115         var icon = this.el.select('i.fa-star', true).first();
9116         
9117         if(label && icon){
9118             icon.remove();
9119         }
9120         
9121         this.el.addClass(this.validClass);
9122         
9123         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9124             
9125             var feedback = this.el.select('.form-control-feedback', true).first();
9126             
9127             if(feedback){
9128                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9129                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9130             }
9131             
9132         }
9133         
9134         this.fireEvent('valid', this);
9135     },
9136     
9137      /**
9138      * Mark this field as invalid
9139      * @param {String} msg The validation message
9140      */
9141     markInvalid : function(msg)
9142     {
9143         if(!this.el  || this.preventMark){ // not rendered
9144             return;
9145         }
9146         
9147         this.el.removeClass([this.invalidClass, this.validClass]);
9148         
9149         var feedback = this.el.select('.form-control-feedback', true).first();
9150             
9151         if(feedback){
9152             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9153         }
9154
9155         if(this.disabled || this.allowBlank){
9156             return;
9157         }
9158         
9159         var label = this.el.select('label', true).first();
9160         var icon = this.el.select('i.fa-star', true).first();
9161         
9162         if(!this.getValue().length && label && !icon){
9163             this.el.createChild({
9164                 tag : 'i',
9165                 cls : 'text-danger fa fa-lg fa-star',
9166                 tooltip : 'This field is required',
9167                 style : 'margin-right:5px;'
9168             }, label, true);
9169         }
9170
9171         this.el.addClass(this.invalidClass);
9172         
9173         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9174             
9175             var feedback = this.el.select('.form-control-feedback', true).first();
9176             
9177             if(feedback){
9178                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9179                 
9180                 if(this.getValue().length || this.forceFeedback){
9181                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9182                 }
9183                 
9184             }
9185             
9186         }
9187         
9188         this.fireEvent('invalid', this, msg);
9189     }
9190 });
9191
9192  
9193 /*
9194  * - LGPL
9195  *
9196  * trigger field - base class for combo..
9197  * 
9198  */
9199  
9200 /**
9201  * @class Roo.bootstrap.TriggerField
9202  * @extends Roo.bootstrap.Input
9203  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9204  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9205  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9206  * for which you can provide a custom implementation.  For example:
9207  * <pre><code>
9208 var trigger = new Roo.bootstrap.TriggerField();
9209 trigger.onTriggerClick = myTriggerFn;
9210 trigger.applyTo('my-field');
9211 </code></pre>
9212  *
9213  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9214  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9215  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9216  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9217  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9218
9219  * @constructor
9220  * Create a new TriggerField.
9221  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9222  * to the base TextField)
9223  */
9224 Roo.bootstrap.TriggerField = function(config){
9225     this.mimicing = false;
9226     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9227 };
9228
9229 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9230     /**
9231      * @cfg {String} triggerClass A CSS class to apply to the trigger
9232      */
9233      /**
9234      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9235      */
9236     hideTrigger:false,
9237
9238     /**
9239      * @cfg {Boolean} removable (true|false) special filter default false
9240      */
9241     removable : false,
9242     
9243     /** @cfg {Boolean} grow @hide */
9244     /** @cfg {Number} growMin @hide */
9245     /** @cfg {Number} growMax @hide */
9246
9247     /**
9248      * @hide 
9249      * @method
9250      */
9251     autoSize: Roo.emptyFn,
9252     // private
9253     monitorTab : true,
9254     // private
9255     deferHeight : true,
9256
9257     
9258     actionMode : 'wrap',
9259     
9260     caret : false,
9261     
9262     
9263     getAutoCreate : function(){
9264        
9265         var align = this.labelAlign || this.parentLabelAlign();
9266         
9267         var id = Roo.id();
9268         
9269         var cfg = {
9270             cls: 'form-group' //input-group
9271         };
9272         
9273         
9274         var input =  {
9275             tag: 'input',
9276             id : id,
9277             type : this.inputType,
9278             cls : 'form-control',
9279             autocomplete: 'new-password',
9280             placeholder : this.placeholder || '' 
9281             
9282         };
9283         if (this.name) {
9284             input.name = this.name;
9285         }
9286         if (this.size) {
9287             input.cls += ' input-' + this.size;
9288         }
9289         
9290         if (this.disabled) {
9291             input.disabled=true;
9292         }
9293         
9294         var inputblock = input;
9295         
9296         if(this.hasFeedback && !this.allowBlank){
9297             
9298             var feedback = {
9299                 tag: 'span',
9300                 cls: 'glyphicon form-control-feedback'
9301             };
9302             
9303             if(this.removable && !this.editable && !this.tickable){
9304                 inputblock = {
9305                     cls : 'has-feedback',
9306                     cn :  [
9307                         inputblock,
9308                         {
9309                             tag: 'button',
9310                             html : 'x',
9311                             cls : 'roo-combo-removable-btn close'
9312                         },
9313                         feedback
9314                     ] 
9315                 };
9316             } else {
9317                 inputblock = {
9318                     cls : 'has-feedback',
9319                     cn :  [
9320                         inputblock,
9321                         feedback
9322                     ] 
9323                 };
9324             }
9325
9326         } else {
9327             if(this.removable && !this.editable && !this.tickable){
9328                 inputblock = {
9329                     cls : 'roo-removable',
9330                     cn :  [
9331                         inputblock,
9332                         {
9333                             tag: 'button',
9334                             html : 'x',
9335                             cls : 'roo-combo-removable-btn close'
9336                         }
9337                     ] 
9338                 };
9339             }
9340         }
9341         
9342         if (this.before || this.after) {
9343             
9344             inputblock = {
9345                 cls : 'input-group',
9346                 cn :  [] 
9347             };
9348             if (this.before) {
9349                 inputblock.cn.push({
9350                     tag :'span',
9351                     cls : 'input-group-addon',
9352                     html : this.before
9353                 });
9354             }
9355             
9356             inputblock.cn.push(input);
9357             
9358             if(this.hasFeedback && !this.allowBlank){
9359                 inputblock.cls += ' has-feedback';
9360                 inputblock.cn.push(feedback);
9361             }
9362             
9363             if (this.after) {
9364                 inputblock.cn.push({
9365                     tag :'span',
9366                     cls : 'input-group-addon',
9367                     html : this.after
9368                 });
9369             }
9370             
9371         };
9372         
9373         var box = {
9374             tag: 'div',
9375             cn: [
9376                 {
9377                     tag: 'input',
9378                     type : 'hidden',
9379                     cls: 'form-hidden-field'
9380                 },
9381                 inputblock
9382             ]
9383             
9384         };
9385         
9386         if(this.multiple){
9387             box = {
9388                 tag: 'div',
9389                 cn: [
9390                     {
9391                         tag: 'input',
9392                         type : 'hidden',
9393                         cls: 'form-hidden-field'
9394                     },
9395                     {
9396                         tag: 'ul',
9397                         cls: 'roo-select2-choices',
9398                         cn:[
9399                             {
9400                                 tag: 'li',
9401                                 cls: 'roo-select2-search-field',
9402                                 cn: [
9403
9404                                     inputblock
9405                                 ]
9406                             }
9407                         ]
9408                     }
9409                 ]
9410             }
9411         };
9412         
9413         var combobox = {
9414             cls: 'roo-select2-container input-group',
9415             cn: [
9416                 box
9417 //                {
9418 //                    tag: 'ul',
9419 //                    cls: 'typeahead typeahead-long dropdown-menu',
9420 //                    style: 'display:none'
9421 //                }
9422             ]
9423         };
9424         
9425         if(!this.multiple && this.showToggleBtn){
9426             
9427             var caret = {
9428                         tag: 'span',
9429                         cls: 'caret'
9430              };
9431             if (this.caret != false) {
9432                 caret = {
9433                      tag: 'i',
9434                      cls: 'fa fa-' + this.caret
9435                 };
9436                 
9437             }
9438             
9439             combobox.cn.push({
9440                 tag :'span',
9441                 cls : 'input-group-addon btn dropdown-toggle',
9442                 cn : [
9443                     caret,
9444                     {
9445                         tag: 'span',
9446                         cls: 'combobox-clear',
9447                         cn  : [
9448                             {
9449                                 tag : 'i',
9450                                 cls: 'icon-remove'
9451                             }
9452                         ]
9453                     }
9454                 ]
9455
9456             })
9457         }
9458         
9459         if(this.multiple){
9460             combobox.cls += ' roo-select2-container-multi';
9461         }
9462         
9463         if (align ==='left' && this.fieldLabel.length) {
9464             
9465 //                Roo.log("left and has label");
9466                 cfg.cn = [
9467                     
9468                     {
9469                         tag: 'label',
9470                         'for' :  id,
9471                         cls : 'control-label col-sm-' + this.labelWidth,
9472                         html : this.fieldLabel
9473                         
9474                     },
9475                     {
9476                         cls : "col-sm-" + (12 - this.labelWidth), 
9477                         cn: [
9478                             combobox
9479                         ]
9480                     }
9481                     
9482                 ];
9483         } else if ( this.fieldLabel.length) {
9484 //                Roo.log(" label");
9485                  cfg.cn = [
9486                    
9487                     {
9488                         tag: 'label',
9489                         //cls : 'input-group-addon',
9490                         html : this.fieldLabel
9491                         
9492                     },
9493                     
9494                     combobox
9495                     
9496                 ];
9497
9498         } else {
9499             
9500 //                Roo.log(" no label && no align");
9501                 cfg = combobox
9502                      
9503                 
9504         }
9505          
9506         var settings=this;
9507         ['xs','sm','md','lg'].map(function(size){
9508             if (settings[size]) {
9509                 cfg.cls += ' col-' + size + '-' + settings[size];
9510             }
9511         });
9512         
9513         return cfg;
9514         
9515     },
9516     
9517     
9518     
9519     // private
9520     onResize : function(w, h){
9521 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9522 //        if(typeof w == 'number'){
9523 //            var x = w - this.trigger.getWidth();
9524 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9525 //            this.trigger.setStyle('left', x+'px');
9526 //        }
9527     },
9528
9529     // private
9530     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9531
9532     // private
9533     getResizeEl : function(){
9534         return this.inputEl();
9535     },
9536
9537     // private
9538     getPositionEl : function(){
9539         return this.inputEl();
9540     },
9541
9542     // private
9543     alignErrorIcon : function(){
9544         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9545     },
9546
9547     // private
9548     initEvents : function(){
9549         
9550         this.createList();
9551         
9552         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9553         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9554         if(!this.multiple && this.showToggleBtn){
9555             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9556             if(this.hideTrigger){
9557                 this.trigger.setDisplayed(false);
9558             }
9559             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9560         }
9561         
9562         if(this.multiple){
9563             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9564         }
9565         
9566         if(this.removable && !this.editable && !this.tickable){
9567             var close = this.closeTriggerEl();
9568             
9569             if(close){
9570                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9571                 close.on('click', this.removeBtnClick, this, close);
9572             }
9573         }
9574         
9575         //this.trigger.addClassOnOver('x-form-trigger-over');
9576         //this.trigger.addClassOnClick('x-form-trigger-click');
9577         
9578         //if(!this.width){
9579         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9580         //}
9581     },
9582     
9583     closeTriggerEl : function()
9584     {
9585         var close = this.el.select('.roo-combo-removable-btn', true).first();
9586         return close ? close : false;
9587     },
9588     
9589     removeBtnClick : function(e, h, el)
9590     {
9591         e.preventDefault();
9592         
9593         if(this.fireEvent("remove", this) !== false){
9594             this.reset();
9595             this.fireEvent("afterremove", this)
9596         }
9597     },
9598     
9599     createList : function()
9600     {
9601         this.list = Roo.get(document.body).createChild({
9602             tag: 'ul',
9603             cls: 'typeahead typeahead-long dropdown-menu',
9604             style: 'display:none'
9605         });
9606         
9607         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9608         
9609     },
9610
9611     // private
9612     initTrigger : function(){
9613        
9614     },
9615
9616     // private
9617     onDestroy : function(){
9618         if(this.trigger){
9619             this.trigger.removeAllListeners();
9620           //  this.trigger.remove();
9621         }
9622         //if(this.wrap){
9623         //    this.wrap.remove();
9624         //}
9625         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9626     },
9627
9628     // private
9629     onFocus : function(){
9630         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9631         /*
9632         if(!this.mimicing){
9633             this.wrap.addClass('x-trigger-wrap-focus');
9634             this.mimicing = true;
9635             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9636             if(this.monitorTab){
9637                 this.el.on("keydown", this.checkTab, this);
9638             }
9639         }
9640         */
9641     },
9642
9643     // private
9644     checkTab : function(e){
9645         if(e.getKey() == e.TAB){
9646             this.triggerBlur();
9647         }
9648     },
9649
9650     // private
9651     onBlur : function(){
9652         // do nothing
9653     },
9654
9655     // private
9656     mimicBlur : function(e, t){
9657         /*
9658         if(!this.wrap.contains(t) && this.validateBlur()){
9659             this.triggerBlur();
9660         }
9661         */
9662     },
9663
9664     // private
9665     triggerBlur : function(){
9666         this.mimicing = false;
9667         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9668         if(this.monitorTab){
9669             this.el.un("keydown", this.checkTab, this);
9670         }
9671         //this.wrap.removeClass('x-trigger-wrap-focus');
9672         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9673     },
9674
9675     // private
9676     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9677     validateBlur : function(e, t){
9678         return true;
9679     },
9680
9681     // private
9682     onDisable : function(){
9683         this.inputEl().dom.disabled = true;
9684         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9685         //if(this.wrap){
9686         //    this.wrap.addClass('x-item-disabled');
9687         //}
9688     },
9689
9690     // private
9691     onEnable : function(){
9692         this.inputEl().dom.disabled = false;
9693         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9694         //if(this.wrap){
9695         //    this.el.removeClass('x-item-disabled');
9696         //}
9697     },
9698
9699     // private
9700     onShow : function(){
9701         var ae = this.getActionEl();
9702         
9703         if(ae){
9704             ae.dom.style.display = '';
9705             ae.dom.style.visibility = 'visible';
9706         }
9707     },
9708
9709     // private
9710     
9711     onHide : function(){
9712         var ae = this.getActionEl();
9713         ae.dom.style.display = 'none';
9714     },
9715
9716     /**
9717      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9718      * by an implementing function.
9719      * @method
9720      * @param {EventObject} e
9721      */
9722     onTriggerClick : Roo.emptyFn
9723 });
9724  /*
9725  * Based on:
9726  * Ext JS Library 1.1.1
9727  * Copyright(c) 2006-2007, Ext JS, LLC.
9728  *
9729  * Originally Released Under LGPL - original licence link has changed is not relivant.
9730  *
9731  * Fork - LGPL
9732  * <script type="text/javascript">
9733  */
9734
9735
9736 /**
9737  * @class Roo.data.SortTypes
9738  * @singleton
9739  * Defines the default sorting (casting?) comparison functions used when sorting data.
9740  */
9741 Roo.data.SortTypes = {
9742     /**
9743      * Default sort that does nothing
9744      * @param {Mixed} s The value being converted
9745      * @return {Mixed} The comparison value
9746      */
9747     none : function(s){
9748         return s;
9749     },
9750     
9751     /**
9752      * The regular expression used to strip tags
9753      * @type {RegExp}
9754      * @property
9755      */
9756     stripTagsRE : /<\/?[^>]+>/gi,
9757     
9758     /**
9759      * Strips all HTML tags to sort on text only
9760      * @param {Mixed} s The value being converted
9761      * @return {String} The comparison value
9762      */
9763     asText : function(s){
9764         return String(s).replace(this.stripTagsRE, "");
9765     },
9766     
9767     /**
9768      * Strips all HTML tags to sort on text only - Case insensitive
9769      * @param {Mixed} s The value being converted
9770      * @return {String} The comparison value
9771      */
9772     asUCText : function(s){
9773         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9774     },
9775     
9776     /**
9777      * Case insensitive string
9778      * @param {Mixed} s The value being converted
9779      * @return {String} The comparison value
9780      */
9781     asUCString : function(s) {
9782         return String(s).toUpperCase();
9783     },
9784     
9785     /**
9786      * Date sorting
9787      * @param {Mixed} s The value being converted
9788      * @return {Number} The comparison value
9789      */
9790     asDate : function(s) {
9791         if(!s){
9792             return 0;
9793         }
9794         if(s instanceof Date){
9795             return s.getTime();
9796         }
9797         return Date.parse(String(s));
9798     },
9799     
9800     /**
9801      * Float sorting
9802      * @param {Mixed} s The value being converted
9803      * @return {Float} The comparison value
9804      */
9805     asFloat : function(s) {
9806         var val = parseFloat(String(s).replace(/,/g, ""));
9807         if(isNaN(val)) {
9808             val = 0;
9809         }
9810         return val;
9811     },
9812     
9813     /**
9814      * Integer sorting
9815      * @param {Mixed} s The value being converted
9816      * @return {Number} The comparison value
9817      */
9818     asInt : function(s) {
9819         var val = parseInt(String(s).replace(/,/g, ""));
9820         if(isNaN(val)) {
9821             val = 0;
9822         }
9823         return val;
9824     }
9825 };/*
9826  * Based on:
9827  * Ext JS Library 1.1.1
9828  * Copyright(c) 2006-2007, Ext JS, LLC.
9829  *
9830  * Originally Released Under LGPL - original licence link has changed is not relivant.
9831  *
9832  * Fork - LGPL
9833  * <script type="text/javascript">
9834  */
9835
9836 /**
9837 * @class Roo.data.Record
9838  * Instances of this class encapsulate both record <em>definition</em> information, and record
9839  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9840  * to access Records cached in an {@link Roo.data.Store} object.<br>
9841  * <p>
9842  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9843  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9844  * objects.<br>
9845  * <p>
9846  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9847  * @constructor
9848  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9849  * {@link #create}. The parameters are the same.
9850  * @param {Array} data An associative Array of data values keyed by the field name.
9851  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9852  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9853  * not specified an integer id is generated.
9854  */
9855 Roo.data.Record = function(data, id){
9856     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9857     this.data = data;
9858 };
9859
9860 /**
9861  * Generate a constructor for a specific record layout.
9862  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9863  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9864  * Each field definition object may contain the following properties: <ul>
9865  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
9866  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9867  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9868  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9869  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9870  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9871  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9872  * this may be omitted.</p></li>
9873  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9874  * <ul><li>auto (Default, implies no conversion)</li>
9875  * <li>string</li>
9876  * <li>int</li>
9877  * <li>float</li>
9878  * <li>boolean</li>
9879  * <li>date</li></ul></p></li>
9880  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9881  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9882  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9883  * by the Reader into an object that will be stored in the Record. It is passed the
9884  * following parameters:<ul>
9885  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9886  * </ul></p></li>
9887  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9888  * </ul>
9889  * <br>usage:<br><pre><code>
9890 var TopicRecord = Roo.data.Record.create(
9891     {name: 'title', mapping: 'topic_title'},
9892     {name: 'author', mapping: 'username'},
9893     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9894     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9895     {name: 'lastPoster', mapping: 'user2'},
9896     {name: 'excerpt', mapping: 'post_text'}
9897 );
9898
9899 var myNewRecord = new TopicRecord({
9900     title: 'Do my job please',
9901     author: 'noobie',
9902     totalPosts: 1,
9903     lastPost: new Date(),
9904     lastPoster: 'Animal',
9905     excerpt: 'No way dude!'
9906 });
9907 myStore.add(myNewRecord);
9908 </code></pre>
9909  * @method create
9910  * @static
9911  */
9912 Roo.data.Record.create = function(o){
9913     var f = function(){
9914         f.superclass.constructor.apply(this, arguments);
9915     };
9916     Roo.extend(f, Roo.data.Record);
9917     var p = f.prototype;
9918     p.fields = new Roo.util.MixedCollection(false, function(field){
9919         return field.name;
9920     });
9921     for(var i = 0, len = o.length; i < len; i++){
9922         p.fields.add(new Roo.data.Field(o[i]));
9923     }
9924     f.getField = function(name){
9925         return p.fields.get(name);  
9926     };
9927     return f;
9928 };
9929
9930 Roo.data.Record.AUTO_ID = 1000;
9931 Roo.data.Record.EDIT = 'edit';
9932 Roo.data.Record.REJECT = 'reject';
9933 Roo.data.Record.COMMIT = 'commit';
9934
9935 Roo.data.Record.prototype = {
9936     /**
9937      * Readonly flag - true if this record has been modified.
9938      * @type Boolean
9939      */
9940     dirty : false,
9941     editing : false,
9942     error: null,
9943     modified: null,
9944
9945     // private
9946     join : function(store){
9947         this.store = store;
9948     },
9949
9950     /**
9951      * Set the named field to the specified value.
9952      * @param {String} name The name of the field to set.
9953      * @param {Object} value The value to set the field to.
9954      */
9955     set : function(name, value){
9956         if(this.data[name] == value){
9957             return;
9958         }
9959         this.dirty = true;
9960         if(!this.modified){
9961             this.modified = {};
9962         }
9963         if(typeof this.modified[name] == 'undefined'){
9964             this.modified[name] = this.data[name];
9965         }
9966         this.data[name] = value;
9967         if(!this.editing && this.store){
9968             this.store.afterEdit(this);
9969         }       
9970     },
9971
9972     /**
9973      * Get the value of the named field.
9974      * @param {String} name The name of the field to get the value of.
9975      * @return {Object} The value of the field.
9976      */
9977     get : function(name){
9978         return this.data[name]; 
9979     },
9980
9981     // private
9982     beginEdit : function(){
9983         this.editing = true;
9984         this.modified = {}; 
9985     },
9986
9987     // private
9988     cancelEdit : function(){
9989         this.editing = false;
9990         delete this.modified;
9991     },
9992
9993     // private
9994     endEdit : function(){
9995         this.editing = false;
9996         if(this.dirty && this.store){
9997             this.store.afterEdit(this);
9998         }
9999     },
10000
10001     /**
10002      * Usually called by the {@link Roo.data.Store} which owns the Record.
10003      * Rejects all changes made to the Record since either creation, or the last commit operation.
10004      * Modified fields are reverted to their original values.
10005      * <p>
10006      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10007      * of reject operations.
10008      */
10009     reject : function(){
10010         var m = this.modified;
10011         for(var n in m){
10012             if(typeof m[n] != "function"){
10013                 this.data[n] = m[n];
10014             }
10015         }
10016         this.dirty = false;
10017         delete this.modified;
10018         this.editing = false;
10019         if(this.store){
10020             this.store.afterReject(this);
10021         }
10022     },
10023
10024     /**
10025      * Usually called by the {@link Roo.data.Store} which owns the Record.
10026      * Commits all changes made to the Record since either creation, or the last commit operation.
10027      * <p>
10028      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10029      * of commit operations.
10030      */
10031     commit : function(){
10032         this.dirty = false;
10033         delete this.modified;
10034         this.editing = false;
10035         if(this.store){
10036             this.store.afterCommit(this);
10037         }
10038     },
10039
10040     // private
10041     hasError : function(){
10042         return this.error != null;
10043     },
10044
10045     // private
10046     clearError : function(){
10047         this.error = null;
10048     },
10049
10050     /**
10051      * Creates a copy of this record.
10052      * @param {String} id (optional) A new record id if you don't want to use this record's id
10053      * @return {Record}
10054      */
10055     copy : function(newId) {
10056         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10057     }
10058 };/*
10059  * Based on:
10060  * Ext JS Library 1.1.1
10061  * Copyright(c) 2006-2007, Ext JS, LLC.
10062  *
10063  * Originally Released Under LGPL - original licence link has changed is not relivant.
10064  *
10065  * Fork - LGPL
10066  * <script type="text/javascript">
10067  */
10068
10069
10070
10071 /**
10072  * @class Roo.data.Store
10073  * @extends Roo.util.Observable
10074  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10075  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10076  * <p>
10077  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10078  * has no knowledge of the format of the data returned by the Proxy.<br>
10079  * <p>
10080  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10081  * instances from the data object. These records are cached and made available through accessor functions.
10082  * @constructor
10083  * Creates a new Store.
10084  * @param {Object} config A config object containing the objects needed for the Store to access data,
10085  * and read the data into Records.
10086  */
10087 Roo.data.Store = function(config){
10088     this.data = new Roo.util.MixedCollection(false);
10089     this.data.getKey = function(o){
10090         return o.id;
10091     };
10092     this.baseParams = {};
10093     // private
10094     this.paramNames = {
10095         "start" : "start",
10096         "limit" : "limit",
10097         "sort" : "sort",
10098         "dir" : "dir",
10099         "multisort" : "_multisort"
10100     };
10101
10102     if(config && config.data){
10103         this.inlineData = config.data;
10104         delete config.data;
10105     }
10106
10107     Roo.apply(this, config);
10108     
10109     if(this.reader){ // reader passed
10110         this.reader = Roo.factory(this.reader, Roo.data);
10111         this.reader.xmodule = this.xmodule || false;
10112         if(!this.recordType){
10113             this.recordType = this.reader.recordType;
10114         }
10115         if(this.reader.onMetaChange){
10116             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10117         }
10118     }
10119
10120     if(this.recordType){
10121         this.fields = this.recordType.prototype.fields;
10122     }
10123     this.modified = [];
10124
10125     this.addEvents({
10126         /**
10127          * @event datachanged
10128          * Fires when the data cache has changed, and a widget which is using this Store
10129          * as a Record cache should refresh its view.
10130          * @param {Store} this
10131          */
10132         datachanged : true,
10133         /**
10134          * @event metachange
10135          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10136          * @param {Store} this
10137          * @param {Object} meta The JSON metadata
10138          */
10139         metachange : true,
10140         /**
10141          * @event add
10142          * Fires when Records have been added to the Store
10143          * @param {Store} this
10144          * @param {Roo.data.Record[]} records The array of Records added
10145          * @param {Number} index The index at which the record(s) were added
10146          */
10147         add : true,
10148         /**
10149          * @event remove
10150          * Fires when a Record has been removed from the Store
10151          * @param {Store} this
10152          * @param {Roo.data.Record} record The Record that was removed
10153          * @param {Number} index The index at which the record was removed
10154          */
10155         remove : true,
10156         /**
10157          * @event update
10158          * Fires when a Record has been updated
10159          * @param {Store} this
10160          * @param {Roo.data.Record} record The Record that was updated
10161          * @param {String} operation The update operation being performed.  Value may be one of:
10162          * <pre><code>
10163  Roo.data.Record.EDIT
10164  Roo.data.Record.REJECT
10165  Roo.data.Record.COMMIT
10166          * </code></pre>
10167          */
10168         update : true,
10169         /**
10170          * @event clear
10171          * Fires when the data cache has been cleared.
10172          * @param {Store} this
10173          */
10174         clear : true,
10175         /**
10176          * @event beforeload
10177          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10178          * the load action will be canceled.
10179          * @param {Store} this
10180          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10181          */
10182         beforeload : true,
10183         /**
10184          * @event beforeloadadd
10185          * Fires after a new set of Records has been loaded.
10186          * @param {Store} this
10187          * @param {Roo.data.Record[]} records The Records that were loaded
10188          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10189          */
10190         beforeloadadd : true,
10191         /**
10192          * @event load
10193          * Fires after a new set of Records has been loaded, before they are added to the store.
10194          * @param {Store} this
10195          * @param {Roo.data.Record[]} records The Records that were loaded
10196          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10197          * @params {Object} return from reader
10198          */
10199         load : true,
10200         /**
10201          * @event loadexception
10202          * Fires if an exception occurs in the Proxy during loading.
10203          * Called with the signature of the Proxy's "loadexception" event.
10204          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10205          * 
10206          * @param {Proxy} 
10207          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10208          * @param {Object} load options 
10209          * @param {Object} jsonData from your request (normally this contains the Exception)
10210          */
10211         loadexception : true
10212     });
10213     
10214     if(this.proxy){
10215         this.proxy = Roo.factory(this.proxy, Roo.data);
10216         this.proxy.xmodule = this.xmodule || false;
10217         this.relayEvents(this.proxy,  ["loadexception"]);
10218     }
10219     this.sortToggle = {};
10220     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10221
10222     Roo.data.Store.superclass.constructor.call(this);
10223
10224     if(this.inlineData){
10225         this.loadData(this.inlineData);
10226         delete this.inlineData;
10227     }
10228 };
10229
10230 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10231      /**
10232     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10233     * without a remote query - used by combo/forms at present.
10234     */
10235     
10236     /**
10237     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10238     */
10239     /**
10240     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10241     */
10242     /**
10243     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10244     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10245     */
10246     /**
10247     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10248     * on any HTTP request
10249     */
10250     /**
10251     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10252     */
10253     /**
10254     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10255     */
10256     multiSort: false,
10257     /**
10258     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10259     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10260     */
10261     remoteSort : false,
10262
10263     /**
10264     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10265      * loaded or when a record is removed. (defaults to false).
10266     */
10267     pruneModifiedRecords : false,
10268
10269     // private
10270     lastOptions : null,
10271
10272     /**
10273      * Add Records to the Store and fires the add event.
10274      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10275      */
10276     add : function(records){
10277         records = [].concat(records);
10278         for(var i = 0, len = records.length; i < len; i++){
10279             records[i].join(this);
10280         }
10281         var index = this.data.length;
10282         this.data.addAll(records);
10283         this.fireEvent("add", this, records, index);
10284     },
10285
10286     /**
10287      * Remove a Record from the Store and fires the remove event.
10288      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10289      */
10290     remove : function(record){
10291         var index = this.data.indexOf(record);
10292         this.data.removeAt(index);
10293         if(this.pruneModifiedRecords){
10294             this.modified.remove(record);
10295         }
10296         this.fireEvent("remove", this, record, index);
10297     },
10298
10299     /**
10300      * Remove all Records from the Store and fires the clear event.
10301      */
10302     removeAll : function(){
10303         this.data.clear();
10304         if(this.pruneModifiedRecords){
10305             this.modified = [];
10306         }
10307         this.fireEvent("clear", this);
10308     },
10309
10310     /**
10311      * Inserts Records to the Store at the given index and fires the add event.
10312      * @param {Number} index The start index at which to insert the passed Records.
10313      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10314      */
10315     insert : function(index, records){
10316         records = [].concat(records);
10317         for(var i = 0, len = records.length; i < len; i++){
10318             this.data.insert(index, records[i]);
10319             records[i].join(this);
10320         }
10321         this.fireEvent("add", this, records, index);
10322     },
10323
10324     /**
10325      * Get the index within the cache of the passed Record.
10326      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10327      * @return {Number} The index of the passed Record. Returns -1 if not found.
10328      */
10329     indexOf : function(record){
10330         return this.data.indexOf(record);
10331     },
10332
10333     /**
10334      * Get the index within the cache of the Record with the passed id.
10335      * @param {String} id The id of the Record to find.
10336      * @return {Number} The index of the Record. Returns -1 if not found.
10337      */
10338     indexOfId : function(id){
10339         return this.data.indexOfKey(id);
10340     },
10341
10342     /**
10343      * Get the Record with the specified id.
10344      * @param {String} id The id of the Record to find.
10345      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10346      */
10347     getById : function(id){
10348         return this.data.key(id);
10349     },
10350
10351     /**
10352      * Get the Record at the specified index.
10353      * @param {Number} index The index of the Record to find.
10354      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10355      */
10356     getAt : function(index){
10357         return this.data.itemAt(index);
10358     },
10359
10360     /**
10361      * Returns a range of Records between specified indices.
10362      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10363      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10364      * @return {Roo.data.Record[]} An array of Records
10365      */
10366     getRange : function(start, end){
10367         return this.data.getRange(start, end);
10368     },
10369
10370     // private
10371     storeOptions : function(o){
10372         o = Roo.apply({}, o);
10373         delete o.callback;
10374         delete o.scope;
10375         this.lastOptions = o;
10376     },
10377
10378     /**
10379      * Loads the Record cache from the configured Proxy using the configured Reader.
10380      * <p>
10381      * If using remote paging, then the first load call must specify the <em>start</em>
10382      * and <em>limit</em> properties in the options.params property to establish the initial
10383      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10384      * <p>
10385      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10386      * and this call will return before the new data has been loaded. Perform any post-processing
10387      * in a callback function, or in a "load" event handler.</strong>
10388      * <p>
10389      * @param {Object} options An object containing properties which control loading options:<ul>
10390      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10391      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10392      * passed the following arguments:<ul>
10393      * <li>r : Roo.data.Record[]</li>
10394      * <li>options: Options object from the load call</li>
10395      * <li>success: Boolean success indicator</li></ul></li>
10396      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10397      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10398      * </ul>
10399      */
10400     load : function(options){
10401         options = options || {};
10402         if(this.fireEvent("beforeload", this, options) !== false){
10403             this.storeOptions(options);
10404             var p = Roo.apply(options.params || {}, this.baseParams);
10405             // if meta was not loaded from remote source.. try requesting it.
10406             if (!this.reader.metaFromRemote) {
10407                 p._requestMeta = 1;
10408             }
10409             if(this.sortInfo && this.remoteSort){
10410                 var pn = this.paramNames;
10411                 p[pn["sort"]] = this.sortInfo.field;
10412                 p[pn["dir"]] = this.sortInfo.direction;
10413             }
10414             if (this.multiSort) {
10415                 var pn = this.paramNames;
10416                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10417             }
10418             
10419             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10420         }
10421     },
10422
10423     /**
10424      * Reloads the Record cache from the configured Proxy using the configured Reader and
10425      * the options from the last load operation performed.
10426      * @param {Object} options (optional) An object containing properties which may override the options
10427      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10428      * the most recently used options are reused).
10429      */
10430     reload : function(options){
10431         this.load(Roo.applyIf(options||{}, this.lastOptions));
10432     },
10433
10434     // private
10435     // Called as a callback by the Reader during a load operation.
10436     loadRecords : function(o, options, success){
10437         if(!o || success === false){
10438             if(success !== false){
10439                 this.fireEvent("load", this, [], options, o);
10440             }
10441             if(options.callback){
10442                 options.callback.call(options.scope || this, [], options, false);
10443             }
10444             return;
10445         }
10446         // if data returned failure - throw an exception.
10447         if (o.success === false) {
10448             // show a message if no listener is registered.
10449             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10450                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10451             }
10452             // loadmask wil be hooked into this..
10453             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10454             return;
10455         }
10456         var r = o.records, t = o.totalRecords || r.length;
10457         
10458         this.fireEvent("beforeloadadd", this, r, options, o);
10459         
10460         if(!options || options.add !== true){
10461             if(this.pruneModifiedRecords){
10462                 this.modified = [];
10463             }
10464             for(var i = 0, len = r.length; i < len; i++){
10465                 r[i].join(this);
10466             }
10467             if(this.snapshot){
10468                 this.data = this.snapshot;
10469                 delete this.snapshot;
10470             }
10471             this.data.clear();
10472             this.data.addAll(r);
10473             this.totalLength = t;
10474             this.applySort();
10475             this.fireEvent("datachanged", this);
10476         }else{
10477             this.totalLength = Math.max(t, this.data.length+r.length);
10478             this.add(r);
10479         }
10480         this.fireEvent("load", this, r, options, o);
10481         if(options.callback){
10482             options.callback.call(options.scope || this, r, options, true);
10483         }
10484     },
10485
10486
10487     /**
10488      * Loads data from a passed data block. A Reader which understands the format of the data
10489      * must have been configured in the constructor.
10490      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10491      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10492      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10493      */
10494     loadData : function(o, append){
10495         var r = this.reader.readRecords(o);
10496         this.loadRecords(r, {add: append}, true);
10497     },
10498
10499     /**
10500      * Gets the number of cached records.
10501      * <p>
10502      * <em>If using paging, this may not be the total size of the dataset. If the data object
10503      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10504      * the data set size</em>
10505      */
10506     getCount : function(){
10507         return this.data.length || 0;
10508     },
10509
10510     /**
10511      * Gets the total number of records in the dataset as returned by the server.
10512      * <p>
10513      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10514      * the dataset size</em>
10515      */
10516     getTotalCount : function(){
10517         return this.totalLength || 0;
10518     },
10519
10520     /**
10521      * Returns the sort state of the Store as an object with two properties:
10522      * <pre><code>
10523  field {String} The name of the field by which the Records are sorted
10524  direction {String} The sort order, "ASC" or "DESC"
10525      * </code></pre>
10526      */
10527     getSortState : function(){
10528         return this.sortInfo;
10529     },
10530
10531     // private
10532     applySort : function(){
10533         if(this.sortInfo && !this.remoteSort){
10534             var s = this.sortInfo, f = s.field;
10535             var st = this.fields.get(f).sortType;
10536             var fn = function(r1, r2){
10537                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10538                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10539             };
10540             this.data.sort(s.direction, fn);
10541             if(this.snapshot && this.snapshot != this.data){
10542                 this.snapshot.sort(s.direction, fn);
10543             }
10544         }
10545     },
10546
10547     /**
10548      * Sets the default sort column and order to be used by the next load operation.
10549      * @param {String} fieldName The name of the field to sort by.
10550      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10551      */
10552     setDefaultSort : function(field, dir){
10553         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10554     },
10555
10556     /**
10557      * Sort the Records.
10558      * If remote sorting is used, the sort is performed on the server, and the cache is
10559      * reloaded. If local sorting is used, the cache is sorted internally.
10560      * @param {String} fieldName The name of the field to sort by.
10561      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10562      */
10563     sort : function(fieldName, dir){
10564         var f = this.fields.get(fieldName);
10565         if(!dir){
10566             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10567             
10568             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10569                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10570             }else{
10571                 dir = f.sortDir;
10572             }
10573         }
10574         this.sortToggle[f.name] = dir;
10575         this.sortInfo = {field: f.name, direction: dir};
10576         if(!this.remoteSort){
10577             this.applySort();
10578             this.fireEvent("datachanged", this);
10579         }else{
10580             this.load(this.lastOptions);
10581         }
10582     },
10583
10584     /**
10585      * Calls the specified function for each of the Records in the cache.
10586      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10587      * Returning <em>false</em> aborts and exits the iteration.
10588      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10589      */
10590     each : function(fn, scope){
10591         this.data.each(fn, scope);
10592     },
10593
10594     /**
10595      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10596      * (e.g., during paging).
10597      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10598      */
10599     getModifiedRecords : function(){
10600         return this.modified;
10601     },
10602
10603     // private
10604     createFilterFn : function(property, value, anyMatch){
10605         if(!value.exec){ // not a regex
10606             value = String(value);
10607             if(value.length == 0){
10608                 return false;
10609             }
10610             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10611         }
10612         return function(r){
10613             return value.test(r.data[property]);
10614         };
10615     },
10616
10617     /**
10618      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10619      * @param {String} property A field on your records
10620      * @param {Number} start The record index to start at (defaults to 0)
10621      * @param {Number} end The last record index to include (defaults to length - 1)
10622      * @return {Number} The sum
10623      */
10624     sum : function(property, start, end){
10625         var rs = this.data.items, v = 0;
10626         start = start || 0;
10627         end = (end || end === 0) ? end : rs.length-1;
10628
10629         for(var i = start; i <= end; i++){
10630             v += (rs[i].data[property] || 0);
10631         }
10632         return v;
10633     },
10634
10635     /**
10636      * Filter the records by a specified property.
10637      * @param {String} field A field on your records
10638      * @param {String/RegExp} value Either a string that the field
10639      * should start with or a RegExp to test against the field
10640      * @param {Boolean} anyMatch True to match any part not just the beginning
10641      */
10642     filter : function(property, value, anyMatch){
10643         var fn = this.createFilterFn(property, value, anyMatch);
10644         return fn ? this.filterBy(fn) : this.clearFilter();
10645     },
10646
10647     /**
10648      * Filter by a function. The specified function will be called with each
10649      * record in this data source. If the function returns true the record is included,
10650      * otherwise it is filtered.
10651      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10652      * @param {Object} scope (optional) The scope of the function (defaults to this)
10653      */
10654     filterBy : function(fn, scope){
10655         this.snapshot = this.snapshot || this.data;
10656         this.data = this.queryBy(fn, scope||this);
10657         this.fireEvent("datachanged", this);
10658     },
10659
10660     /**
10661      * Query the records by a specified property.
10662      * @param {String} field A field on your records
10663      * @param {String/RegExp} value Either a string that the field
10664      * should start with or a RegExp to test against the field
10665      * @param {Boolean} anyMatch True to match any part not just the beginning
10666      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10667      */
10668     query : function(property, value, anyMatch){
10669         var fn = this.createFilterFn(property, value, anyMatch);
10670         return fn ? this.queryBy(fn) : this.data.clone();
10671     },
10672
10673     /**
10674      * Query by a function. The specified function will be called with each
10675      * record in this data source. If the function returns true the record is included
10676      * in the results.
10677      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10678      * @param {Object} scope (optional) The scope of the function (defaults to this)
10679       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10680      **/
10681     queryBy : function(fn, scope){
10682         var data = this.snapshot || this.data;
10683         return data.filterBy(fn, scope||this);
10684     },
10685
10686     /**
10687      * Collects unique values for a particular dataIndex from this store.
10688      * @param {String} dataIndex The property to collect
10689      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10690      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10691      * @return {Array} An array of the unique values
10692      **/
10693     collect : function(dataIndex, allowNull, bypassFilter){
10694         var d = (bypassFilter === true && this.snapshot) ?
10695                 this.snapshot.items : this.data.items;
10696         var v, sv, r = [], l = {};
10697         for(var i = 0, len = d.length; i < len; i++){
10698             v = d[i].data[dataIndex];
10699             sv = String(v);
10700             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10701                 l[sv] = true;
10702                 r[r.length] = v;
10703             }
10704         }
10705         return r;
10706     },
10707
10708     /**
10709      * Revert to a view of the Record cache with no filtering applied.
10710      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10711      */
10712     clearFilter : function(suppressEvent){
10713         if(this.snapshot && this.snapshot != this.data){
10714             this.data = this.snapshot;
10715             delete this.snapshot;
10716             if(suppressEvent !== true){
10717                 this.fireEvent("datachanged", this);
10718             }
10719         }
10720     },
10721
10722     // private
10723     afterEdit : function(record){
10724         if(this.modified.indexOf(record) == -1){
10725             this.modified.push(record);
10726         }
10727         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10728     },
10729     
10730     // private
10731     afterReject : function(record){
10732         this.modified.remove(record);
10733         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10734     },
10735
10736     // private
10737     afterCommit : function(record){
10738         this.modified.remove(record);
10739         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10740     },
10741
10742     /**
10743      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10744      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10745      */
10746     commitChanges : function(){
10747         var m = this.modified.slice(0);
10748         this.modified = [];
10749         for(var i = 0, len = m.length; i < len; i++){
10750             m[i].commit();
10751         }
10752     },
10753
10754     /**
10755      * Cancel outstanding changes on all changed records.
10756      */
10757     rejectChanges : function(){
10758         var m = this.modified.slice(0);
10759         this.modified = [];
10760         for(var i = 0, len = m.length; i < len; i++){
10761             m[i].reject();
10762         }
10763     },
10764
10765     onMetaChange : function(meta, rtype, o){
10766         this.recordType = rtype;
10767         this.fields = rtype.prototype.fields;
10768         delete this.snapshot;
10769         this.sortInfo = meta.sortInfo || this.sortInfo;
10770         this.modified = [];
10771         this.fireEvent('metachange', this, this.reader.meta);
10772     },
10773     
10774     moveIndex : function(data, type)
10775     {
10776         var index = this.indexOf(data);
10777         
10778         var newIndex = index + type;
10779         
10780         this.remove(data);
10781         
10782         this.insert(newIndex, data);
10783         
10784     }
10785 });/*
10786  * Based on:
10787  * Ext JS Library 1.1.1
10788  * Copyright(c) 2006-2007, Ext JS, LLC.
10789  *
10790  * Originally Released Under LGPL - original licence link has changed is not relivant.
10791  *
10792  * Fork - LGPL
10793  * <script type="text/javascript">
10794  */
10795
10796 /**
10797  * @class Roo.data.SimpleStore
10798  * @extends Roo.data.Store
10799  * Small helper class to make creating Stores from Array data easier.
10800  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10801  * @cfg {Array} fields An array of field definition objects, or field name strings.
10802  * @cfg {Array} data The multi-dimensional array of data
10803  * @constructor
10804  * @param {Object} config
10805  */
10806 Roo.data.SimpleStore = function(config){
10807     Roo.data.SimpleStore.superclass.constructor.call(this, {
10808         isLocal : true,
10809         reader: new Roo.data.ArrayReader({
10810                 id: config.id
10811             },
10812             Roo.data.Record.create(config.fields)
10813         ),
10814         proxy : new Roo.data.MemoryProxy(config.data)
10815     });
10816     this.load();
10817 };
10818 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10819  * Based on:
10820  * Ext JS Library 1.1.1
10821  * Copyright(c) 2006-2007, Ext JS, LLC.
10822  *
10823  * Originally Released Under LGPL - original licence link has changed is not relivant.
10824  *
10825  * Fork - LGPL
10826  * <script type="text/javascript">
10827  */
10828
10829 /**
10830 /**
10831  * @extends Roo.data.Store
10832  * @class Roo.data.JsonStore
10833  * Small helper class to make creating Stores for JSON data easier. <br/>
10834 <pre><code>
10835 var store = new Roo.data.JsonStore({
10836     url: 'get-images.php',
10837     root: 'images',
10838     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10839 });
10840 </code></pre>
10841  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10842  * JsonReader and HttpProxy (unless inline data is provided).</b>
10843  * @cfg {Array} fields An array of field definition objects, or field name strings.
10844  * @constructor
10845  * @param {Object} config
10846  */
10847 Roo.data.JsonStore = function(c){
10848     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10849         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10850         reader: new Roo.data.JsonReader(c, c.fields)
10851     }));
10852 };
10853 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10854  * Based on:
10855  * Ext JS Library 1.1.1
10856  * Copyright(c) 2006-2007, Ext JS, LLC.
10857  *
10858  * Originally Released Under LGPL - original licence link has changed is not relivant.
10859  *
10860  * Fork - LGPL
10861  * <script type="text/javascript">
10862  */
10863
10864  
10865 Roo.data.Field = function(config){
10866     if(typeof config == "string"){
10867         config = {name: config};
10868     }
10869     Roo.apply(this, config);
10870     
10871     if(!this.type){
10872         this.type = "auto";
10873     }
10874     
10875     var st = Roo.data.SortTypes;
10876     // named sortTypes are supported, here we look them up
10877     if(typeof this.sortType == "string"){
10878         this.sortType = st[this.sortType];
10879     }
10880     
10881     // set default sortType for strings and dates
10882     if(!this.sortType){
10883         switch(this.type){
10884             case "string":
10885                 this.sortType = st.asUCString;
10886                 break;
10887             case "date":
10888                 this.sortType = st.asDate;
10889                 break;
10890             default:
10891                 this.sortType = st.none;
10892         }
10893     }
10894
10895     // define once
10896     var stripRe = /[\$,%]/g;
10897
10898     // prebuilt conversion function for this field, instead of
10899     // switching every time we're reading a value
10900     if(!this.convert){
10901         var cv, dateFormat = this.dateFormat;
10902         switch(this.type){
10903             case "":
10904             case "auto":
10905             case undefined:
10906                 cv = function(v){ return v; };
10907                 break;
10908             case "string":
10909                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10910                 break;
10911             case "int":
10912                 cv = function(v){
10913                     return v !== undefined && v !== null && v !== '' ?
10914                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10915                     };
10916                 break;
10917             case "float":
10918                 cv = function(v){
10919                     return v !== undefined && v !== null && v !== '' ?
10920                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10921                     };
10922                 break;
10923             case "bool":
10924             case "boolean":
10925                 cv = function(v){ return v === true || v === "true" || v == 1; };
10926                 break;
10927             case "date":
10928                 cv = function(v){
10929                     if(!v){
10930                         return '';
10931                     }
10932                     if(v instanceof Date){
10933                         return v;
10934                     }
10935                     if(dateFormat){
10936                         if(dateFormat == "timestamp"){
10937                             return new Date(v*1000);
10938                         }
10939                         return Date.parseDate(v, dateFormat);
10940                     }
10941                     var parsed = Date.parse(v);
10942                     return parsed ? new Date(parsed) : null;
10943                 };
10944              break;
10945             
10946         }
10947         this.convert = cv;
10948     }
10949 };
10950
10951 Roo.data.Field.prototype = {
10952     dateFormat: null,
10953     defaultValue: "",
10954     mapping: null,
10955     sortType : null,
10956     sortDir : "ASC"
10957 };/*
10958  * Based on:
10959  * Ext JS Library 1.1.1
10960  * Copyright(c) 2006-2007, Ext JS, LLC.
10961  *
10962  * Originally Released Under LGPL - original licence link has changed is not relivant.
10963  *
10964  * Fork - LGPL
10965  * <script type="text/javascript">
10966  */
10967  
10968 // Base class for reading structured data from a data source.  This class is intended to be
10969 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10970
10971 /**
10972  * @class Roo.data.DataReader
10973  * Base class for reading structured data from a data source.  This class is intended to be
10974  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10975  */
10976
10977 Roo.data.DataReader = function(meta, recordType){
10978     
10979     this.meta = meta;
10980     
10981     this.recordType = recordType instanceof Array ? 
10982         Roo.data.Record.create(recordType) : recordType;
10983 };
10984
10985 Roo.data.DataReader.prototype = {
10986      /**
10987      * Create an empty record
10988      * @param {Object} data (optional) - overlay some values
10989      * @return {Roo.data.Record} record created.
10990      */
10991     newRow :  function(d) {
10992         var da =  {};
10993         this.recordType.prototype.fields.each(function(c) {
10994             switch( c.type) {
10995                 case 'int' : da[c.name] = 0; break;
10996                 case 'date' : da[c.name] = new Date(); break;
10997                 case 'float' : da[c.name] = 0.0; break;
10998                 case 'boolean' : da[c.name] = false; break;
10999                 default : da[c.name] = ""; break;
11000             }
11001             
11002         });
11003         return new this.recordType(Roo.apply(da, d));
11004     }
11005     
11006 };/*
11007  * Based on:
11008  * Ext JS Library 1.1.1
11009  * Copyright(c) 2006-2007, Ext JS, LLC.
11010  *
11011  * Originally Released Under LGPL - original licence link has changed is not relivant.
11012  *
11013  * Fork - LGPL
11014  * <script type="text/javascript">
11015  */
11016
11017 /**
11018  * @class Roo.data.DataProxy
11019  * @extends Roo.data.Observable
11020  * This class is an abstract base class for implementations which provide retrieval of
11021  * unformatted data objects.<br>
11022  * <p>
11023  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11024  * (of the appropriate type which knows how to parse the data object) to provide a block of
11025  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11026  * <p>
11027  * Custom implementations must implement the load method as described in
11028  * {@link Roo.data.HttpProxy#load}.
11029  */
11030 Roo.data.DataProxy = function(){
11031     this.addEvents({
11032         /**
11033          * @event beforeload
11034          * Fires before a network request is made to retrieve a data object.
11035          * @param {Object} This DataProxy object.
11036          * @param {Object} params The params parameter to the load function.
11037          */
11038         beforeload : true,
11039         /**
11040          * @event load
11041          * Fires before the load method's callback is called.
11042          * @param {Object} This DataProxy object.
11043          * @param {Object} o The data object.
11044          * @param {Object} arg The callback argument object passed to the load function.
11045          */
11046         load : true,
11047         /**
11048          * @event loadexception
11049          * Fires if an Exception occurs during data retrieval.
11050          * @param {Object} This DataProxy object.
11051          * @param {Object} o The data object.
11052          * @param {Object} arg The callback argument object passed to the load function.
11053          * @param {Object} e The Exception.
11054          */
11055         loadexception : true
11056     });
11057     Roo.data.DataProxy.superclass.constructor.call(this);
11058 };
11059
11060 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11061
11062     /**
11063      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11064      */
11065 /*
11066  * Based on:
11067  * Ext JS Library 1.1.1
11068  * Copyright(c) 2006-2007, Ext JS, LLC.
11069  *
11070  * Originally Released Under LGPL - original licence link has changed is not relivant.
11071  *
11072  * Fork - LGPL
11073  * <script type="text/javascript">
11074  */
11075 /**
11076  * @class Roo.data.MemoryProxy
11077  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11078  * to the Reader when its load method is called.
11079  * @constructor
11080  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11081  */
11082 Roo.data.MemoryProxy = function(data){
11083     if (data.data) {
11084         data = data.data;
11085     }
11086     Roo.data.MemoryProxy.superclass.constructor.call(this);
11087     this.data = data;
11088 };
11089
11090 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11091     
11092     /**
11093      * Load data from the requested source (in this case an in-memory
11094      * data object passed to the constructor), read the data object into
11095      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11096      * process that block using the passed callback.
11097      * @param {Object} params This parameter is not used by the MemoryProxy class.
11098      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11099      * object into a block of Roo.data.Records.
11100      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11101      * The function must be passed <ul>
11102      * <li>The Record block object</li>
11103      * <li>The "arg" argument from the load function</li>
11104      * <li>A boolean success indicator</li>
11105      * </ul>
11106      * @param {Object} scope The scope in which to call the callback
11107      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11108      */
11109     load : function(params, reader, callback, scope, arg){
11110         params = params || {};
11111         var result;
11112         try {
11113             result = reader.readRecords(this.data);
11114         }catch(e){
11115             this.fireEvent("loadexception", this, arg, null, e);
11116             callback.call(scope, null, arg, false);
11117             return;
11118         }
11119         callback.call(scope, result, arg, true);
11120     },
11121     
11122     // private
11123     update : function(params, records){
11124         
11125     }
11126 });/*
11127  * Based on:
11128  * Ext JS Library 1.1.1
11129  * Copyright(c) 2006-2007, Ext JS, LLC.
11130  *
11131  * Originally Released Under LGPL - original licence link has changed is not relivant.
11132  *
11133  * Fork - LGPL
11134  * <script type="text/javascript">
11135  */
11136 /**
11137  * @class Roo.data.HttpProxy
11138  * @extends Roo.data.DataProxy
11139  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11140  * configured to reference a certain URL.<br><br>
11141  * <p>
11142  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11143  * from which the running page was served.<br><br>
11144  * <p>
11145  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11146  * <p>
11147  * Be aware that to enable the browser to parse an XML document, the server must set
11148  * the Content-Type header in the HTTP response to "text/xml".
11149  * @constructor
11150  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11151  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11152  * will be used to make the request.
11153  */
11154 Roo.data.HttpProxy = function(conn){
11155     Roo.data.HttpProxy.superclass.constructor.call(this);
11156     // is conn a conn config or a real conn?
11157     this.conn = conn;
11158     this.useAjax = !conn || !conn.events;
11159   
11160 };
11161
11162 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11163     // thse are take from connection...
11164     
11165     /**
11166      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11167      */
11168     /**
11169      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11170      * extra parameters to each request made by this object. (defaults to undefined)
11171      */
11172     /**
11173      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11174      *  to each request made by this object. (defaults to undefined)
11175      */
11176     /**
11177      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11178      */
11179     /**
11180      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11181      */
11182      /**
11183      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11184      * @type Boolean
11185      */
11186   
11187
11188     /**
11189      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11190      * @type Boolean
11191      */
11192     /**
11193      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11194      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11195      * a finer-grained basis than the DataProxy events.
11196      */
11197     getConnection : function(){
11198         return this.useAjax ? Roo.Ajax : this.conn;
11199     },
11200
11201     /**
11202      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11203      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11204      * process that block using the passed callback.
11205      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11206      * for the request to the remote server.
11207      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11208      * object into a block of Roo.data.Records.
11209      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11210      * The function must be passed <ul>
11211      * <li>The Record block object</li>
11212      * <li>The "arg" argument from the load function</li>
11213      * <li>A boolean success indicator</li>
11214      * </ul>
11215      * @param {Object} scope The scope in which to call the callback
11216      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11217      */
11218     load : function(params, reader, callback, scope, arg){
11219         if(this.fireEvent("beforeload", this, params) !== false){
11220             var  o = {
11221                 params : params || {},
11222                 request: {
11223                     callback : callback,
11224                     scope : scope,
11225                     arg : arg
11226                 },
11227                 reader: reader,
11228                 callback : this.loadResponse,
11229                 scope: this
11230             };
11231             if(this.useAjax){
11232                 Roo.applyIf(o, this.conn);
11233                 if(this.activeRequest){
11234                     Roo.Ajax.abort(this.activeRequest);
11235                 }
11236                 this.activeRequest = Roo.Ajax.request(o);
11237             }else{
11238                 this.conn.request(o);
11239             }
11240         }else{
11241             callback.call(scope||this, null, arg, false);
11242         }
11243     },
11244
11245     // private
11246     loadResponse : function(o, success, response){
11247         delete this.activeRequest;
11248         if(!success){
11249             this.fireEvent("loadexception", this, o, response);
11250             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11251             return;
11252         }
11253         var result;
11254         try {
11255             result = o.reader.read(response);
11256         }catch(e){
11257             this.fireEvent("loadexception", this, o, response, e);
11258             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11259             return;
11260         }
11261         
11262         this.fireEvent("load", this, o, o.request.arg);
11263         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11264     },
11265
11266     // private
11267     update : function(dataSet){
11268
11269     },
11270
11271     // private
11272     updateResponse : function(dataSet){
11273
11274     }
11275 });/*
11276  * Based on:
11277  * Ext JS Library 1.1.1
11278  * Copyright(c) 2006-2007, Ext JS, LLC.
11279  *
11280  * Originally Released Under LGPL - original licence link has changed is not relivant.
11281  *
11282  * Fork - LGPL
11283  * <script type="text/javascript">
11284  */
11285
11286 /**
11287  * @class Roo.data.ScriptTagProxy
11288  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11289  * other than the originating domain of the running page.<br><br>
11290  * <p>
11291  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
11292  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11293  * <p>
11294  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11295  * source code that is used as the source inside a &lt;script> tag.<br><br>
11296  * <p>
11297  * In order for the browser to process the returned data, the server must wrap the data object
11298  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11299  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11300  * depending on whether the callback name was passed:
11301  * <p>
11302  * <pre><code>
11303 boolean scriptTag = false;
11304 String cb = request.getParameter("callback");
11305 if (cb != null) {
11306     scriptTag = true;
11307     response.setContentType("text/javascript");
11308 } else {
11309     response.setContentType("application/x-json");
11310 }
11311 Writer out = response.getWriter();
11312 if (scriptTag) {
11313     out.write(cb + "(");
11314 }
11315 out.print(dataBlock.toJsonString());
11316 if (scriptTag) {
11317     out.write(");");
11318 }
11319 </pre></code>
11320  *
11321  * @constructor
11322  * @param {Object} config A configuration object.
11323  */
11324 Roo.data.ScriptTagProxy = function(config){
11325     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11326     Roo.apply(this, config);
11327     this.head = document.getElementsByTagName("head")[0];
11328 };
11329
11330 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11331
11332 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11333     /**
11334      * @cfg {String} url The URL from which to request the data object.
11335      */
11336     /**
11337      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11338      */
11339     timeout : 30000,
11340     /**
11341      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11342      * the server the name of the callback function set up by the load call to process the returned data object.
11343      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11344      * javascript output which calls this named function passing the data object as its only parameter.
11345      */
11346     callbackParam : "callback",
11347     /**
11348      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11349      * name to the request.
11350      */
11351     nocache : true,
11352
11353     /**
11354      * Load data from the configured URL, read the data object into
11355      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11356      * process that block using the passed callback.
11357      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11358      * for the request to the remote server.
11359      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11360      * object into a block of Roo.data.Records.
11361      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11362      * The function must be passed <ul>
11363      * <li>The Record block object</li>
11364      * <li>The "arg" argument from the load function</li>
11365      * <li>A boolean success indicator</li>
11366      * </ul>
11367      * @param {Object} scope The scope in which to call the callback
11368      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11369      */
11370     load : function(params, reader, callback, scope, arg){
11371         if(this.fireEvent("beforeload", this, params) !== false){
11372
11373             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11374
11375             var url = this.url;
11376             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11377             if(this.nocache){
11378                 url += "&_dc=" + (new Date().getTime());
11379             }
11380             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11381             var trans = {
11382                 id : transId,
11383                 cb : "stcCallback"+transId,
11384                 scriptId : "stcScript"+transId,
11385                 params : params,
11386                 arg : arg,
11387                 url : url,
11388                 callback : callback,
11389                 scope : scope,
11390                 reader : reader
11391             };
11392             var conn = this;
11393
11394             window[trans.cb] = function(o){
11395                 conn.handleResponse(o, trans);
11396             };
11397
11398             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11399
11400             if(this.autoAbort !== false){
11401                 this.abort();
11402             }
11403
11404             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11405
11406             var script = document.createElement("script");
11407             script.setAttribute("src", url);
11408             script.setAttribute("type", "text/javascript");
11409             script.setAttribute("id", trans.scriptId);
11410             this.head.appendChild(script);
11411
11412             this.trans = trans;
11413         }else{
11414             callback.call(scope||this, null, arg, false);
11415         }
11416     },
11417
11418     // private
11419     isLoading : function(){
11420         return this.trans ? true : false;
11421     },
11422
11423     /**
11424      * Abort the current server request.
11425      */
11426     abort : function(){
11427         if(this.isLoading()){
11428             this.destroyTrans(this.trans);
11429         }
11430     },
11431
11432     // private
11433     destroyTrans : function(trans, isLoaded){
11434         this.head.removeChild(document.getElementById(trans.scriptId));
11435         clearTimeout(trans.timeoutId);
11436         if(isLoaded){
11437             window[trans.cb] = undefined;
11438             try{
11439                 delete window[trans.cb];
11440             }catch(e){}
11441         }else{
11442             // if hasn't been loaded, wait for load to remove it to prevent script error
11443             window[trans.cb] = function(){
11444                 window[trans.cb] = undefined;
11445                 try{
11446                     delete window[trans.cb];
11447                 }catch(e){}
11448             };
11449         }
11450     },
11451
11452     // private
11453     handleResponse : function(o, trans){
11454         this.trans = false;
11455         this.destroyTrans(trans, true);
11456         var result;
11457         try {
11458             result = trans.reader.readRecords(o);
11459         }catch(e){
11460             this.fireEvent("loadexception", this, o, trans.arg, e);
11461             trans.callback.call(trans.scope||window, null, trans.arg, false);
11462             return;
11463         }
11464         this.fireEvent("load", this, o, trans.arg);
11465         trans.callback.call(trans.scope||window, result, trans.arg, true);
11466     },
11467
11468     // private
11469     handleFailure : function(trans){
11470         this.trans = false;
11471         this.destroyTrans(trans, false);
11472         this.fireEvent("loadexception", this, null, trans.arg);
11473         trans.callback.call(trans.scope||window, null, trans.arg, false);
11474     }
11475 });/*
11476  * Based on:
11477  * Ext JS Library 1.1.1
11478  * Copyright(c) 2006-2007, Ext JS, LLC.
11479  *
11480  * Originally Released Under LGPL - original licence link has changed is not relivant.
11481  *
11482  * Fork - LGPL
11483  * <script type="text/javascript">
11484  */
11485
11486 /**
11487  * @class Roo.data.JsonReader
11488  * @extends Roo.data.DataReader
11489  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11490  * based on mappings in a provided Roo.data.Record constructor.
11491  * 
11492  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11493  * in the reply previously. 
11494  * 
11495  * <p>
11496  * Example code:
11497  * <pre><code>
11498 var RecordDef = Roo.data.Record.create([
11499     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11500     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11501 ]);
11502 var myReader = new Roo.data.JsonReader({
11503     totalProperty: "results",    // The property which contains the total dataset size (optional)
11504     root: "rows",                // The property which contains an Array of row objects
11505     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11506 }, RecordDef);
11507 </code></pre>
11508  * <p>
11509  * This would consume a JSON file like this:
11510  * <pre><code>
11511 { 'results': 2, 'rows': [
11512     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11513     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11514 }
11515 </code></pre>
11516  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11517  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11518  * paged from the remote server.
11519  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11520  * @cfg {String} root name of the property which contains the Array of row objects.
11521  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11522  * @cfg {Array} fields Array of field definition objects
11523  * @constructor
11524  * Create a new JsonReader
11525  * @param {Object} meta Metadata configuration options
11526  * @param {Object} recordType Either an Array of field definition objects,
11527  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11528  */
11529 Roo.data.JsonReader = function(meta, recordType){
11530     
11531     meta = meta || {};
11532     // set some defaults:
11533     Roo.applyIf(meta, {
11534         totalProperty: 'total',
11535         successProperty : 'success',
11536         root : 'data',
11537         id : 'id'
11538     });
11539     
11540     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11541 };
11542 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11543     
11544     /**
11545      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11546      * Used by Store query builder to append _requestMeta to params.
11547      * 
11548      */
11549     metaFromRemote : false,
11550     /**
11551      * This method is only used by a DataProxy which has retrieved data from a remote server.
11552      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11553      * @return {Object} data A data block which is used by an Roo.data.Store object as
11554      * a cache of Roo.data.Records.
11555      */
11556     read : function(response){
11557         var json = response.responseText;
11558        
11559         var o = /* eval:var:o */ eval("("+json+")");
11560         if(!o) {
11561             throw {message: "JsonReader.read: Json object not found"};
11562         }
11563         
11564         if(o.metaData){
11565             
11566             delete this.ef;
11567             this.metaFromRemote = true;
11568             this.meta = o.metaData;
11569             this.recordType = Roo.data.Record.create(o.metaData.fields);
11570             this.onMetaChange(this.meta, this.recordType, o);
11571         }
11572         return this.readRecords(o);
11573     },
11574
11575     // private function a store will implement
11576     onMetaChange : function(meta, recordType, o){
11577
11578     },
11579
11580     /**
11581          * @ignore
11582          */
11583     simpleAccess: function(obj, subsc) {
11584         return obj[subsc];
11585     },
11586
11587         /**
11588          * @ignore
11589          */
11590     getJsonAccessor: function(){
11591         var re = /[\[\.]/;
11592         return function(expr) {
11593             try {
11594                 return(re.test(expr))
11595                     ? new Function("obj", "return obj." + expr)
11596                     : function(obj){
11597                         return obj[expr];
11598                     };
11599             } catch(e){}
11600             return Roo.emptyFn;
11601         };
11602     }(),
11603
11604     /**
11605      * Create a data block containing Roo.data.Records from an XML document.
11606      * @param {Object} o An object which contains an Array of row objects in the property specified
11607      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11608      * which contains the total size of the dataset.
11609      * @return {Object} data A data block which is used by an Roo.data.Store object as
11610      * a cache of Roo.data.Records.
11611      */
11612     readRecords : function(o){
11613         /**
11614          * After any data loads, the raw JSON data is available for further custom processing.
11615          * @type Object
11616          */
11617         this.o = o;
11618         var s = this.meta, Record = this.recordType,
11619             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11620
11621 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11622         if (!this.ef) {
11623             if(s.totalProperty) {
11624                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11625                 }
11626                 if(s.successProperty) {
11627                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11628                 }
11629                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11630                 if (s.id) {
11631                         var g = this.getJsonAccessor(s.id);
11632                         this.getId = function(rec) {
11633                                 var r = g(rec);  
11634                                 return (r === undefined || r === "") ? null : r;
11635                         };
11636                 } else {
11637                         this.getId = function(){return null;};
11638                 }
11639             this.ef = [];
11640             for(var jj = 0; jj < fl; jj++){
11641                 f = fi[jj];
11642                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11643                 this.ef[jj] = this.getJsonAccessor(map);
11644             }
11645         }
11646
11647         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11648         if(s.totalProperty){
11649             var vt = parseInt(this.getTotal(o), 10);
11650             if(!isNaN(vt)){
11651                 totalRecords = vt;
11652             }
11653         }
11654         if(s.successProperty){
11655             var vs = this.getSuccess(o);
11656             if(vs === false || vs === 'false'){
11657                 success = false;
11658             }
11659         }
11660         var records = [];
11661         for(var i = 0; i < c; i++){
11662                 var n = root[i];
11663             var values = {};
11664             var id = this.getId(n);
11665             for(var j = 0; j < fl; j++){
11666                 f = fi[j];
11667             var v = this.ef[j](n);
11668             if (!f.convert) {
11669                 Roo.log('missing convert for ' + f.name);
11670                 Roo.log(f);
11671                 continue;
11672             }
11673             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11674             }
11675             var record = new Record(values, id);
11676             record.json = n;
11677             records[i] = record;
11678         }
11679         return {
11680             raw : o,
11681             success : success,
11682             records : records,
11683             totalRecords : totalRecords
11684         };
11685     }
11686 });/*
11687  * Based on:
11688  * Ext JS Library 1.1.1
11689  * Copyright(c) 2006-2007, Ext JS, LLC.
11690  *
11691  * Originally Released Under LGPL - original licence link has changed is not relivant.
11692  *
11693  * Fork - LGPL
11694  * <script type="text/javascript">
11695  */
11696
11697 /**
11698  * @class Roo.data.ArrayReader
11699  * @extends Roo.data.DataReader
11700  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11701  * Each element of that Array represents a row of data fields. The
11702  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11703  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11704  * <p>
11705  * Example code:.
11706  * <pre><code>
11707 var RecordDef = Roo.data.Record.create([
11708     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11709     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11710 ]);
11711 var myReader = new Roo.data.ArrayReader({
11712     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11713 }, RecordDef);
11714 </code></pre>
11715  * <p>
11716  * This would consume an Array like this:
11717  * <pre><code>
11718 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11719   </code></pre>
11720  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11721  * @constructor
11722  * Create a new JsonReader
11723  * @param {Object} meta Metadata configuration options.
11724  * @param {Object} recordType Either an Array of field definition objects
11725  * as specified to {@link Roo.data.Record#create},
11726  * or an {@link Roo.data.Record} object
11727  * created using {@link Roo.data.Record#create}.
11728  */
11729 Roo.data.ArrayReader = function(meta, recordType){
11730     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11731 };
11732
11733 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11734     /**
11735      * Create a data block containing Roo.data.Records from an XML document.
11736      * @param {Object} o An Array of row objects which represents the dataset.
11737      * @return {Object} data A data block which is used by an Roo.data.Store object as
11738      * a cache of Roo.data.Records.
11739      */
11740     readRecords : function(o){
11741         var sid = this.meta ? this.meta.id : null;
11742         var recordType = this.recordType, fields = recordType.prototype.fields;
11743         var records = [];
11744         var root = o;
11745             for(var i = 0; i < root.length; i++){
11746                     var n = root[i];
11747                 var values = {};
11748                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11749                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11750                 var f = fields.items[j];
11751                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11752                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11753                 v = f.convert(v);
11754                 values[f.name] = v;
11755             }
11756                 var record = new recordType(values, id);
11757                 record.json = n;
11758                 records[records.length] = record;
11759             }
11760             return {
11761                 records : records,
11762                 totalRecords : records.length
11763             };
11764     }
11765 });/*
11766  * - LGPL
11767  * * 
11768  */
11769
11770 /**
11771  * @class Roo.bootstrap.ComboBox
11772  * @extends Roo.bootstrap.TriggerField
11773  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11774  * @cfg {Boolean} append (true|false) default false
11775  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11776  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11777  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11778  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11779  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11780  * @cfg {Boolean} animate default true
11781  * @cfg {Boolean} emptyResultText only for touch device
11782  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11783  * @constructor
11784  * Create a new ComboBox.
11785  * @param {Object} config Configuration options
11786  */
11787 Roo.bootstrap.ComboBox = function(config){
11788     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11789     this.addEvents({
11790         /**
11791          * @event expand
11792          * Fires when the dropdown list is expanded
11793              * @param {Roo.bootstrap.ComboBox} combo This combo box
11794              */
11795         'expand' : true,
11796         /**
11797          * @event collapse
11798          * Fires when the dropdown list is collapsed
11799              * @param {Roo.bootstrap.ComboBox} combo This combo box
11800              */
11801         'collapse' : true,
11802         /**
11803          * @event beforeselect
11804          * Fires before a list item is selected. Return false to cancel the selection.
11805              * @param {Roo.bootstrap.ComboBox} combo This combo box
11806              * @param {Roo.data.Record} record The data record returned from the underlying store
11807              * @param {Number} index The index of the selected item in the dropdown list
11808              */
11809         'beforeselect' : true,
11810         /**
11811          * @event select
11812          * Fires when a list item is selected
11813              * @param {Roo.bootstrap.ComboBox} combo This combo box
11814              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11815              * @param {Number} index The index of the selected item in the dropdown list
11816              */
11817         'select' : true,
11818         /**
11819          * @event beforequery
11820          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11821          * The event object passed has these properties:
11822              * @param {Roo.bootstrap.ComboBox} combo This combo box
11823              * @param {String} query The query
11824              * @param {Boolean} forceAll true to force "all" query
11825              * @param {Boolean} cancel true to cancel the query
11826              * @param {Object} e The query event object
11827              */
11828         'beforequery': true,
11829          /**
11830          * @event add
11831          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11832              * @param {Roo.bootstrap.ComboBox} combo This combo box
11833              */
11834         'add' : true,
11835         /**
11836          * @event edit
11837          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11838              * @param {Roo.bootstrap.ComboBox} combo This combo box
11839              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11840              */
11841         'edit' : true,
11842         /**
11843          * @event remove
11844          * Fires when the remove value from the combobox array
11845              * @param {Roo.bootstrap.ComboBox} combo This combo box
11846              */
11847         'remove' : true,
11848         /**
11849          * @event afterremove
11850          * Fires when the remove value from the combobox array
11851              * @param {Roo.bootstrap.ComboBox} combo This combo box
11852              */
11853         'afterremove' : true,
11854         /**
11855          * @event specialfilter
11856          * Fires when specialfilter
11857             * @param {Roo.bootstrap.ComboBox} combo This combo box
11858             */
11859         'specialfilter' : true,
11860         /**
11861          * @event tick
11862          * Fires when tick the element
11863             * @param {Roo.bootstrap.ComboBox} combo This combo box
11864             */
11865         'tick' : true,
11866         /**
11867          * @event touchviewdisplay
11868          * Fires when touch view require special display (default is using displayField)
11869             * @param {Roo.bootstrap.ComboBox} combo This combo box
11870             * @param {Object} cfg set html .
11871             */
11872         'touchviewdisplay' : true
11873         
11874     });
11875     
11876     this.item = [];
11877     this.tickItems = [];
11878     
11879     this.selectedIndex = -1;
11880     if(this.mode == 'local'){
11881         if(config.queryDelay === undefined){
11882             this.queryDelay = 10;
11883         }
11884         if(config.minChars === undefined){
11885             this.minChars = 0;
11886         }
11887     }
11888 };
11889
11890 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11891      
11892     /**
11893      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11894      * rendering into an Roo.Editor, defaults to false)
11895      */
11896     /**
11897      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11898      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11899      */
11900     /**
11901      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11902      */
11903     /**
11904      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11905      * the dropdown list (defaults to undefined, with no header element)
11906      */
11907
11908      /**
11909      * @cfg {String/Roo.Template} tpl The template to use to render the output
11910      */
11911      
11912      /**
11913      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11914      */
11915     listWidth: undefined,
11916     /**
11917      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11918      * mode = 'remote' or 'text' if mode = 'local')
11919      */
11920     displayField: undefined,
11921     
11922     /**
11923      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11924      * mode = 'remote' or 'value' if mode = 'local'). 
11925      * Note: use of a valueField requires the user make a selection
11926      * in order for a value to be mapped.
11927      */
11928     valueField: undefined,
11929     
11930     
11931     /**
11932      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11933      * field's data value (defaults to the underlying DOM element's name)
11934      */
11935     hiddenName: undefined,
11936     /**
11937      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11938      */
11939     listClass: '',
11940     /**
11941      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11942      */
11943     selectedClass: 'active',
11944     
11945     /**
11946      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11947      */
11948     shadow:'sides',
11949     /**
11950      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11951      * anchor positions (defaults to 'tl-bl')
11952      */
11953     listAlign: 'tl-bl?',
11954     /**
11955      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11956      */
11957     maxHeight: 300,
11958     /**
11959      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11960      * query specified by the allQuery config option (defaults to 'query')
11961      */
11962     triggerAction: 'query',
11963     /**
11964      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11965      * (defaults to 4, does not apply if editable = false)
11966      */
11967     minChars : 4,
11968     /**
11969      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11970      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11971      */
11972     typeAhead: false,
11973     /**
11974      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11975      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11976      */
11977     queryDelay: 500,
11978     /**
11979      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11980      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11981      */
11982     pageSize: 0,
11983     /**
11984      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11985      * when editable = true (defaults to false)
11986      */
11987     selectOnFocus:false,
11988     /**
11989      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11990      */
11991     queryParam: 'query',
11992     /**
11993      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11994      * when mode = 'remote' (defaults to 'Loading...')
11995      */
11996     loadingText: 'Loading...',
11997     /**
11998      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11999      */
12000     resizable: false,
12001     /**
12002      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12003      */
12004     handleHeight : 8,
12005     /**
12006      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12007      * traditional select (defaults to true)
12008      */
12009     editable: true,
12010     /**
12011      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12012      */
12013     allQuery: '',
12014     /**
12015      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12016      */
12017     mode: 'remote',
12018     /**
12019      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12020      * listWidth has a higher value)
12021      */
12022     minListWidth : 70,
12023     /**
12024      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12025      * allow the user to set arbitrary text into the field (defaults to false)
12026      */
12027     forceSelection:false,
12028     /**
12029      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12030      * if typeAhead = true (defaults to 250)
12031      */
12032     typeAheadDelay : 250,
12033     /**
12034      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12035      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12036      */
12037     valueNotFoundText : undefined,
12038     /**
12039      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12040      */
12041     blockFocus : false,
12042     
12043     /**
12044      * @cfg {Boolean} disableClear Disable showing of clear button.
12045      */
12046     disableClear : false,
12047     /**
12048      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12049      */
12050     alwaysQuery : false,
12051     
12052     /**
12053      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12054      */
12055     multiple : false,
12056     
12057     /**
12058      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12059      */
12060     invalidClass : "has-warning",
12061     
12062     /**
12063      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12064      */
12065     validClass : "has-success",
12066     
12067     /**
12068      * @cfg {Boolean} specialFilter (true|false) special filter default false
12069      */
12070     specialFilter : false,
12071     
12072     /**
12073      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12074      */
12075     mobileTouchView : true,
12076     
12077     //private
12078     addicon : false,
12079     editicon: false,
12080     
12081     page: 0,
12082     hasQuery: false,
12083     append: false,
12084     loadNext: false,
12085     autoFocus : true,
12086     tickable : false,
12087     btnPosition : 'right',
12088     triggerList : true,
12089     showToggleBtn : true,
12090     animate : true,
12091     emptyResultText: 'Empty',
12092     triggerText : 'Select',
12093     
12094     // element that contains real text value.. (when hidden is used..)
12095     
12096     getAutoCreate : function()
12097     {
12098         var cfg = false;
12099         
12100         /*
12101          * Touch Devices
12102          */
12103         
12104         if(Roo.isTouch && this.mobileTouchView){
12105             cfg = this.getAutoCreateTouchView();
12106             return cfg;;
12107         }
12108         
12109         /*
12110          *  Normal ComboBox
12111          */
12112         if(!this.tickable){
12113             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12114             return cfg;
12115         }
12116         
12117         /*
12118          *  ComboBox with tickable selections
12119          */
12120              
12121         var align = this.labelAlign || this.parentLabelAlign();
12122         
12123         cfg = {
12124             cls : 'form-group roo-combobox-tickable' //input-group
12125         };
12126         
12127         var buttons = {
12128             tag : 'div',
12129             cls : 'tickable-buttons',
12130             cn : [
12131                 {
12132                     tag : 'button',
12133                     type : 'button',
12134                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12135                     html : this.triggerText
12136                 },
12137                 {
12138                     tag : 'button',
12139                     type : 'button',
12140                     name : 'ok',
12141                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12142                     html : 'Done'
12143                 },
12144                 {
12145                     tag : 'button',
12146                     type : 'button',
12147                     name : 'cancel',
12148                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12149                     html : 'Cancel'
12150                 }
12151             ]
12152         };
12153         
12154         if(this.editable){
12155             buttons.cn.unshift({
12156                 tag: 'input',
12157                 cls: 'roo-select2-search-field-input'
12158             });
12159         }
12160         
12161         var _this = this;
12162         
12163         Roo.each(buttons.cn, function(c){
12164             if (_this.size) {
12165                 c.cls += ' btn-' + _this.size;
12166             }
12167
12168             if (_this.disabled) {
12169                 c.disabled = true;
12170             }
12171         });
12172         
12173         var box = {
12174             tag: 'div',
12175             cn: [
12176                 {
12177                     tag: 'input',
12178                     type : 'hidden',
12179                     cls: 'form-hidden-field'
12180                 },
12181                 {
12182                     tag: 'ul',
12183                     cls: 'roo-select2-choices',
12184                     cn:[
12185                         {
12186                             tag: 'li',
12187                             cls: 'roo-select2-search-field',
12188                             cn: [
12189
12190                                 buttons
12191                             ]
12192                         }
12193                     ]
12194                 }
12195             ]
12196         };
12197         
12198         var combobox = {
12199             cls: 'roo-select2-container input-group roo-select2-container-multi',
12200             cn: [
12201                 box
12202 //                {
12203 //                    tag: 'ul',
12204 //                    cls: 'typeahead typeahead-long dropdown-menu',
12205 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12206 //                }
12207             ]
12208         };
12209         
12210         if(this.hasFeedback && !this.allowBlank){
12211             
12212             var feedback = {
12213                 tag: 'span',
12214                 cls: 'glyphicon form-control-feedback'
12215             };
12216
12217             combobox.cn.push(feedback);
12218         }
12219         
12220         if (align ==='left' && this.fieldLabel.length) {
12221             
12222 //                Roo.log("left and has label");
12223                 cfg.cn = [
12224                     
12225                     {
12226                         tag: 'label',
12227                         'for' :  id,
12228                         cls : 'control-label col-sm-' + this.labelWidth,
12229                         html : this.fieldLabel
12230                         
12231                     },
12232                     {
12233                         cls : "col-sm-" + (12 - this.labelWidth), 
12234                         cn: [
12235                             combobox
12236                         ]
12237                     }
12238                     
12239                 ];
12240         } else if ( this.fieldLabel.length) {
12241 //                Roo.log(" label");
12242                  cfg.cn = [
12243                    
12244                     {
12245                         tag: 'label',
12246                         //cls : 'input-group-addon',
12247                         html : this.fieldLabel
12248                         
12249                     },
12250                     
12251                     combobox
12252                     
12253                 ];
12254
12255         } else {
12256             
12257 //                Roo.log(" no label && no align");
12258                 cfg = combobox
12259                      
12260                 
12261         }
12262          
12263         var settings=this;
12264         ['xs','sm','md','lg'].map(function(size){
12265             if (settings[size]) {
12266                 cfg.cls += ' col-' + size + '-' + settings[size];
12267             }
12268         });
12269         
12270         return cfg;
12271         
12272     },
12273     
12274     _initEventsCalled : false,
12275     
12276     // private
12277     initEvents: function()
12278     {
12279         
12280         if (this._initEventsCalled) { // as we call render... prevent looping...
12281             return;
12282         }
12283         this._initEventsCalled = true;
12284         
12285         if (!this.store) {
12286             throw "can not find store for combo";
12287         }
12288         
12289         this.store = Roo.factory(this.store, Roo.data);
12290         
12291         // if we are building from html. then this element is so complex, that we can not really
12292         // use the rendered HTML.
12293         // so we have to trash and replace the previous code.
12294         if (Roo.XComponent.build_from_html) {
12295             
12296             // remove this element....
12297             var e = this.el.dom, k=0;
12298             while (e ) { e = e.previousSibling;  ++k;}
12299
12300             this.el.remove();
12301             
12302             this.el=false;
12303             this.rendered = false;
12304             
12305             this.render(this.parent().getChildContainer(true), k);
12306             
12307             
12308             
12309         }
12310         
12311         
12312         /*
12313          * Touch Devices
12314          */
12315         
12316         if(Roo.isTouch && this.mobileTouchView){
12317             this.initTouchView();
12318             return;
12319         }
12320         
12321         if(this.tickable){
12322             this.initTickableEvents();
12323             return;
12324         }
12325         
12326         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12327         
12328         if(this.hiddenName){
12329             
12330             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12331             
12332             this.hiddenField.dom.value =
12333                 this.hiddenValue !== undefined ? this.hiddenValue :
12334                 this.value !== undefined ? this.value : '';
12335
12336             // prevent input submission
12337             this.el.dom.removeAttribute('name');
12338             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12339              
12340              
12341         }
12342         //if(Roo.isGecko){
12343         //    this.el.dom.setAttribute('autocomplete', 'off');
12344         //}
12345         
12346         var cls = 'x-combo-list';
12347         
12348         //this.list = new Roo.Layer({
12349         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12350         //});
12351         
12352         var _this = this;
12353         
12354         (function(){
12355             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12356             _this.list.setWidth(lw);
12357         }).defer(100);
12358         
12359         this.list.on('mouseover', this.onViewOver, this);
12360         this.list.on('mousemove', this.onViewMove, this);
12361         
12362         this.list.on('scroll', this.onViewScroll, this);
12363         
12364         /*
12365         this.list.swallowEvent('mousewheel');
12366         this.assetHeight = 0;
12367
12368         if(this.title){
12369             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12370             this.assetHeight += this.header.getHeight();
12371         }
12372
12373         this.innerList = this.list.createChild({cls:cls+'-inner'});
12374         this.innerList.on('mouseover', this.onViewOver, this);
12375         this.innerList.on('mousemove', this.onViewMove, this);
12376         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12377         
12378         if(this.allowBlank && !this.pageSize && !this.disableClear){
12379             this.footer = this.list.createChild({cls:cls+'-ft'});
12380             this.pageTb = new Roo.Toolbar(this.footer);
12381            
12382         }
12383         if(this.pageSize){
12384             this.footer = this.list.createChild({cls:cls+'-ft'});
12385             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12386                     {pageSize: this.pageSize});
12387             
12388         }
12389         
12390         if (this.pageTb && this.allowBlank && !this.disableClear) {
12391             var _this = this;
12392             this.pageTb.add(new Roo.Toolbar.Fill(), {
12393                 cls: 'x-btn-icon x-btn-clear',
12394                 text: '&#160;',
12395                 handler: function()
12396                 {
12397                     _this.collapse();
12398                     _this.clearValue();
12399                     _this.onSelect(false, -1);
12400                 }
12401             });
12402         }
12403         if (this.footer) {
12404             this.assetHeight += this.footer.getHeight();
12405         }
12406         */
12407             
12408         if(!this.tpl){
12409             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12410         }
12411
12412         this.view = new Roo.View(this.list, this.tpl, {
12413             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12414         });
12415         //this.view.wrapEl.setDisplayed(false);
12416         this.view.on('click', this.onViewClick, this);
12417         
12418         
12419         
12420         this.store.on('beforeload', this.onBeforeLoad, this);
12421         this.store.on('load', this.onLoad, this);
12422         this.store.on('loadexception', this.onLoadException, this);
12423         /*
12424         if(this.resizable){
12425             this.resizer = new Roo.Resizable(this.list,  {
12426                pinned:true, handles:'se'
12427             });
12428             this.resizer.on('resize', function(r, w, h){
12429                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12430                 this.listWidth = w;
12431                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12432                 this.restrictHeight();
12433             }, this);
12434             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12435         }
12436         */
12437         if(!this.editable){
12438             this.editable = true;
12439             this.setEditable(false);
12440         }
12441         
12442         /*
12443         
12444         if (typeof(this.events.add.listeners) != 'undefined') {
12445             
12446             this.addicon = this.wrap.createChild(
12447                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12448        
12449             this.addicon.on('click', function(e) {
12450                 this.fireEvent('add', this);
12451             }, this);
12452         }
12453         if (typeof(this.events.edit.listeners) != 'undefined') {
12454             
12455             this.editicon = this.wrap.createChild(
12456                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12457             if (this.addicon) {
12458                 this.editicon.setStyle('margin-left', '40px');
12459             }
12460             this.editicon.on('click', function(e) {
12461                 
12462                 // we fire even  if inothing is selected..
12463                 this.fireEvent('edit', this, this.lastData );
12464                 
12465             }, this);
12466         }
12467         */
12468         
12469         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12470             "up" : function(e){
12471                 this.inKeyMode = true;
12472                 this.selectPrev();
12473             },
12474
12475             "down" : function(e){
12476                 if(!this.isExpanded()){
12477                     this.onTriggerClick();
12478                 }else{
12479                     this.inKeyMode = true;
12480                     this.selectNext();
12481                 }
12482             },
12483
12484             "enter" : function(e){
12485 //                this.onViewClick();
12486                 //return true;
12487                 this.collapse();
12488                 
12489                 if(this.fireEvent("specialkey", this, e)){
12490                     this.onViewClick(false);
12491                 }
12492                 
12493                 return true;
12494             },
12495
12496             "esc" : function(e){
12497                 this.collapse();
12498             },
12499
12500             "tab" : function(e){
12501                 this.collapse();
12502                 
12503                 if(this.fireEvent("specialkey", this, e)){
12504                     this.onViewClick(false);
12505                 }
12506                 
12507                 return true;
12508             },
12509
12510             scope : this,
12511
12512             doRelay : function(foo, bar, hname){
12513                 if(hname == 'down' || this.scope.isExpanded()){
12514                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12515                 }
12516                 return true;
12517             },
12518
12519             forceKeyDown: true
12520         });
12521         
12522         
12523         this.queryDelay = Math.max(this.queryDelay || 10,
12524                 this.mode == 'local' ? 10 : 250);
12525         
12526         
12527         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12528         
12529         if(this.typeAhead){
12530             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12531         }
12532         if(this.editable !== false){
12533             this.inputEl().on("keyup", this.onKeyUp, this);
12534         }
12535         if(this.forceSelection){
12536             this.inputEl().on('blur', this.doForce, this);
12537         }
12538         
12539         if(this.multiple){
12540             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12541             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12542         }
12543     },
12544     
12545     initTickableEvents: function()
12546     {   
12547         this.createList();
12548         
12549         if(this.hiddenName){
12550             
12551             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12552             
12553             this.hiddenField.dom.value =
12554                 this.hiddenValue !== undefined ? this.hiddenValue :
12555                 this.value !== undefined ? this.value : '';
12556
12557             // prevent input submission
12558             this.el.dom.removeAttribute('name');
12559             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12560              
12561              
12562         }
12563         
12564 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12565         
12566         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12567         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12568         if(this.triggerList){
12569             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12570         }
12571          
12572         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12573         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12574         
12575         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12576         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12577         
12578         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12579         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12580         
12581         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12582         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12583         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12584         
12585         this.okBtn.hide();
12586         this.cancelBtn.hide();
12587         
12588         var _this = this;
12589         
12590         (function(){
12591             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12592             _this.list.setWidth(lw);
12593         }).defer(100);
12594         
12595         this.list.on('mouseover', this.onViewOver, this);
12596         this.list.on('mousemove', this.onViewMove, this);
12597         
12598         this.list.on('scroll', this.onViewScroll, this);
12599         
12600         if(!this.tpl){
12601             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12602         }
12603
12604         this.view = new Roo.View(this.list, this.tpl, {
12605             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12606         });
12607         
12608         //this.view.wrapEl.setDisplayed(false);
12609         this.view.on('click', this.onViewClick, this);
12610         
12611         
12612         
12613         this.store.on('beforeload', this.onBeforeLoad, this);
12614         this.store.on('load', this.onLoad, this);
12615         this.store.on('loadexception', this.onLoadException, this);
12616         
12617         if(this.editable){
12618             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12619                 "up" : function(e){
12620                     this.inKeyMode = true;
12621                     this.selectPrev();
12622                 },
12623
12624                 "down" : function(e){
12625                     this.inKeyMode = true;
12626                     this.selectNext();
12627                 },
12628
12629                 "enter" : function(e){
12630                     if(this.fireEvent("specialkey", this, e)){
12631                         this.onViewClick(false);
12632                     }
12633                     
12634                     return true;
12635                 },
12636
12637                 "esc" : function(e){
12638                     this.onTickableFooterButtonClick(e, false, false);
12639                 },
12640
12641                 "tab" : function(e){
12642                     this.fireEvent("specialkey", this, e);
12643                     
12644                     this.onTickableFooterButtonClick(e, false, false);
12645                     
12646                     return true;
12647                 },
12648
12649                 scope : this,
12650
12651                 doRelay : function(e, fn, key){
12652                     if(this.scope.isExpanded()){
12653                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12654                     }
12655                     return true;
12656                 },
12657
12658                 forceKeyDown: true
12659             });
12660         }
12661         
12662         this.queryDelay = Math.max(this.queryDelay || 10,
12663                 this.mode == 'local' ? 10 : 250);
12664         
12665         
12666         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12667         
12668         if(this.typeAhead){
12669             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12670         }
12671         
12672         if(this.editable !== false){
12673             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12674         }
12675         
12676     },
12677
12678     onDestroy : function(){
12679         if(this.view){
12680             this.view.setStore(null);
12681             this.view.el.removeAllListeners();
12682             this.view.el.remove();
12683             this.view.purgeListeners();
12684         }
12685         if(this.list){
12686             this.list.dom.innerHTML  = '';
12687         }
12688         
12689         if(this.store){
12690             this.store.un('beforeload', this.onBeforeLoad, this);
12691             this.store.un('load', this.onLoad, this);
12692             this.store.un('loadexception', this.onLoadException, this);
12693         }
12694         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12695     },
12696
12697     // private
12698     fireKey : function(e){
12699         if(e.isNavKeyPress() && !this.list.isVisible()){
12700             this.fireEvent("specialkey", this, e);
12701         }
12702     },
12703
12704     // private
12705     onResize: function(w, h){
12706 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12707 //        
12708 //        if(typeof w != 'number'){
12709 //            // we do not handle it!?!?
12710 //            return;
12711 //        }
12712 //        var tw = this.trigger.getWidth();
12713 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12714 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12715 //        var x = w - tw;
12716 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12717 //            
12718 //        //this.trigger.setStyle('left', x+'px');
12719 //        
12720 //        if(this.list && this.listWidth === undefined){
12721 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12722 //            this.list.setWidth(lw);
12723 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12724 //        }
12725         
12726     
12727         
12728     },
12729
12730     /**
12731      * Allow or prevent the user from directly editing the field text.  If false is passed,
12732      * the user will only be able to select from the items defined in the dropdown list.  This method
12733      * is the runtime equivalent of setting the 'editable' config option at config time.
12734      * @param {Boolean} value True to allow the user to directly edit the field text
12735      */
12736     setEditable : function(value){
12737         if(value == this.editable){
12738             return;
12739         }
12740         this.editable = value;
12741         if(!value){
12742             this.inputEl().dom.setAttribute('readOnly', true);
12743             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12744             this.inputEl().addClass('x-combo-noedit');
12745         }else{
12746             this.inputEl().dom.setAttribute('readOnly', false);
12747             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12748             this.inputEl().removeClass('x-combo-noedit');
12749         }
12750     },
12751
12752     // private
12753     
12754     onBeforeLoad : function(combo,opts){
12755         if(!this.hasFocus){
12756             return;
12757         }
12758          if (!opts.add) {
12759             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12760          }
12761         this.restrictHeight();
12762         this.selectedIndex = -1;
12763     },
12764
12765     // private
12766     onLoad : function(){
12767         
12768         this.hasQuery = false;
12769         
12770         if(!this.hasFocus){
12771             return;
12772         }
12773         
12774         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12775             this.loading.hide();
12776         }
12777              
12778         if(this.store.getCount() > 0){
12779             this.expand();
12780             this.restrictHeight();
12781             if(this.lastQuery == this.allQuery){
12782                 if(this.editable && !this.tickable){
12783                     this.inputEl().dom.select();
12784                 }
12785                 
12786                 if(
12787                     !this.selectByValue(this.value, true) &&
12788                     this.autoFocus && 
12789                     (
12790                         !this.store.lastOptions ||
12791                         typeof(this.store.lastOptions.add) == 'undefined' || 
12792                         this.store.lastOptions.add != true
12793                     )
12794                 ){
12795                     this.select(0, true);
12796                 }
12797             }else{
12798                 if(this.autoFocus){
12799                     this.selectNext();
12800                 }
12801                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12802                     this.taTask.delay(this.typeAheadDelay);
12803                 }
12804             }
12805         }else{
12806             this.onEmptyResults();
12807         }
12808         
12809         //this.el.focus();
12810     },
12811     // private
12812     onLoadException : function()
12813     {
12814         this.hasQuery = false;
12815         
12816         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12817             this.loading.hide();
12818         }
12819         
12820         if(this.tickable && this.editable){
12821             return;
12822         }
12823         
12824         this.collapse();
12825         // only causes errors at present
12826         //Roo.log(this.store.reader.jsonData);
12827         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12828             // fixme
12829             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12830         //}
12831         
12832         
12833     },
12834     // private
12835     onTypeAhead : function(){
12836         if(this.store.getCount() > 0){
12837             var r = this.store.getAt(0);
12838             var newValue = r.data[this.displayField];
12839             var len = newValue.length;
12840             var selStart = this.getRawValue().length;
12841             
12842             if(selStart != len){
12843                 this.setRawValue(newValue);
12844                 this.selectText(selStart, newValue.length);
12845             }
12846         }
12847     },
12848
12849     // private
12850     onSelect : function(record, index){
12851         
12852         if(this.fireEvent('beforeselect', this, record, index) !== false){
12853         
12854             this.setFromData(index > -1 ? record.data : false);
12855             
12856             this.collapse();
12857             this.fireEvent('select', this, record, index);
12858         }
12859     },
12860
12861     /**
12862      * Returns the currently selected field value or empty string if no value is set.
12863      * @return {String} value The selected value
12864      */
12865     getValue : function(){
12866         
12867         if(this.multiple){
12868             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12869         }
12870         
12871         if(this.valueField){
12872             return typeof this.value != 'undefined' ? this.value : '';
12873         }else{
12874             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12875         }
12876     },
12877
12878     /**
12879      * Clears any text/value currently set in the field
12880      */
12881     clearValue : function(){
12882         if(this.hiddenField){
12883             this.hiddenField.dom.value = '';
12884         }
12885         this.value = '';
12886         this.setRawValue('');
12887         this.lastSelectionText = '';
12888         this.lastData = false;
12889         
12890         var close = this.closeTriggerEl();
12891         
12892         if(close){
12893             close.hide();
12894         }
12895         
12896     },
12897
12898     /**
12899      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12900      * will be displayed in the field.  If the value does not match the data value of an existing item,
12901      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12902      * Otherwise the field will be blank (although the value will still be set).
12903      * @param {String} value The value to match
12904      */
12905     setValue : function(v){
12906         if(this.multiple){
12907             this.syncValue();
12908             return;
12909         }
12910         
12911         var text = v;
12912         if(this.valueField){
12913             var r = this.findRecord(this.valueField, v);
12914             if(r){
12915                 text = r.data[this.displayField];
12916             }else if(this.valueNotFoundText !== undefined){
12917                 text = this.valueNotFoundText;
12918             }
12919         }
12920         this.lastSelectionText = text;
12921         if(this.hiddenField){
12922             this.hiddenField.dom.value = v;
12923         }
12924         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12925         this.value = v;
12926         
12927         var close = this.closeTriggerEl();
12928         
12929         if(close){
12930             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12931         }
12932     },
12933     /**
12934      * @property {Object} the last set data for the element
12935      */
12936     
12937     lastData : false,
12938     /**
12939      * Sets the value of the field based on a object which is related to the record format for the store.
12940      * @param {Object} value the value to set as. or false on reset?
12941      */
12942     setFromData : function(o){
12943         
12944         if(this.multiple){
12945             this.addItem(o);
12946             return;
12947         }
12948             
12949         var dv = ''; // display value
12950         var vv = ''; // value value..
12951         this.lastData = o;
12952         if (this.displayField) {
12953             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12954         } else {
12955             // this is an error condition!!!
12956             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12957         }
12958         
12959         if(this.valueField){
12960             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12961         }
12962         
12963         var close = this.closeTriggerEl();
12964         
12965         if(close){
12966             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12967         }
12968         
12969         if(this.hiddenField){
12970             this.hiddenField.dom.value = vv;
12971             
12972             this.lastSelectionText = dv;
12973             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12974             this.value = vv;
12975             return;
12976         }
12977         // no hidden field.. - we store the value in 'value', but still display
12978         // display field!!!!
12979         this.lastSelectionText = dv;
12980         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12981         this.value = vv;
12982         
12983         
12984         
12985     },
12986     // private
12987     reset : function(){
12988         // overridden so that last data is reset..
12989         
12990         if(this.multiple){
12991             this.clearItem();
12992             return;
12993         }
12994         
12995         this.setValue(this.originalValue);
12996         this.clearInvalid();
12997         this.lastData = false;
12998         if (this.view) {
12999             this.view.clearSelections();
13000         }
13001     },
13002     // private
13003     findRecord : function(prop, value){
13004         var record;
13005         if(this.store.getCount() > 0){
13006             this.store.each(function(r){
13007                 if(r.data[prop] == value){
13008                     record = r;
13009                     return false;
13010                 }
13011                 return true;
13012             });
13013         }
13014         return record;
13015     },
13016     
13017     getName: function()
13018     {
13019         // returns hidden if it's set..
13020         if (!this.rendered) {return ''};
13021         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13022         
13023     },
13024     // private
13025     onViewMove : function(e, t){
13026         this.inKeyMode = false;
13027     },
13028
13029     // private
13030     onViewOver : function(e, t){
13031         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13032             return;
13033         }
13034         var item = this.view.findItemFromChild(t);
13035         
13036         if(item){
13037             var index = this.view.indexOf(item);
13038             this.select(index, false);
13039         }
13040     },
13041
13042     // private
13043     onViewClick : function(view, doFocus, el, e)
13044     {
13045         var index = this.view.getSelectedIndexes()[0];
13046         
13047         var r = this.store.getAt(index);
13048         
13049         if(this.tickable){
13050             
13051             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13052                 return;
13053             }
13054             
13055             var rm = false;
13056             var _this = this;
13057             
13058             Roo.each(this.tickItems, function(v,k){
13059                 
13060                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13061                     Roo.log(v);
13062                     _this.tickItems.splice(k, 1);
13063                     
13064                     if(typeof(e) == 'undefined' && view == false){
13065                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13066                     }
13067                     
13068                     rm = true;
13069                     return;
13070                 }
13071             });
13072             
13073             if(rm){
13074                 return;
13075             }
13076             
13077             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13078                 this.tickItems.push(r.data);
13079             }
13080             
13081             if(typeof(e) == 'undefined' && view == false){
13082                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13083             }
13084                     
13085             return;
13086         }
13087         
13088         if(r){
13089             this.onSelect(r, index);
13090         }
13091         if(doFocus !== false && !this.blockFocus){
13092             this.inputEl().focus();
13093         }
13094     },
13095
13096     // private
13097     restrictHeight : function(){
13098         //this.innerList.dom.style.height = '';
13099         //var inner = this.innerList.dom;
13100         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13101         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13102         //this.list.beginUpdate();
13103         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13104         this.list.alignTo(this.inputEl(), this.listAlign);
13105         this.list.alignTo(this.inputEl(), this.listAlign);
13106         //this.list.endUpdate();
13107     },
13108
13109     // private
13110     onEmptyResults : function(){
13111         
13112         if(this.tickable && this.editable){
13113             this.restrictHeight();
13114             return;
13115         }
13116         
13117         this.collapse();
13118     },
13119
13120     /**
13121      * Returns true if the dropdown list is expanded, else false.
13122      */
13123     isExpanded : function(){
13124         return this.list.isVisible();
13125     },
13126
13127     /**
13128      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13129      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13130      * @param {String} value The data value of the item to select
13131      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13132      * selected item if it is not currently in view (defaults to true)
13133      * @return {Boolean} True if the value matched an item in the list, else false
13134      */
13135     selectByValue : function(v, scrollIntoView){
13136         if(v !== undefined && v !== null){
13137             var r = this.findRecord(this.valueField || this.displayField, v);
13138             if(r){
13139                 this.select(this.store.indexOf(r), scrollIntoView);
13140                 return true;
13141             }
13142         }
13143         return false;
13144     },
13145
13146     /**
13147      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13148      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13149      * @param {Number} index The zero-based index of the list item to select
13150      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13151      * selected item if it is not currently in view (defaults to true)
13152      */
13153     select : function(index, scrollIntoView){
13154         this.selectedIndex = index;
13155         this.view.select(index);
13156         if(scrollIntoView !== false){
13157             var el = this.view.getNode(index);
13158             /*
13159              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13160              */
13161             if(el){
13162                 this.list.scrollChildIntoView(el, false);
13163             }
13164         }
13165     },
13166
13167     // private
13168     selectNext : function(){
13169         var ct = this.store.getCount();
13170         if(ct > 0){
13171             if(this.selectedIndex == -1){
13172                 this.select(0);
13173             }else if(this.selectedIndex < ct-1){
13174                 this.select(this.selectedIndex+1);
13175             }
13176         }
13177     },
13178
13179     // private
13180     selectPrev : function(){
13181         var ct = this.store.getCount();
13182         if(ct > 0){
13183             if(this.selectedIndex == -1){
13184                 this.select(0);
13185             }else if(this.selectedIndex != 0){
13186                 this.select(this.selectedIndex-1);
13187             }
13188         }
13189     },
13190
13191     // private
13192     onKeyUp : function(e){
13193         if(this.editable !== false && !e.isSpecialKey()){
13194             this.lastKey = e.getKey();
13195             this.dqTask.delay(this.queryDelay);
13196         }
13197     },
13198
13199     // private
13200     validateBlur : function(){
13201         return !this.list || !this.list.isVisible();   
13202     },
13203
13204     // private
13205     initQuery : function(){
13206         
13207         var v = this.getRawValue();
13208         
13209         if(this.tickable && this.editable){
13210             v = this.tickableInputEl().getValue();
13211         }
13212         
13213         this.doQuery(v);
13214     },
13215
13216     // private
13217     doForce : function(){
13218         if(this.inputEl().dom.value.length > 0){
13219             this.inputEl().dom.value =
13220                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13221              
13222         }
13223     },
13224
13225     /**
13226      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13227      * query allowing the query action to be canceled if needed.
13228      * @param {String} query The SQL query to execute
13229      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13230      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13231      * saved in the current store (defaults to false)
13232      */
13233     doQuery : function(q, forceAll){
13234         
13235         if(q === undefined || q === null){
13236             q = '';
13237         }
13238         var qe = {
13239             query: q,
13240             forceAll: forceAll,
13241             combo: this,
13242             cancel:false
13243         };
13244         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13245             return false;
13246         }
13247         q = qe.query;
13248         
13249         forceAll = qe.forceAll;
13250         if(forceAll === true || (q.length >= this.minChars)){
13251             
13252             this.hasQuery = true;
13253             
13254             if(this.lastQuery != q || this.alwaysQuery){
13255                 this.lastQuery = q;
13256                 if(this.mode == 'local'){
13257                     this.selectedIndex = -1;
13258                     if(forceAll){
13259                         this.store.clearFilter();
13260                     }else{
13261                         
13262                         if(this.specialFilter){
13263                             this.fireEvent('specialfilter', this);
13264                             this.onLoad();
13265                             return;
13266                         }
13267                         
13268                         this.store.filter(this.displayField, q);
13269                     }
13270                     
13271                     this.store.fireEvent("datachanged", this.store);
13272                     
13273                     this.onLoad();
13274                     
13275                     
13276                 }else{
13277                     
13278                     this.store.baseParams[this.queryParam] = q;
13279                     
13280                     var options = {params : this.getParams(q)};
13281                     
13282                     if(this.loadNext){
13283                         options.add = true;
13284                         options.params.start = this.page * this.pageSize;
13285                     }
13286                     
13287                     this.store.load(options);
13288                     
13289                     /*
13290                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13291                      *  we should expand the list on onLoad
13292                      *  so command out it
13293                      */
13294 //                    this.expand();
13295                 }
13296             }else{
13297                 this.selectedIndex = -1;
13298                 this.onLoad();   
13299             }
13300         }
13301         
13302         this.loadNext = false;
13303     },
13304     
13305     // private
13306     getParams : function(q){
13307         var p = {};
13308         //p[this.queryParam] = q;
13309         
13310         if(this.pageSize){
13311             p.start = 0;
13312             p.limit = this.pageSize;
13313         }
13314         return p;
13315     },
13316
13317     /**
13318      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13319      */
13320     collapse : function(){
13321         if(!this.isExpanded()){
13322             return;
13323         }
13324         
13325         this.list.hide();
13326         
13327         if(this.tickable){
13328             this.hasFocus = false;
13329             this.okBtn.hide();
13330             this.cancelBtn.hide();
13331             this.trigger.show();
13332             
13333             if(this.editable){
13334                 this.tickableInputEl().dom.value = '';
13335                 this.tickableInputEl().blur();
13336             }
13337             
13338         }
13339         
13340         Roo.get(document).un('mousedown', this.collapseIf, this);
13341         Roo.get(document).un('mousewheel', this.collapseIf, this);
13342         if (!this.editable) {
13343             Roo.get(document).un('keydown', this.listKeyPress, this);
13344         }
13345         this.fireEvent('collapse', this);
13346     },
13347
13348     // private
13349     collapseIf : function(e){
13350         var in_combo  = e.within(this.el);
13351         var in_list =  e.within(this.list);
13352         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13353         
13354         if (in_combo || in_list || is_list) {
13355             //e.stopPropagation();
13356             return;
13357         }
13358         
13359         if(this.tickable){
13360             this.onTickableFooterButtonClick(e, false, false);
13361         }
13362
13363         this.collapse();
13364         
13365     },
13366
13367     /**
13368      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13369      */
13370     expand : function(){
13371        
13372         if(this.isExpanded() || !this.hasFocus){
13373             return;
13374         }
13375         
13376         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13377         this.list.setWidth(lw);
13378         
13379         
13380          Roo.log('expand');
13381         
13382         this.list.show();
13383         
13384         this.restrictHeight();
13385         
13386         if(this.tickable){
13387             
13388             this.tickItems = Roo.apply([], this.item);
13389             
13390             this.okBtn.show();
13391             this.cancelBtn.show();
13392             this.trigger.hide();
13393             
13394             if(this.editable){
13395                 this.tickableInputEl().focus();
13396             }
13397             
13398         }
13399         
13400         Roo.get(document).on('mousedown', this.collapseIf, this);
13401         Roo.get(document).on('mousewheel', this.collapseIf, this);
13402         if (!this.editable) {
13403             Roo.get(document).on('keydown', this.listKeyPress, this);
13404         }
13405         
13406         this.fireEvent('expand', this);
13407     },
13408
13409     // private
13410     // Implements the default empty TriggerField.onTriggerClick function
13411     onTriggerClick : function(e)
13412     {
13413         Roo.log('trigger click');
13414         
13415         if(this.disabled || !this.triggerList){
13416             return;
13417         }
13418         
13419         this.page = 0;
13420         this.loadNext = false;
13421         
13422         if(this.isExpanded()){
13423             this.collapse();
13424             if (!this.blockFocus) {
13425                 this.inputEl().focus();
13426             }
13427             
13428         }else {
13429             this.hasFocus = true;
13430             if(this.triggerAction == 'all') {
13431                 this.doQuery(this.allQuery, true);
13432             } else {
13433                 this.doQuery(this.getRawValue());
13434             }
13435             if (!this.blockFocus) {
13436                 this.inputEl().focus();
13437             }
13438         }
13439     },
13440     
13441     onTickableTriggerClick : function(e)
13442     {
13443         if(this.disabled){
13444             return;
13445         }
13446         
13447         this.page = 0;
13448         this.loadNext = false;
13449         this.hasFocus = true;
13450         
13451         if(this.triggerAction == 'all') {
13452             this.doQuery(this.allQuery, true);
13453         } else {
13454             this.doQuery(this.getRawValue());
13455         }
13456     },
13457     
13458     onSearchFieldClick : function(e)
13459     {
13460         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13461             this.onTickableFooterButtonClick(e, false, false);
13462             return;
13463         }
13464         
13465         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13466             return;
13467         }
13468         
13469         this.page = 0;
13470         this.loadNext = false;
13471         this.hasFocus = true;
13472         
13473         if(this.triggerAction == 'all') {
13474             this.doQuery(this.allQuery, true);
13475         } else {
13476             this.doQuery(this.getRawValue());
13477         }
13478     },
13479     
13480     listKeyPress : function(e)
13481     {
13482         //Roo.log('listkeypress');
13483         // scroll to first matching element based on key pres..
13484         if (e.isSpecialKey()) {
13485             return false;
13486         }
13487         var k = String.fromCharCode(e.getKey()).toUpperCase();
13488         //Roo.log(k);
13489         var match  = false;
13490         var csel = this.view.getSelectedNodes();
13491         var cselitem = false;
13492         if (csel.length) {
13493             var ix = this.view.indexOf(csel[0]);
13494             cselitem  = this.store.getAt(ix);
13495             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13496                 cselitem = false;
13497             }
13498             
13499         }
13500         
13501         this.store.each(function(v) { 
13502             if (cselitem) {
13503                 // start at existing selection.
13504                 if (cselitem.id == v.id) {
13505                     cselitem = false;
13506                 }
13507                 return true;
13508             }
13509                 
13510             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13511                 match = this.store.indexOf(v);
13512                 return false;
13513             }
13514             return true;
13515         }, this);
13516         
13517         if (match === false) {
13518             return true; // no more action?
13519         }
13520         // scroll to?
13521         this.view.select(match);
13522         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13523         sn.scrollIntoView(sn.dom.parentNode, false);
13524     },
13525     
13526     onViewScroll : function(e, t){
13527         
13528         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13529             return;
13530         }
13531         
13532         this.hasQuery = true;
13533         
13534         this.loading = this.list.select('.loading', true).first();
13535         
13536         if(this.loading === null){
13537             this.list.createChild({
13538                 tag: 'div',
13539                 cls: 'loading roo-select2-more-results roo-select2-active',
13540                 html: 'Loading more results...'
13541             });
13542             
13543             this.loading = this.list.select('.loading', true).first();
13544             
13545             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13546             
13547             this.loading.hide();
13548         }
13549         
13550         this.loading.show();
13551         
13552         var _combo = this;
13553         
13554         this.page++;
13555         this.loadNext = true;
13556         
13557         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13558         
13559         return;
13560     },
13561     
13562     addItem : function(o)
13563     {   
13564         var dv = ''; // display value
13565         
13566         if (this.displayField) {
13567             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13568         } else {
13569             // this is an error condition!!!
13570             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13571         }
13572         
13573         if(!dv.length){
13574             return;
13575         }
13576         
13577         var choice = this.choices.createChild({
13578             tag: 'li',
13579             cls: 'roo-select2-search-choice',
13580             cn: [
13581                 {
13582                     tag: 'div',
13583                     html: dv
13584                 },
13585                 {
13586                     tag: 'a',
13587                     href: '#',
13588                     cls: 'roo-select2-search-choice-close',
13589                     tabindex: '-1'
13590                 }
13591             ]
13592             
13593         }, this.searchField);
13594         
13595         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13596         
13597         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13598         
13599         this.item.push(o);
13600         
13601         this.lastData = o;
13602         
13603         this.syncValue();
13604         
13605         this.inputEl().dom.value = '';
13606         
13607         this.validate();
13608     },
13609     
13610     onRemoveItem : function(e, _self, o)
13611     {
13612         e.preventDefault();
13613         
13614         this.lastItem = Roo.apply([], this.item);
13615         
13616         var index = this.item.indexOf(o.data) * 1;
13617         
13618         if( index < 0){
13619             Roo.log('not this item?!');
13620             return;
13621         }
13622         
13623         this.item.splice(index, 1);
13624         o.item.remove();
13625         
13626         this.syncValue();
13627         
13628         this.fireEvent('remove', this, e);
13629         
13630         this.validate();
13631         
13632     },
13633     
13634     syncValue : function()
13635     {
13636         if(!this.item.length){
13637             this.clearValue();
13638             return;
13639         }
13640             
13641         var value = [];
13642         var _this = this;
13643         Roo.each(this.item, function(i){
13644             if(_this.valueField){
13645                 value.push(i[_this.valueField]);
13646                 return;
13647             }
13648
13649             value.push(i);
13650         });
13651
13652         this.value = value.join(',');
13653
13654         if(this.hiddenField){
13655             this.hiddenField.dom.value = this.value;
13656         }
13657         
13658         this.store.fireEvent("datachanged", this.store);
13659     },
13660     
13661     clearItem : function()
13662     {
13663         if(!this.multiple){
13664             return;
13665         }
13666         
13667         this.item = [];
13668         
13669         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13670            c.remove();
13671         });
13672         
13673         this.syncValue();
13674         
13675         this.validate();
13676         
13677         if(this.tickable && !Roo.isTouch){
13678             this.view.refresh();
13679         }
13680     },
13681     
13682     inputEl: function ()
13683     {
13684         if(Roo.isTouch && this.mobileTouchView){
13685             return this.el.select('input.form-control',true).first();
13686         }
13687         
13688         if(this.tickable){
13689             return this.searchField;
13690         }
13691         
13692         return this.el.select('input.form-control',true).first();
13693     },
13694     
13695     
13696     onTickableFooterButtonClick : function(e, btn, el)
13697     {
13698         e.preventDefault();
13699         
13700         this.lastItem = Roo.apply([], this.item);
13701         
13702         if(btn && btn.name == 'cancel'){
13703             this.tickItems = Roo.apply([], this.item);
13704             this.collapse();
13705             return;
13706         }
13707         
13708         this.clearItem();
13709         
13710         var _this = this;
13711         
13712         Roo.each(this.tickItems, function(o){
13713             _this.addItem(o);
13714         });
13715         
13716         this.collapse();
13717         
13718     },
13719     
13720     validate : function()
13721     {
13722         var v = this.getRawValue();
13723         
13724         if(this.multiple){
13725             v = this.getValue();
13726         }
13727         
13728         if(this.disabled || this.allowBlank || v.length){
13729             this.markValid();
13730             return true;
13731         }
13732         
13733         this.markInvalid();
13734         return false;
13735     },
13736     
13737     tickableInputEl : function()
13738     {
13739         if(!this.tickable || !this.editable){
13740             return this.inputEl();
13741         }
13742         
13743         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13744     },
13745     
13746     
13747     getAutoCreateTouchView : function()
13748     {
13749         var id = Roo.id();
13750         
13751         var cfg = {
13752             cls: 'form-group' //input-group
13753         };
13754         
13755         var input =  {
13756             tag: 'input',
13757             id : id,
13758             type : this.inputType,
13759             cls : 'form-control x-combo-noedit',
13760             autocomplete: 'new-password',
13761             placeholder : this.placeholder || '',
13762             readonly : true
13763         };
13764         
13765         if (this.name) {
13766             input.name = this.name;
13767         }
13768         
13769         if (this.size) {
13770             input.cls += ' input-' + this.size;
13771         }
13772         
13773         if (this.disabled) {
13774             input.disabled = true;
13775         }
13776         
13777         var inputblock = {
13778             cls : '',
13779             cn : [
13780                 input
13781             ]
13782         };
13783         
13784         if(this.before){
13785             inputblock.cls += ' input-group';
13786             
13787             inputblock.cn.unshift({
13788                 tag :'span',
13789                 cls : 'input-group-addon',
13790                 html : this.before
13791             });
13792         }
13793         
13794         if(this.removable && !this.multiple){
13795             inputblock.cls += ' roo-removable';
13796             
13797             inputblock.cn.push({
13798                 tag: 'button',
13799                 html : 'x',
13800                 cls : 'roo-combo-removable-btn close'
13801             });
13802         }
13803
13804         if(this.hasFeedback && !this.allowBlank){
13805             
13806             inputblock.cls += ' has-feedback';
13807             
13808             inputblock.cn.push({
13809                 tag: 'span',
13810                 cls: 'glyphicon form-control-feedback'
13811             });
13812             
13813         }
13814         
13815         if (this.after) {
13816             
13817             inputblock.cls += (this.before) ? '' : ' input-group';
13818             
13819             inputblock.cn.push({
13820                 tag :'span',
13821                 cls : 'input-group-addon',
13822                 html : this.after
13823             });
13824         }
13825
13826         var box = {
13827             tag: 'div',
13828             cn: [
13829                 {
13830                     tag: 'input',
13831                     type : 'hidden',
13832                     cls: 'form-hidden-field'
13833                 },
13834                 inputblock
13835             ]
13836             
13837         };
13838         
13839         if(this.multiple){
13840             box = {
13841                 tag: 'div',
13842                 cn: [
13843                     {
13844                         tag: 'input',
13845                         type : 'hidden',
13846                         cls: 'form-hidden-field'
13847                     },
13848                     {
13849                         tag: 'ul',
13850                         cls: 'roo-select2-choices',
13851                         cn:[
13852                             {
13853                                 tag: 'li',
13854                                 cls: 'roo-select2-search-field',
13855                                 cn: [
13856
13857                                     inputblock
13858                                 ]
13859                             }
13860                         ]
13861                     }
13862                 ]
13863             }
13864         };
13865         
13866         var combobox = {
13867             cls: 'roo-select2-container input-group',
13868             cn: [
13869                 box
13870             ]
13871         };
13872         
13873         if(this.multiple){
13874             combobox.cls += ' roo-select2-container-multi';
13875         }
13876         
13877         var align = this.labelAlign || this.parentLabelAlign();
13878         
13879         cfg.cn = combobox;
13880         
13881         if(this.fieldLabel.length){
13882             
13883             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13884             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13885             
13886             cfg.cn = [
13887                 {
13888                     tag: 'label',
13889                     cls : 'control-label ' + lw,
13890                     html : this.fieldLabel
13891
13892                 },
13893                 {
13894                     cls : cw, 
13895                     cn: [
13896                         combobox
13897                     ]
13898                 }
13899             ];
13900         }
13901         
13902         var settings = this;
13903         
13904         ['xs','sm','md','lg'].map(function(size){
13905             if (settings[size]) {
13906                 cfg.cls += ' col-' + size + '-' + settings[size];
13907             }
13908         });
13909         
13910         return cfg;
13911     },
13912     
13913     initTouchView : function()
13914     {
13915         this.renderTouchView();
13916         
13917         this.touchViewEl.on('scroll', function(){
13918             this.el.dom.scrollTop = 0;
13919         }, this);
13920         
13921         this.originalValue = this.getValue();
13922         
13923         this.inputEl().on("click", this.showTouchView, this);
13924         
13925         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13926         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13927         
13928         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13929         
13930         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13931         this.store.on('load', this.onTouchViewLoad, this);
13932         this.store.on('loadexception', this.onTouchViewLoadException, this);
13933         
13934         if(this.hiddenName){
13935             
13936             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13937             
13938             this.hiddenField.dom.value =
13939                 this.hiddenValue !== undefined ? this.hiddenValue :
13940                 this.value !== undefined ? this.value : '';
13941         
13942             this.el.dom.removeAttribute('name');
13943             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13944         }
13945         
13946         if(this.multiple){
13947             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13948             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13949         }
13950         
13951         if(this.removable && !this.multiple){
13952             var close = this.closeTriggerEl();
13953             if(close){
13954                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13955                 close.on('click', this.removeBtnClick, this, close);
13956             }
13957         }
13958         /*
13959          * fix the bug in Safari iOS8
13960          */
13961         this.inputEl().on("focus", function(e){
13962             document.activeElement.blur();
13963         }, this);
13964         
13965         return;
13966         
13967         
13968     },
13969     
13970     renderTouchView : function()
13971     {
13972         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13973         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13974         
13975         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13976         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13977         
13978         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13979         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13980         this.touchViewBodyEl.setStyle('overflow', 'auto');
13981         
13982         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13983         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13984         
13985         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13986         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13987         
13988     },
13989     
13990     showTouchView : function()
13991     {
13992         if(this.disabled){
13993             return;
13994         }
13995         
13996         this.touchViewHeaderEl.hide();
13997
13998         if(this.fieldLabel.length){
13999             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
14000             this.touchViewHeaderEl.show();
14001         }
14002
14003         this.touchViewEl.show();
14004
14005         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14006         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14007
14008         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14009
14010         if(this.fieldLabel.length){
14011             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14012         }
14013         
14014         this.touchViewBodyEl.setHeight(bodyHeight);
14015
14016         if(this.animate){
14017             var _this = this;
14018             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14019         }else{
14020             this.touchViewEl.addClass('in');
14021         }
14022
14023         this.doTouchViewQuery();
14024         
14025     },
14026     
14027     hideTouchView : function()
14028     {
14029         this.touchViewEl.removeClass('in');
14030
14031         if(this.animate){
14032             var _this = this;
14033             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14034         }else{
14035             this.touchViewEl.setStyle('display', 'none');
14036         }
14037         
14038     },
14039     
14040     setTouchViewValue : function()
14041     {
14042         if(this.multiple){
14043             this.clearItem();
14044         
14045             var _this = this;
14046
14047             Roo.each(this.tickItems, function(o){
14048                 this.addItem(o);
14049             }, this);
14050         }
14051         
14052         this.hideTouchView();
14053     },
14054     
14055     doTouchViewQuery : function()
14056     {
14057         var qe = {
14058             query: '',
14059             forceAll: true,
14060             combo: this,
14061             cancel:false
14062         };
14063         
14064         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14065             return false;
14066         }
14067         
14068         if(!this.alwaysQuery || this.mode == 'local'){
14069             this.onTouchViewLoad();
14070             return;
14071         }
14072         
14073         this.store.load();
14074     },
14075     
14076     onTouchViewBeforeLoad : function(combo,opts)
14077     {
14078         return;
14079     },
14080
14081     // private
14082     onTouchViewLoad : function()
14083     {
14084         if(this.store.getCount() < 1){
14085             this.onTouchViewEmptyResults();
14086             return;
14087         }
14088         
14089         this.clearTouchView();
14090         
14091         var rawValue = this.getRawValue();
14092         
14093         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14094         
14095         this.tickItems = [];
14096         
14097         this.store.data.each(function(d, rowIndex){
14098             var row = this.touchViewListGroup.createChild(template);
14099             
14100             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14101                 row.addClass(d.data.cls);
14102             }
14103             
14104             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14105                 var cfg = {
14106                     data : d.data,
14107                     html : d.data[this.displayField]
14108                 };
14109                 
14110                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14111                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14112                 }
14113             }
14114             
14115             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
14116                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14117             }
14118             
14119             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
14120                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14121                 this.tickItems.push(d.data);
14122             }
14123             
14124             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14125             
14126         }, this);
14127         
14128         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14129         
14130         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14131
14132         if(this.fieldLabel.length){
14133             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14134         }
14135
14136         var listHeight = this.touchViewListGroup.getHeight();
14137         
14138         var _this = this;
14139         
14140         if(firstChecked && listHeight > bodyHeight){
14141             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14142         }
14143         
14144     },
14145     
14146     onTouchViewLoadException : function()
14147     {
14148         this.hideTouchView();
14149     },
14150     
14151     onTouchViewEmptyResults : function()
14152     {
14153         this.clearTouchView();
14154         
14155         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14156         
14157         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14158         
14159     },
14160     
14161     clearTouchView : function()
14162     {
14163         this.touchViewListGroup.dom.innerHTML = '';
14164     },
14165     
14166     onTouchViewClick : function(e, el, o)
14167     {
14168         e.preventDefault();
14169         
14170         var row = o.row;
14171         var rowIndex = o.rowIndex;
14172         
14173         var r = this.store.getAt(rowIndex);
14174         
14175         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14176             
14177             if(!this.multiple){
14178                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14179                     c.dom.removeAttribute('checked');
14180                 }, this);
14181
14182                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14183
14184                 this.setFromData(r.data);
14185
14186                 var close = this.closeTriggerEl();
14187
14188                 if(close){
14189                     close.show();
14190                 }
14191
14192                 this.hideTouchView();
14193
14194                 this.fireEvent('select', this, r, rowIndex);
14195
14196                 return;
14197             }
14198
14199             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14200                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14201                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14202                 return;
14203             }
14204
14205             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14206             this.addItem(r.data);
14207             this.tickItems.push(r.data);
14208         }
14209     }
14210     
14211
14212     /** 
14213     * @cfg {Boolean} grow 
14214     * @hide 
14215     */
14216     /** 
14217     * @cfg {Number} growMin 
14218     * @hide 
14219     */
14220     /** 
14221     * @cfg {Number} growMax 
14222     * @hide 
14223     */
14224     /**
14225      * @hide
14226      * @method autoSize
14227      */
14228 });
14229
14230 Roo.apply(Roo.bootstrap.ComboBox,  {
14231     
14232     header : {
14233         tag: 'div',
14234         cls: 'modal-header',
14235         cn: [
14236             {
14237                 tag: 'h4',
14238                 cls: 'modal-title'
14239             }
14240         ]
14241     },
14242     
14243     body : {
14244         tag: 'div',
14245         cls: 'modal-body',
14246         cn: [
14247             {
14248                 tag: 'ul',
14249                 cls: 'list-group'
14250             }
14251         ]
14252     },
14253     
14254     listItemRadio : {
14255         tag: 'li',
14256         cls: 'list-group-item',
14257         cn: [
14258             {
14259                 tag: 'span',
14260                 cls: 'roo-combobox-list-group-item-value'
14261             },
14262             {
14263                 tag: 'div',
14264                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14265                 cn: [
14266                     {
14267                         tag: 'input',
14268                         type: 'radio'
14269                     },
14270                     {
14271                         tag: 'label'
14272                     }
14273                 ]
14274             }
14275         ]
14276     },
14277     
14278     listItemCheckbox : {
14279         tag: 'li',
14280         cls: 'list-group-item',
14281         cn: [
14282             {
14283                 tag: 'span',
14284                 cls: 'roo-combobox-list-group-item-value'
14285             },
14286             {
14287                 tag: 'div',
14288                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14289                 cn: [
14290                     {
14291                         tag: 'input',
14292                         type: 'checkbox'
14293                     },
14294                     {
14295                         tag: 'label'
14296                     }
14297                 ]
14298             }
14299         ]
14300     },
14301     
14302     emptyResult : {
14303         tag: 'div',
14304         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14305     },
14306     
14307     footer : {
14308         tag: 'div',
14309         cls: 'modal-footer',
14310         cn: [
14311             {
14312                 tag: 'div',
14313                 cls: 'row',
14314                 cn: [
14315                     {
14316                         tag: 'div',
14317                         cls: 'col-xs-6 text-left',
14318                         cn: {
14319                             tag: 'button',
14320                             cls: 'btn btn-danger roo-touch-view-cancel',
14321                             html: 'Cancel'
14322                         }
14323                     },
14324                     {
14325                         tag: 'div',
14326                         cls: 'col-xs-6 text-right',
14327                         cn: {
14328                             tag: 'button',
14329                             cls: 'btn btn-success roo-touch-view-ok',
14330                             html: 'OK'
14331                         }
14332                     }
14333                 ]
14334             }
14335         ]
14336         
14337     }
14338 });
14339
14340 Roo.apply(Roo.bootstrap.ComboBox,  {
14341     
14342     touchViewTemplate : {
14343         tag: 'div',
14344         cls: 'modal fade roo-combobox-touch-view',
14345         cn: [
14346             {
14347                 tag: 'div',
14348                 cls: 'modal-dialog',
14349                 style : 'position:fixed', // we have to fix position....
14350                 cn: [
14351                     {
14352                         tag: 'div',
14353                         cls: 'modal-content',
14354                         cn: [
14355                             Roo.bootstrap.ComboBox.header,
14356                             Roo.bootstrap.ComboBox.body,
14357                             Roo.bootstrap.ComboBox.footer
14358                         ]
14359                     }
14360                 ]
14361             }
14362         ]
14363     }
14364 });/*
14365  * Based on:
14366  * Ext JS Library 1.1.1
14367  * Copyright(c) 2006-2007, Ext JS, LLC.
14368  *
14369  * Originally Released Under LGPL - original licence link has changed is not relivant.
14370  *
14371  * Fork - LGPL
14372  * <script type="text/javascript">
14373  */
14374
14375 /**
14376  * @class Roo.View
14377  * @extends Roo.util.Observable
14378  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14379  * This class also supports single and multi selection modes. <br>
14380  * Create a data model bound view:
14381  <pre><code>
14382  var store = new Roo.data.Store(...);
14383
14384  var view = new Roo.View({
14385     el : "my-element",
14386     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14387  
14388     singleSelect: true,
14389     selectedClass: "ydataview-selected",
14390     store: store
14391  });
14392
14393  // listen for node click?
14394  view.on("click", function(vw, index, node, e){
14395  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14396  });
14397
14398  // load XML data
14399  dataModel.load("foobar.xml");
14400  </code></pre>
14401  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14402  * <br><br>
14403  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14404  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14405  * 
14406  * Note: old style constructor is still suported (container, template, config)
14407  * 
14408  * @constructor
14409  * Create a new View
14410  * @param {Object} config The config object
14411  * 
14412  */
14413 Roo.View = function(config, depreciated_tpl, depreciated_config){
14414     
14415     this.parent = false;
14416     
14417     if (typeof(depreciated_tpl) == 'undefined') {
14418         // new way.. - universal constructor.
14419         Roo.apply(this, config);
14420         this.el  = Roo.get(this.el);
14421     } else {
14422         // old format..
14423         this.el  = Roo.get(config);
14424         this.tpl = depreciated_tpl;
14425         Roo.apply(this, depreciated_config);
14426     }
14427     this.wrapEl  = this.el.wrap().wrap();
14428     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14429     
14430     
14431     if(typeof(this.tpl) == "string"){
14432         this.tpl = new Roo.Template(this.tpl);
14433     } else {
14434         // support xtype ctors..
14435         this.tpl = new Roo.factory(this.tpl, Roo);
14436     }
14437     
14438     
14439     this.tpl.compile();
14440     
14441     /** @private */
14442     this.addEvents({
14443         /**
14444          * @event beforeclick
14445          * Fires before a click is processed. Returns false to cancel the default action.
14446          * @param {Roo.View} this
14447          * @param {Number} index The index of the target node
14448          * @param {HTMLElement} node The target node
14449          * @param {Roo.EventObject} e The raw event object
14450          */
14451             "beforeclick" : true,
14452         /**
14453          * @event click
14454          * Fires when a template node is clicked.
14455          * @param {Roo.View} this
14456          * @param {Number} index The index of the target node
14457          * @param {HTMLElement} node The target node
14458          * @param {Roo.EventObject} e The raw event object
14459          */
14460             "click" : true,
14461         /**
14462          * @event dblclick
14463          * Fires when a template node is double clicked.
14464          * @param {Roo.View} this
14465          * @param {Number} index The index of the target node
14466          * @param {HTMLElement} node The target node
14467          * @param {Roo.EventObject} e The raw event object
14468          */
14469             "dblclick" : true,
14470         /**
14471          * @event contextmenu
14472          * Fires when a template node is right clicked.
14473          * @param {Roo.View} this
14474          * @param {Number} index The index of the target node
14475          * @param {HTMLElement} node The target node
14476          * @param {Roo.EventObject} e The raw event object
14477          */
14478             "contextmenu" : true,
14479         /**
14480          * @event selectionchange
14481          * Fires when the selected nodes change.
14482          * @param {Roo.View} this
14483          * @param {Array} selections Array of the selected nodes
14484          */
14485             "selectionchange" : true,
14486     
14487         /**
14488          * @event beforeselect
14489          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14490          * @param {Roo.View} this
14491          * @param {HTMLElement} node The node to be selected
14492          * @param {Array} selections Array of currently selected nodes
14493          */
14494             "beforeselect" : true,
14495         /**
14496          * @event preparedata
14497          * Fires on every row to render, to allow you to change the data.
14498          * @param {Roo.View} this
14499          * @param {Object} data to be rendered (change this)
14500          */
14501           "preparedata" : true
14502           
14503           
14504         });
14505
14506
14507
14508     this.el.on({
14509         "click": this.onClick,
14510         "dblclick": this.onDblClick,
14511         "contextmenu": this.onContextMenu,
14512         scope:this
14513     });
14514
14515     this.selections = [];
14516     this.nodes = [];
14517     this.cmp = new Roo.CompositeElementLite([]);
14518     if(this.store){
14519         this.store = Roo.factory(this.store, Roo.data);
14520         this.setStore(this.store, true);
14521     }
14522     
14523     if ( this.footer && this.footer.xtype) {
14524            
14525          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14526         
14527         this.footer.dataSource = this.store;
14528         this.footer.container = fctr;
14529         this.footer = Roo.factory(this.footer, Roo);
14530         fctr.insertFirst(this.el);
14531         
14532         // this is a bit insane - as the paging toolbar seems to detach the el..
14533 //        dom.parentNode.parentNode.parentNode
14534          // they get detached?
14535     }
14536     
14537     
14538     Roo.View.superclass.constructor.call(this);
14539     
14540     
14541 };
14542
14543 Roo.extend(Roo.View, Roo.util.Observable, {
14544     
14545      /**
14546      * @cfg {Roo.data.Store} store Data store to load data from.
14547      */
14548     store : false,
14549     
14550     /**
14551      * @cfg {String|Roo.Element} el The container element.
14552      */
14553     el : '',
14554     
14555     /**
14556      * @cfg {String|Roo.Template} tpl The template used by this View 
14557      */
14558     tpl : false,
14559     /**
14560      * @cfg {String} dataName the named area of the template to use as the data area
14561      *                          Works with domtemplates roo-name="name"
14562      */
14563     dataName: false,
14564     /**
14565      * @cfg {String} selectedClass The css class to add to selected nodes
14566      */
14567     selectedClass : "x-view-selected",
14568      /**
14569      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14570      */
14571     emptyText : "",
14572     
14573     /**
14574      * @cfg {String} text to display on mask (default Loading)
14575      */
14576     mask : false,
14577     /**
14578      * @cfg {Boolean} multiSelect Allow multiple selection
14579      */
14580     multiSelect : false,
14581     /**
14582      * @cfg {Boolean} singleSelect Allow single selection
14583      */
14584     singleSelect:  false,
14585     
14586     /**
14587      * @cfg {Boolean} toggleSelect - selecting 
14588      */
14589     toggleSelect : false,
14590     
14591     /**
14592      * @cfg {Boolean} tickable - selecting 
14593      */
14594     tickable : false,
14595     
14596     /**
14597      * Returns the element this view is bound to.
14598      * @return {Roo.Element}
14599      */
14600     getEl : function(){
14601         return this.wrapEl;
14602     },
14603     
14604     
14605
14606     /**
14607      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14608      */
14609     refresh : function(){
14610         //Roo.log('refresh');
14611         var t = this.tpl;
14612         
14613         // if we are using something like 'domtemplate', then
14614         // the what gets used is:
14615         // t.applySubtemplate(NAME, data, wrapping data..)
14616         // the outer template then get' applied with
14617         //     the store 'extra data'
14618         // and the body get's added to the
14619         //      roo-name="data" node?
14620         //      <span class='roo-tpl-{name}'></span> ?????
14621         
14622         
14623         
14624         this.clearSelections();
14625         this.el.update("");
14626         var html = [];
14627         var records = this.store.getRange();
14628         if(records.length < 1) {
14629             
14630             // is this valid??  = should it render a template??
14631             
14632             this.el.update(this.emptyText);
14633             return;
14634         }
14635         var el = this.el;
14636         if (this.dataName) {
14637             this.el.update(t.apply(this.store.meta)); //????
14638             el = this.el.child('.roo-tpl-' + this.dataName);
14639         }
14640         
14641         for(var i = 0, len = records.length; i < len; i++){
14642             var data = this.prepareData(records[i].data, i, records[i]);
14643             this.fireEvent("preparedata", this, data, i, records[i]);
14644             
14645             var d = Roo.apply({}, data);
14646             
14647             if(this.tickable){
14648                 Roo.apply(d, {'roo-id' : Roo.id()});
14649                 
14650                 var _this = this;
14651             
14652                 Roo.each(this.parent.item, function(item){
14653                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14654                         return;
14655                     }
14656                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14657                 });
14658             }
14659             
14660             html[html.length] = Roo.util.Format.trim(
14661                 this.dataName ?
14662                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14663                     t.apply(d)
14664             );
14665         }
14666         
14667         
14668         
14669         el.update(html.join(""));
14670         this.nodes = el.dom.childNodes;
14671         this.updateIndexes(0);
14672     },
14673     
14674
14675     /**
14676      * Function to override to reformat the data that is sent to
14677      * the template for each node.
14678      * DEPRICATED - use the preparedata event handler.
14679      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14680      * a JSON object for an UpdateManager bound view).
14681      */
14682     prepareData : function(data, index, record)
14683     {
14684         this.fireEvent("preparedata", this, data, index, record);
14685         return data;
14686     },
14687
14688     onUpdate : function(ds, record){
14689         // Roo.log('on update');   
14690         this.clearSelections();
14691         var index = this.store.indexOf(record);
14692         var n = this.nodes[index];
14693         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14694         n.parentNode.removeChild(n);
14695         this.updateIndexes(index, index);
14696     },
14697
14698     
14699     
14700 // --------- FIXME     
14701     onAdd : function(ds, records, index)
14702     {
14703         //Roo.log(['on Add', ds, records, index] );        
14704         this.clearSelections();
14705         if(this.nodes.length == 0){
14706             this.refresh();
14707             return;
14708         }
14709         var n = this.nodes[index];
14710         for(var i = 0, len = records.length; i < len; i++){
14711             var d = this.prepareData(records[i].data, i, records[i]);
14712             if(n){
14713                 this.tpl.insertBefore(n, d);
14714             }else{
14715                 
14716                 this.tpl.append(this.el, d);
14717             }
14718         }
14719         this.updateIndexes(index);
14720     },
14721
14722     onRemove : function(ds, record, index){
14723        // Roo.log('onRemove');
14724         this.clearSelections();
14725         var el = this.dataName  ?
14726             this.el.child('.roo-tpl-' + this.dataName) :
14727             this.el; 
14728         
14729         el.dom.removeChild(this.nodes[index]);
14730         this.updateIndexes(index);
14731     },
14732
14733     /**
14734      * Refresh an individual node.
14735      * @param {Number} index
14736      */
14737     refreshNode : function(index){
14738         this.onUpdate(this.store, this.store.getAt(index));
14739     },
14740
14741     updateIndexes : function(startIndex, endIndex){
14742         var ns = this.nodes;
14743         startIndex = startIndex || 0;
14744         endIndex = endIndex || ns.length - 1;
14745         for(var i = startIndex; i <= endIndex; i++){
14746             ns[i].nodeIndex = i;
14747         }
14748     },
14749
14750     /**
14751      * Changes the data store this view uses and refresh the view.
14752      * @param {Store} store
14753      */
14754     setStore : function(store, initial){
14755         if(!initial && this.store){
14756             this.store.un("datachanged", this.refresh);
14757             this.store.un("add", this.onAdd);
14758             this.store.un("remove", this.onRemove);
14759             this.store.un("update", this.onUpdate);
14760             this.store.un("clear", this.refresh);
14761             this.store.un("beforeload", this.onBeforeLoad);
14762             this.store.un("load", this.onLoad);
14763             this.store.un("loadexception", this.onLoad);
14764         }
14765         if(store){
14766           
14767             store.on("datachanged", this.refresh, this);
14768             store.on("add", this.onAdd, this);
14769             store.on("remove", this.onRemove, this);
14770             store.on("update", this.onUpdate, this);
14771             store.on("clear", this.refresh, this);
14772             store.on("beforeload", this.onBeforeLoad, this);
14773             store.on("load", this.onLoad, this);
14774             store.on("loadexception", this.onLoad, this);
14775         }
14776         
14777         if(store){
14778             this.refresh();
14779         }
14780     },
14781     /**
14782      * onbeforeLoad - masks the loading area.
14783      *
14784      */
14785     onBeforeLoad : function(store,opts)
14786     {
14787          //Roo.log('onBeforeLoad');   
14788         if (!opts.add) {
14789             this.el.update("");
14790         }
14791         this.el.mask(this.mask ? this.mask : "Loading" ); 
14792     },
14793     onLoad : function ()
14794     {
14795         this.el.unmask();
14796     },
14797     
14798
14799     /**
14800      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14801      * @param {HTMLElement} node
14802      * @return {HTMLElement} The template node
14803      */
14804     findItemFromChild : function(node){
14805         var el = this.dataName  ?
14806             this.el.child('.roo-tpl-' + this.dataName,true) :
14807             this.el.dom; 
14808         
14809         if(!node || node.parentNode == el){
14810                     return node;
14811             }
14812             var p = node.parentNode;
14813             while(p && p != el){
14814             if(p.parentNode == el){
14815                 return p;
14816             }
14817             p = p.parentNode;
14818         }
14819             return null;
14820     },
14821
14822     /** @ignore */
14823     onClick : function(e){
14824         var item = this.findItemFromChild(e.getTarget());
14825         if(item){
14826             var index = this.indexOf(item);
14827             if(this.onItemClick(item, index, e) !== false){
14828                 this.fireEvent("click", this, index, item, e);
14829             }
14830         }else{
14831             this.clearSelections();
14832         }
14833     },
14834
14835     /** @ignore */
14836     onContextMenu : function(e){
14837         var item = this.findItemFromChild(e.getTarget());
14838         if(item){
14839             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14840         }
14841     },
14842
14843     /** @ignore */
14844     onDblClick : function(e){
14845         var item = this.findItemFromChild(e.getTarget());
14846         if(item){
14847             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14848         }
14849     },
14850
14851     onItemClick : function(item, index, e)
14852     {
14853         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14854             return false;
14855         }
14856         if (this.toggleSelect) {
14857             var m = this.isSelected(item) ? 'unselect' : 'select';
14858             //Roo.log(m);
14859             var _t = this;
14860             _t[m](item, true, false);
14861             return true;
14862         }
14863         if(this.multiSelect || this.singleSelect){
14864             if(this.multiSelect && e.shiftKey && this.lastSelection){
14865                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14866             }else{
14867                 this.select(item, this.multiSelect && e.ctrlKey);
14868                 this.lastSelection = item;
14869             }
14870             
14871             if(!this.tickable){
14872                 e.preventDefault();
14873             }
14874             
14875         }
14876         return true;
14877     },
14878
14879     /**
14880      * Get the number of selected nodes.
14881      * @return {Number}
14882      */
14883     getSelectionCount : function(){
14884         return this.selections.length;
14885     },
14886
14887     /**
14888      * Get the currently selected nodes.
14889      * @return {Array} An array of HTMLElements
14890      */
14891     getSelectedNodes : function(){
14892         return this.selections;
14893     },
14894
14895     /**
14896      * Get the indexes of the selected nodes.
14897      * @return {Array}
14898      */
14899     getSelectedIndexes : function(){
14900         var indexes = [], s = this.selections;
14901         for(var i = 0, len = s.length; i < len; i++){
14902             indexes.push(s[i].nodeIndex);
14903         }
14904         return indexes;
14905     },
14906
14907     /**
14908      * Clear all selections
14909      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14910      */
14911     clearSelections : function(suppressEvent){
14912         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14913             this.cmp.elements = this.selections;
14914             this.cmp.removeClass(this.selectedClass);
14915             this.selections = [];
14916             if(!suppressEvent){
14917                 this.fireEvent("selectionchange", this, this.selections);
14918             }
14919         }
14920     },
14921
14922     /**
14923      * Returns true if the passed node is selected
14924      * @param {HTMLElement/Number} node The node or node index
14925      * @return {Boolean}
14926      */
14927     isSelected : function(node){
14928         var s = this.selections;
14929         if(s.length < 1){
14930             return false;
14931         }
14932         node = this.getNode(node);
14933         return s.indexOf(node) !== -1;
14934     },
14935
14936     /**
14937      * Selects nodes.
14938      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14939      * @param {Boolean} keepExisting (optional) true to keep existing selections
14940      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14941      */
14942     select : function(nodeInfo, keepExisting, suppressEvent){
14943         if(nodeInfo instanceof Array){
14944             if(!keepExisting){
14945                 this.clearSelections(true);
14946             }
14947             for(var i = 0, len = nodeInfo.length; i < len; i++){
14948                 this.select(nodeInfo[i], true, true);
14949             }
14950             return;
14951         } 
14952         var node = this.getNode(nodeInfo);
14953         if(!node || this.isSelected(node)){
14954             return; // already selected.
14955         }
14956         if(!keepExisting){
14957             this.clearSelections(true);
14958         }
14959         
14960         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14961             Roo.fly(node).addClass(this.selectedClass);
14962             this.selections.push(node);
14963             if(!suppressEvent){
14964                 this.fireEvent("selectionchange", this, this.selections);
14965             }
14966         }
14967         
14968         
14969     },
14970       /**
14971      * Unselects nodes.
14972      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14973      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14974      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14975      */
14976     unselect : function(nodeInfo, keepExisting, suppressEvent)
14977     {
14978         if(nodeInfo instanceof Array){
14979             Roo.each(this.selections, function(s) {
14980                 this.unselect(s, nodeInfo);
14981             }, this);
14982             return;
14983         }
14984         var node = this.getNode(nodeInfo);
14985         if(!node || !this.isSelected(node)){
14986             //Roo.log("not selected");
14987             return; // not selected.
14988         }
14989         // fireevent???
14990         var ns = [];
14991         Roo.each(this.selections, function(s) {
14992             if (s == node ) {
14993                 Roo.fly(node).removeClass(this.selectedClass);
14994
14995                 return;
14996             }
14997             ns.push(s);
14998         },this);
14999         
15000         this.selections= ns;
15001         this.fireEvent("selectionchange", this, this.selections);
15002     },
15003
15004     /**
15005      * Gets a template node.
15006      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15007      * @return {HTMLElement} The node or null if it wasn't found
15008      */
15009     getNode : function(nodeInfo){
15010         if(typeof nodeInfo == "string"){
15011             return document.getElementById(nodeInfo);
15012         }else if(typeof nodeInfo == "number"){
15013             return this.nodes[nodeInfo];
15014         }
15015         return nodeInfo;
15016     },
15017
15018     /**
15019      * Gets a range template nodes.
15020      * @param {Number} startIndex
15021      * @param {Number} endIndex
15022      * @return {Array} An array of nodes
15023      */
15024     getNodes : function(start, end){
15025         var ns = this.nodes;
15026         start = start || 0;
15027         end = typeof end == "undefined" ? ns.length - 1 : end;
15028         var nodes = [];
15029         if(start <= end){
15030             for(var i = start; i <= end; i++){
15031                 nodes.push(ns[i]);
15032             }
15033         } else{
15034             for(var i = start; i >= end; i--){
15035                 nodes.push(ns[i]);
15036             }
15037         }
15038         return nodes;
15039     },
15040
15041     /**
15042      * Finds the index of the passed node
15043      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15044      * @return {Number} The index of the node or -1
15045      */
15046     indexOf : function(node){
15047         node = this.getNode(node);
15048         if(typeof node.nodeIndex == "number"){
15049             return node.nodeIndex;
15050         }
15051         var ns = this.nodes;
15052         for(var i = 0, len = ns.length; i < len; i++){
15053             if(ns[i] == node){
15054                 return i;
15055             }
15056         }
15057         return -1;
15058     }
15059 });
15060 /*
15061  * - LGPL
15062  *
15063  * based on jquery fullcalendar
15064  * 
15065  */
15066
15067 Roo.bootstrap = Roo.bootstrap || {};
15068 /**
15069  * @class Roo.bootstrap.Calendar
15070  * @extends Roo.bootstrap.Component
15071  * Bootstrap Calendar class
15072  * @cfg {Boolean} loadMask (true|false) default false
15073  * @cfg {Object} header generate the user specific header of the calendar, default false
15074
15075  * @constructor
15076  * Create a new Container
15077  * @param {Object} config The config object
15078  */
15079
15080
15081
15082 Roo.bootstrap.Calendar = function(config){
15083     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15084      this.addEvents({
15085         /**
15086              * @event select
15087              * Fires when a date is selected
15088              * @param {DatePicker} this
15089              * @param {Date} date The selected date
15090              */
15091         'select': true,
15092         /**
15093              * @event monthchange
15094              * Fires when the displayed month changes 
15095              * @param {DatePicker} this
15096              * @param {Date} date The selected month
15097              */
15098         'monthchange': true,
15099         /**
15100              * @event evententer
15101              * Fires when mouse over an event
15102              * @param {Calendar} this
15103              * @param {event} Event
15104              */
15105         'evententer': true,
15106         /**
15107              * @event eventleave
15108              * Fires when the mouse leaves an
15109              * @param {Calendar} this
15110              * @param {event}
15111              */
15112         'eventleave': true,
15113         /**
15114              * @event eventclick
15115              * Fires when the mouse click an
15116              * @param {Calendar} this
15117              * @param {event}
15118              */
15119         'eventclick': true
15120         
15121     });
15122
15123 };
15124
15125 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15126     
15127      /**
15128      * @cfg {Number} startDay
15129      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15130      */
15131     startDay : 0,
15132     
15133     loadMask : false,
15134     
15135     header : false,
15136       
15137     getAutoCreate : function(){
15138         
15139         
15140         var fc_button = function(name, corner, style, content ) {
15141             return Roo.apply({},{
15142                 tag : 'span',
15143                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15144                          (corner.length ?
15145                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15146                             ''
15147                         ),
15148                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15149                 unselectable: 'on'
15150             });
15151         };
15152         
15153         var header = {};
15154         
15155         if(!this.header){
15156             header = {
15157                 tag : 'table',
15158                 cls : 'fc-header',
15159                 style : 'width:100%',
15160                 cn : [
15161                     {
15162                         tag: 'tr',
15163                         cn : [
15164                             {
15165                                 tag : 'td',
15166                                 cls : 'fc-header-left',
15167                                 cn : [
15168                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15169                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15170                                     { tag: 'span', cls: 'fc-header-space' },
15171                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15172
15173
15174                                 ]
15175                             },
15176
15177                             {
15178                                 tag : 'td',
15179                                 cls : 'fc-header-center',
15180                                 cn : [
15181                                     {
15182                                         tag: 'span',
15183                                         cls: 'fc-header-title',
15184                                         cn : {
15185                                             tag: 'H2',
15186                                             html : 'month / year'
15187                                         }
15188                                     }
15189
15190                                 ]
15191                             },
15192                             {
15193                                 tag : 'td',
15194                                 cls : 'fc-header-right',
15195                                 cn : [
15196                               /*      fc_button('month', 'left', '', 'month' ),
15197                                     fc_button('week', '', '', 'week' ),
15198                                     fc_button('day', 'right', '', 'day' )
15199                                 */    
15200
15201                                 ]
15202                             }
15203
15204                         ]
15205                     }
15206                 ]
15207             };
15208         }
15209         
15210         header = this.header;
15211         
15212        
15213         var cal_heads = function() {
15214             var ret = [];
15215             // fixme - handle this.
15216             
15217             for (var i =0; i < Date.dayNames.length; i++) {
15218                 var d = Date.dayNames[i];
15219                 ret.push({
15220                     tag: 'th',
15221                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15222                     html : d.substring(0,3)
15223                 });
15224                 
15225             }
15226             ret[0].cls += ' fc-first';
15227             ret[6].cls += ' fc-last';
15228             return ret;
15229         };
15230         var cal_cell = function(n) {
15231             return  {
15232                 tag: 'td',
15233                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15234                 cn : [
15235                     {
15236                         cn : [
15237                             {
15238                                 cls: 'fc-day-number',
15239                                 html: 'D'
15240                             },
15241                             {
15242                                 cls: 'fc-day-content',
15243                              
15244                                 cn : [
15245                                      {
15246                                         style: 'position: relative;' // height: 17px;
15247                                     }
15248                                 ]
15249                             }
15250                             
15251                             
15252                         ]
15253                     }
15254                 ]
15255                 
15256             }
15257         };
15258         var cal_rows = function() {
15259             
15260             var ret = [];
15261             for (var r = 0; r < 6; r++) {
15262                 var row= {
15263                     tag : 'tr',
15264                     cls : 'fc-week',
15265                     cn : []
15266                 };
15267                 
15268                 for (var i =0; i < Date.dayNames.length; i++) {
15269                     var d = Date.dayNames[i];
15270                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15271
15272                 }
15273                 row.cn[0].cls+=' fc-first';
15274                 row.cn[0].cn[0].style = 'min-height:90px';
15275                 row.cn[6].cls+=' fc-last';
15276                 ret.push(row);
15277                 
15278             }
15279             ret[0].cls += ' fc-first';
15280             ret[4].cls += ' fc-prev-last';
15281             ret[5].cls += ' fc-last';
15282             return ret;
15283             
15284         };
15285         
15286         var cal_table = {
15287             tag: 'table',
15288             cls: 'fc-border-separate',
15289             style : 'width:100%',
15290             cellspacing  : 0,
15291             cn : [
15292                 { 
15293                     tag: 'thead',
15294                     cn : [
15295                         { 
15296                             tag: 'tr',
15297                             cls : 'fc-first fc-last',
15298                             cn : cal_heads()
15299                         }
15300                     ]
15301                 },
15302                 { 
15303                     tag: 'tbody',
15304                     cn : cal_rows()
15305                 }
15306                   
15307             ]
15308         };
15309          
15310          var cfg = {
15311             cls : 'fc fc-ltr',
15312             cn : [
15313                 header,
15314                 {
15315                     cls : 'fc-content',
15316                     style : "position: relative;",
15317                     cn : [
15318                         {
15319                             cls : 'fc-view fc-view-month fc-grid',
15320                             style : 'position: relative',
15321                             unselectable : 'on',
15322                             cn : [
15323                                 {
15324                                     cls : 'fc-event-container',
15325                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15326                                 },
15327                                 cal_table
15328                             ]
15329                         }
15330                     ]
15331     
15332                 }
15333            ] 
15334             
15335         };
15336         
15337          
15338         
15339         return cfg;
15340     },
15341     
15342     
15343     initEvents : function()
15344     {
15345         if(!this.store){
15346             throw "can not find store for calendar";
15347         }
15348         
15349         var mark = {
15350             tag: "div",
15351             cls:"x-dlg-mask",
15352             style: "text-align:center",
15353             cn: [
15354                 {
15355                     tag: "div",
15356                     style: "background-color:white;width:50%;margin:250 auto",
15357                     cn: [
15358                         {
15359                             tag: "img",
15360                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15361                         },
15362                         {
15363                             tag: "span",
15364                             html: "Loading"
15365                         }
15366                         
15367                     ]
15368                 }
15369             ]
15370         };
15371         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15372         
15373         var size = this.el.select('.fc-content', true).first().getSize();
15374         this.maskEl.setSize(size.width, size.height);
15375         this.maskEl.enableDisplayMode("block");
15376         if(!this.loadMask){
15377             this.maskEl.hide();
15378         }
15379         
15380         this.store = Roo.factory(this.store, Roo.data);
15381         this.store.on('load', this.onLoad, this);
15382         this.store.on('beforeload', this.onBeforeLoad, this);
15383         
15384         this.resize();
15385         
15386         this.cells = this.el.select('.fc-day',true);
15387         //Roo.log(this.cells);
15388         this.textNodes = this.el.query('.fc-day-number');
15389         this.cells.addClassOnOver('fc-state-hover');
15390         
15391         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15392         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15393         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15394         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15395         
15396         this.on('monthchange', this.onMonthChange, this);
15397         
15398         this.update(new Date().clearTime());
15399     },
15400     
15401     resize : function() {
15402         var sz  = this.el.getSize();
15403         
15404         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15405         this.el.select('.fc-day-content div',true).setHeight(34);
15406     },
15407     
15408     
15409     // private
15410     showPrevMonth : function(e){
15411         this.update(this.activeDate.add("mo", -1));
15412     },
15413     showToday : function(e){
15414         this.update(new Date().clearTime());
15415     },
15416     // private
15417     showNextMonth : function(e){
15418         this.update(this.activeDate.add("mo", 1));
15419     },
15420
15421     // private
15422     showPrevYear : function(){
15423         this.update(this.activeDate.add("y", -1));
15424     },
15425
15426     // private
15427     showNextYear : function(){
15428         this.update(this.activeDate.add("y", 1));
15429     },
15430
15431     
15432    // private
15433     update : function(date)
15434     {
15435         var vd = this.activeDate;
15436         this.activeDate = date;
15437 //        if(vd && this.el){
15438 //            var t = date.getTime();
15439 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15440 //                Roo.log('using add remove');
15441 //                
15442 //                this.fireEvent('monthchange', this, date);
15443 //                
15444 //                this.cells.removeClass("fc-state-highlight");
15445 //                this.cells.each(function(c){
15446 //                   if(c.dateValue == t){
15447 //                       c.addClass("fc-state-highlight");
15448 //                       setTimeout(function(){
15449 //                            try{c.dom.firstChild.focus();}catch(e){}
15450 //                       }, 50);
15451 //                       return false;
15452 //                   }
15453 //                   return true;
15454 //                });
15455 //                return;
15456 //            }
15457 //        }
15458         
15459         var days = date.getDaysInMonth();
15460         
15461         var firstOfMonth = date.getFirstDateOfMonth();
15462         var startingPos = firstOfMonth.getDay()-this.startDay;
15463         
15464         if(startingPos < this.startDay){
15465             startingPos += 7;
15466         }
15467         
15468         var pm = date.add(Date.MONTH, -1);
15469         var prevStart = pm.getDaysInMonth()-startingPos;
15470 //        
15471         this.cells = this.el.select('.fc-day',true);
15472         this.textNodes = this.el.query('.fc-day-number');
15473         this.cells.addClassOnOver('fc-state-hover');
15474         
15475         var cells = this.cells.elements;
15476         var textEls = this.textNodes;
15477         
15478         Roo.each(cells, function(cell){
15479             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15480         });
15481         
15482         days += startingPos;
15483
15484         // convert everything to numbers so it's fast
15485         var day = 86400000;
15486         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15487         //Roo.log(d);
15488         //Roo.log(pm);
15489         //Roo.log(prevStart);
15490         
15491         var today = new Date().clearTime().getTime();
15492         var sel = date.clearTime().getTime();
15493         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15494         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15495         var ddMatch = this.disabledDatesRE;
15496         var ddText = this.disabledDatesText;
15497         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15498         var ddaysText = this.disabledDaysText;
15499         var format = this.format;
15500         
15501         var setCellClass = function(cal, cell){
15502             cell.row = 0;
15503             cell.events = [];
15504             cell.more = [];
15505             //Roo.log('set Cell Class');
15506             cell.title = "";
15507             var t = d.getTime();
15508             
15509             //Roo.log(d);
15510             
15511             cell.dateValue = t;
15512             if(t == today){
15513                 cell.className += " fc-today";
15514                 cell.className += " fc-state-highlight";
15515                 cell.title = cal.todayText;
15516             }
15517             if(t == sel){
15518                 // disable highlight in other month..
15519                 //cell.className += " fc-state-highlight";
15520                 
15521             }
15522             // disabling
15523             if(t < min) {
15524                 cell.className = " fc-state-disabled";
15525                 cell.title = cal.minText;
15526                 return;
15527             }
15528             if(t > max) {
15529                 cell.className = " fc-state-disabled";
15530                 cell.title = cal.maxText;
15531                 return;
15532             }
15533             if(ddays){
15534                 if(ddays.indexOf(d.getDay()) != -1){
15535                     cell.title = ddaysText;
15536                     cell.className = " fc-state-disabled";
15537                 }
15538             }
15539             if(ddMatch && format){
15540                 var fvalue = d.dateFormat(format);
15541                 if(ddMatch.test(fvalue)){
15542                     cell.title = ddText.replace("%0", fvalue);
15543                     cell.className = " fc-state-disabled";
15544                 }
15545             }
15546             
15547             if (!cell.initialClassName) {
15548                 cell.initialClassName = cell.dom.className;
15549             }
15550             
15551             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15552         };
15553
15554         var i = 0;
15555         
15556         for(; i < startingPos; i++) {
15557             textEls[i].innerHTML = (++prevStart);
15558             d.setDate(d.getDate()+1);
15559             
15560             cells[i].className = "fc-past fc-other-month";
15561             setCellClass(this, cells[i]);
15562         }
15563         
15564         var intDay = 0;
15565         
15566         for(; i < days; i++){
15567             intDay = i - startingPos + 1;
15568             textEls[i].innerHTML = (intDay);
15569             d.setDate(d.getDate()+1);
15570             
15571             cells[i].className = ''; // "x-date-active";
15572             setCellClass(this, cells[i]);
15573         }
15574         var extraDays = 0;
15575         
15576         for(; i < 42; i++) {
15577             textEls[i].innerHTML = (++extraDays);
15578             d.setDate(d.getDate()+1);
15579             
15580             cells[i].className = "fc-future fc-other-month";
15581             setCellClass(this, cells[i]);
15582         }
15583         
15584         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15585         
15586         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15587         
15588         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15589         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15590         
15591         if(totalRows != 6){
15592             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15593             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15594         }
15595         
15596         this.fireEvent('monthchange', this, date);
15597         
15598         
15599         /*
15600         if(!this.internalRender){
15601             var main = this.el.dom.firstChild;
15602             var w = main.offsetWidth;
15603             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15604             Roo.fly(main).setWidth(w);
15605             this.internalRender = true;
15606             // opera does not respect the auto grow header center column
15607             // then, after it gets a width opera refuses to recalculate
15608             // without a second pass
15609             if(Roo.isOpera && !this.secondPass){
15610                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15611                 this.secondPass = true;
15612                 this.update.defer(10, this, [date]);
15613             }
15614         }
15615         */
15616         
15617     },
15618     
15619     findCell : function(dt) {
15620         dt = dt.clearTime().getTime();
15621         var ret = false;
15622         this.cells.each(function(c){
15623             //Roo.log("check " +c.dateValue + '?=' + dt);
15624             if(c.dateValue == dt){
15625                 ret = c;
15626                 return false;
15627             }
15628             return true;
15629         });
15630         
15631         return ret;
15632     },
15633     
15634     findCells : function(ev) {
15635         var s = ev.start.clone().clearTime().getTime();
15636        // Roo.log(s);
15637         var e= ev.end.clone().clearTime().getTime();
15638        // Roo.log(e);
15639         var ret = [];
15640         this.cells.each(function(c){
15641              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15642             
15643             if(c.dateValue > e){
15644                 return ;
15645             }
15646             if(c.dateValue < s){
15647                 return ;
15648             }
15649             ret.push(c);
15650         });
15651         
15652         return ret;    
15653     },
15654     
15655 //    findBestRow: function(cells)
15656 //    {
15657 //        var ret = 0;
15658 //        
15659 //        for (var i =0 ; i < cells.length;i++) {
15660 //            ret  = Math.max(cells[i].rows || 0,ret);
15661 //        }
15662 //        return ret;
15663 //        
15664 //    },
15665     
15666     
15667     addItem : function(ev)
15668     {
15669         // look for vertical location slot in
15670         var cells = this.findCells(ev);
15671         
15672 //        ev.row = this.findBestRow(cells);
15673         
15674         // work out the location.
15675         
15676         var crow = false;
15677         var rows = [];
15678         for(var i =0; i < cells.length; i++) {
15679             
15680             cells[i].row = cells[0].row;
15681             
15682             if(i == 0){
15683                 cells[i].row = cells[i].row + 1;
15684             }
15685             
15686             if (!crow) {
15687                 crow = {
15688                     start : cells[i],
15689                     end :  cells[i]
15690                 };
15691                 continue;
15692             }
15693             if (crow.start.getY() == cells[i].getY()) {
15694                 // on same row.
15695                 crow.end = cells[i];
15696                 continue;
15697             }
15698             // different row.
15699             rows.push(crow);
15700             crow = {
15701                 start: cells[i],
15702                 end : cells[i]
15703             };
15704             
15705         }
15706         
15707         rows.push(crow);
15708         ev.els = [];
15709         ev.rows = rows;
15710         ev.cells = cells;
15711         
15712         cells[0].events.push(ev);
15713         
15714         this.calevents.push(ev);
15715     },
15716     
15717     clearEvents: function() {
15718         
15719         if(!this.calevents){
15720             return;
15721         }
15722         
15723         Roo.each(this.cells.elements, function(c){
15724             c.row = 0;
15725             c.events = [];
15726             c.more = [];
15727         });
15728         
15729         Roo.each(this.calevents, function(e) {
15730             Roo.each(e.els, function(el) {
15731                 el.un('mouseenter' ,this.onEventEnter, this);
15732                 el.un('mouseleave' ,this.onEventLeave, this);
15733                 el.remove();
15734             },this);
15735         },this);
15736         
15737         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15738             e.remove();
15739         });
15740         
15741     },
15742     
15743     renderEvents: function()
15744     {   
15745         var _this = this;
15746         
15747         this.cells.each(function(c) {
15748             
15749             if(c.row < 5){
15750                 return;
15751             }
15752             
15753             var ev = c.events;
15754             
15755             var r = 4;
15756             if(c.row != c.events.length){
15757                 r = 4 - (4 - (c.row - c.events.length));
15758             }
15759             
15760             c.events = ev.slice(0, r);
15761             c.more = ev.slice(r);
15762             
15763             if(c.more.length && c.more.length == 1){
15764                 c.events.push(c.more.pop());
15765             }
15766             
15767             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15768             
15769         });
15770             
15771         this.cells.each(function(c) {
15772             
15773             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15774             
15775             
15776             for (var e = 0; e < c.events.length; e++){
15777                 var ev = c.events[e];
15778                 var rows = ev.rows;
15779                 
15780                 for(var i = 0; i < rows.length; i++) {
15781                 
15782                     // how many rows should it span..
15783
15784                     var  cfg = {
15785                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15786                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15787
15788                         unselectable : "on",
15789                         cn : [
15790                             {
15791                                 cls: 'fc-event-inner',
15792                                 cn : [
15793     //                                {
15794     //                                  tag:'span',
15795     //                                  cls: 'fc-event-time',
15796     //                                  html : cells.length > 1 ? '' : ev.time
15797     //                                },
15798                                     {
15799                                       tag:'span',
15800                                       cls: 'fc-event-title',
15801                                       html : String.format('{0}', ev.title)
15802                                     }
15803
15804
15805                                 ]
15806                             },
15807                             {
15808                                 cls: 'ui-resizable-handle ui-resizable-e',
15809                                 html : '&nbsp;&nbsp;&nbsp'
15810                             }
15811
15812                         ]
15813                     };
15814
15815                     if (i == 0) {
15816                         cfg.cls += ' fc-event-start';
15817                     }
15818                     if ((i+1) == rows.length) {
15819                         cfg.cls += ' fc-event-end';
15820                     }
15821
15822                     var ctr = _this.el.select('.fc-event-container',true).first();
15823                     var cg = ctr.createChild(cfg);
15824
15825                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15826                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15827
15828                     var r = (c.more.length) ? 1 : 0;
15829                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15830                     cg.setWidth(ebox.right - sbox.x -2);
15831
15832                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15833                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15834                     cg.on('click', _this.onEventClick, _this, ev);
15835
15836                     ev.els.push(cg);
15837                     
15838                 }
15839                 
15840             }
15841             
15842             
15843             if(c.more.length){
15844                 var  cfg = {
15845                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15846                     style : 'position: absolute',
15847                     unselectable : "on",
15848                     cn : [
15849                         {
15850                             cls: 'fc-event-inner',
15851                             cn : [
15852                                 {
15853                                   tag:'span',
15854                                   cls: 'fc-event-title',
15855                                   html : 'More'
15856                                 }
15857
15858
15859                             ]
15860                         },
15861                         {
15862                             cls: 'ui-resizable-handle ui-resizable-e',
15863                             html : '&nbsp;&nbsp;&nbsp'
15864                         }
15865
15866                     ]
15867                 };
15868
15869                 var ctr = _this.el.select('.fc-event-container',true).first();
15870                 var cg = ctr.createChild(cfg);
15871
15872                 var sbox = c.select('.fc-day-content',true).first().getBox();
15873                 var ebox = c.select('.fc-day-content',true).first().getBox();
15874                 //Roo.log(cg);
15875                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15876                 cg.setWidth(ebox.right - sbox.x -2);
15877
15878                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15879                 
15880             }
15881             
15882         });
15883         
15884         
15885         
15886     },
15887     
15888     onEventEnter: function (e, el,event,d) {
15889         this.fireEvent('evententer', this, el, event);
15890     },
15891     
15892     onEventLeave: function (e, el,event,d) {
15893         this.fireEvent('eventleave', this, el, event);
15894     },
15895     
15896     onEventClick: function (e, el,event,d) {
15897         this.fireEvent('eventclick', this, el, event);
15898     },
15899     
15900     onMonthChange: function () {
15901         this.store.load();
15902     },
15903     
15904     onMoreEventClick: function(e, el, more)
15905     {
15906         var _this = this;
15907         
15908         this.calpopover.placement = 'right';
15909         this.calpopover.setTitle('More');
15910         
15911         this.calpopover.setContent('');
15912         
15913         var ctr = this.calpopover.el.select('.popover-content', true).first();
15914         
15915         Roo.each(more, function(m){
15916             var cfg = {
15917                 cls : 'fc-event-hori fc-event-draggable',
15918                 html : m.title
15919             };
15920             var cg = ctr.createChild(cfg);
15921             
15922             cg.on('click', _this.onEventClick, _this, m);
15923         });
15924         
15925         this.calpopover.show(el);
15926         
15927         
15928     },
15929     
15930     onLoad: function () 
15931     {   
15932         this.calevents = [];
15933         var cal = this;
15934         
15935         if(this.store.getCount() > 0){
15936             this.store.data.each(function(d){
15937                cal.addItem({
15938                     id : d.data.id,
15939                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15940                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15941                     time : d.data.start_time,
15942                     title : d.data.title,
15943                     description : d.data.description,
15944                     venue : d.data.venue
15945                 });
15946             });
15947         }
15948         
15949         this.renderEvents();
15950         
15951         if(this.calevents.length && this.loadMask){
15952             this.maskEl.hide();
15953         }
15954     },
15955     
15956     onBeforeLoad: function()
15957     {
15958         this.clearEvents();
15959         if(this.loadMask){
15960             this.maskEl.show();
15961         }
15962     }
15963 });
15964
15965  
15966  /*
15967  * - LGPL
15968  *
15969  * element
15970  * 
15971  */
15972
15973 /**
15974  * @class Roo.bootstrap.Popover
15975  * @extends Roo.bootstrap.Component
15976  * Bootstrap Popover class
15977  * @cfg {String} html contents of the popover   (or false to use children..)
15978  * @cfg {String} title of popover (or false to hide)
15979  * @cfg {String} placement how it is placed
15980  * @cfg {String} trigger click || hover (or false to trigger manually)
15981  * @cfg {String} over what (parent or false to trigger manually.)
15982  * @cfg {Number} delay - delay before showing
15983  
15984  * @constructor
15985  * Create a new Popover
15986  * @param {Object} config The config object
15987  */
15988
15989 Roo.bootstrap.Popover = function(config){
15990     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15991     
15992     this.addEvents({
15993         // raw events
15994          /**
15995          * @event show
15996          * After the popover show
15997          * 
15998          * @param {Roo.bootstrap.Popover} this
15999          */
16000         "show" : true,
16001         /**
16002          * @event hide
16003          * After the popover hide
16004          * 
16005          * @param {Roo.bootstrap.Popover} this
16006          */
16007         "hide" : true
16008     });
16009 };
16010
16011 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16012     
16013     title: 'Fill in a title',
16014     html: false,
16015     
16016     placement : 'right',
16017     trigger : 'hover', // hover
16018     
16019     delay : 0,
16020     
16021     over: 'parent',
16022     
16023     can_build_overlaid : false,
16024     
16025     getChildContainer : function()
16026     {
16027         return this.el.select('.popover-content',true).first();
16028     },
16029     
16030     getAutoCreate : function(){
16031          
16032         var cfg = {
16033            cls : 'popover roo-dynamic',
16034            style: 'display:block',
16035            cn : [
16036                 {
16037                     cls : 'arrow'
16038                 },
16039                 {
16040                     cls : 'popover-inner',
16041                     cn : [
16042                         {
16043                             tag: 'h3',
16044                             cls: 'popover-title',
16045                             html : this.title
16046                         },
16047                         {
16048                             cls : 'popover-content',
16049                             html : this.html
16050                         }
16051                     ]
16052                     
16053                 }
16054            ]
16055         };
16056         
16057         return cfg;
16058     },
16059     setTitle: function(str)
16060     {
16061         this.title = str;
16062         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16063     },
16064     setContent: function(str)
16065     {
16066         this.html = str;
16067         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16068     },
16069     // as it get's added to the bottom of the page.
16070     onRender : function(ct, position)
16071     {
16072         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16073         if(!this.el){
16074             var cfg = Roo.apply({},  this.getAutoCreate());
16075             cfg.id = Roo.id();
16076             
16077             if (this.cls) {
16078                 cfg.cls += ' ' + this.cls;
16079             }
16080             if (this.style) {
16081                 cfg.style = this.style;
16082             }
16083             //Roo.log("adding to ");
16084             this.el = Roo.get(document.body).createChild(cfg, position);
16085 //            Roo.log(this.el);
16086         }
16087         this.initEvents();
16088     },
16089     
16090     initEvents : function()
16091     {
16092         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16093         this.el.enableDisplayMode('block');
16094         this.el.hide();
16095         if (this.over === false) {
16096             return; 
16097         }
16098         if (this.triggers === false) {
16099             return;
16100         }
16101         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16102         var triggers = this.trigger ? this.trigger.split(' ') : [];
16103         Roo.each(triggers, function(trigger) {
16104         
16105             if (trigger == 'click') {
16106                 on_el.on('click', this.toggle, this);
16107             } else if (trigger != 'manual') {
16108                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16109                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16110       
16111                 on_el.on(eventIn  ,this.enter, this);
16112                 on_el.on(eventOut, this.leave, this);
16113             }
16114         }, this);
16115         
16116     },
16117     
16118     
16119     // private
16120     timeout : null,
16121     hoverState : null,
16122     
16123     toggle : function () {
16124         this.hoverState == 'in' ? this.leave() : this.enter();
16125     },
16126     
16127     enter : function () {
16128         
16129         clearTimeout(this.timeout);
16130     
16131         this.hoverState = 'in';
16132     
16133         if (!this.delay || !this.delay.show) {
16134             this.show();
16135             return;
16136         }
16137         var _t = this;
16138         this.timeout = setTimeout(function () {
16139             if (_t.hoverState == 'in') {
16140                 _t.show();
16141             }
16142         }, this.delay.show)
16143     },
16144     
16145     leave : function() {
16146         clearTimeout(this.timeout);
16147     
16148         this.hoverState = 'out';
16149     
16150         if (!this.delay || !this.delay.hide) {
16151             this.hide();
16152             return;
16153         }
16154         var _t = this;
16155         this.timeout = setTimeout(function () {
16156             if (_t.hoverState == 'out') {
16157                 _t.hide();
16158             }
16159         }, this.delay.hide)
16160     },
16161     
16162     show : function (on_el)
16163     {
16164         if (!on_el) {
16165             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16166         }
16167         
16168         // set content.
16169         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16170         if (this.html !== false) {
16171             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16172         }
16173         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16174         if (!this.title.length) {
16175             this.el.select('.popover-title',true).hide();
16176         }
16177         
16178         var placement = typeof this.placement == 'function' ?
16179             this.placement.call(this, this.el, on_el) :
16180             this.placement;
16181             
16182         var autoToken = /\s?auto?\s?/i;
16183         var autoPlace = autoToken.test(placement);
16184         if (autoPlace) {
16185             placement = placement.replace(autoToken, '') || 'top';
16186         }
16187         
16188         //this.el.detach()
16189         //this.el.setXY([0,0]);
16190         this.el.show();
16191         this.el.dom.style.display='block';
16192         this.el.addClass(placement);
16193         
16194         //this.el.appendTo(on_el);
16195         
16196         var p = this.getPosition();
16197         var box = this.el.getBox();
16198         
16199         if (autoPlace) {
16200             // fixme..
16201         }
16202         var align = Roo.bootstrap.Popover.alignment[placement];
16203         this.el.alignTo(on_el, align[0],align[1]);
16204         //var arrow = this.el.select('.arrow',true).first();
16205         //arrow.set(align[2], 
16206         
16207         this.el.addClass('in');
16208         
16209         
16210         if (this.el.hasClass('fade')) {
16211             // fade it?
16212         }
16213         
16214         this.hoverState = 'in';
16215         
16216         this.fireEvent('show', this);
16217         
16218     },
16219     hide : function()
16220     {
16221         this.el.setXY([0,0]);
16222         this.el.removeClass('in');
16223         this.el.hide();
16224         this.hoverState = null;
16225         
16226         this.fireEvent('hide', this);
16227     }
16228     
16229 });
16230
16231 Roo.bootstrap.Popover.alignment = {
16232     'left' : ['r-l', [-10,0], 'right'],
16233     'right' : ['l-r', [10,0], 'left'],
16234     'bottom' : ['t-b', [0,10], 'top'],
16235     'top' : [ 'b-t', [0,-10], 'bottom']
16236 };
16237
16238  /*
16239  * - LGPL
16240  *
16241  * Progress
16242  * 
16243  */
16244
16245 /**
16246  * @class Roo.bootstrap.Progress
16247  * @extends Roo.bootstrap.Component
16248  * Bootstrap Progress class
16249  * @cfg {Boolean} striped striped of the progress bar
16250  * @cfg {Boolean} active animated of the progress bar
16251  * 
16252  * 
16253  * @constructor
16254  * Create a new Progress
16255  * @param {Object} config The config object
16256  */
16257
16258 Roo.bootstrap.Progress = function(config){
16259     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16260 };
16261
16262 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16263     
16264     striped : false,
16265     active: false,
16266     
16267     getAutoCreate : function(){
16268         var cfg = {
16269             tag: 'div',
16270             cls: 'progress'
16271         };
16272         
16273         
16274         if(this.striped){
16275             cfg.cls += ' progress-striped';
16276         }
16277       
16278         if(this.active){
16279             cfg.cls += ' active';
16280         }
16281         
16282         
16283         return cfg;
16284     }
16285    
16286 });
16287
16288  
16289
16290  /*
16291  * - LGPL
16292  *
16293  * ProgressBar
16294  * 
16295  */
16296
16297 /**
16298  * @class Roo.bootstrap.ProgressBar
16299  * @extends Roo.bootstrap.Component
16300  * Bootstrap ProgressBar class
16301  * @cfg {Number} aria_valuenow aria-value now
16302  * @cfg {Number} aria_valuemin aria-value min
16303  * @cfg {Number} aria_valuemax aria-value max
16304  * @cfg {String} label label for the progress bar
16305  * @cfg {String} panel (success | info | warning | danger )
16306  * @cfg {String} role role of the progress bar
16307  * @cfg {String} sr_only text
16308  * 
16309  * 
16310  * @constructor
16311  * Create a new ProgressBar
16312  * @param {Object} config The config object
16313  */
16314
16315 Roo.bootstrap.ProgressBar = function(config){
16316     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16317 };
16318
16319 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16320     
16321     aria_valuenow : 0,
16322     aria_valuemin : 0,
16323     aria_valuemax : 100,
16324     label : false,
16325     panel : false,
16326     role : false,
16327     sr_only: false,
16328     
16329     getAutoCreate : function()
16330     {
16331         
16332         var cfg = {
16333             tag: 'div',
16334             cls: 'progress-bar',
16335             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16336         };
16337         
16338         if(this.sr_only){
16339             cfg.cn = {
16340                 tag: 'span',
16341                 cls: 'sr-only',
16342                 html: this.sr_only
16343             }
16344         }
16345         
16346         if(this.role){
16347             cfg.role = this.role;
16348         }
16349         
16350         if(this.aria_valuenow){
16351             cfg['aria-valuenow'] = this.aria_valuenow;
16352         }
16353         
16354         if(this.aria_valuemin){
16355             cfg['aria-valuemin'] = this.aria_valuemin;
16356         }
16357         
16358         if(this.aria_valuemax){
16359             cfg['aria-valuemax'] = this.aria_valuemax;
16360         }
16361         
16362         if(this.label && !this.sr_only){
16363             cfg.html = this.label;
16364         }
16365         
16366         if(this.panel){
16367             cfg.cls += ' progress-bar-' + this.panel;
16368         }
16369         
16370         return cfg;
16371     },
16372     
16373     update : function(aria_valuenow)
16374     {
16375         this.aria_valuenow = aria_valuenow;
16376         
16377         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16378     }
16379    
16380 });
16381
16382  
16383
16384  /*
16385  * - LGPL
16386  *
16387  * column
16388  * 
16389  */
16390
16391 /**
16392  * @class Roo.bootstrap.TabGroup
16393  * @extends Roo.bootstrap.Column
16394  * Bootstrap Column class
16395  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16396  * @cfg {Boolean} carousel true to make the group behave like a carousel
16397  * @cfg {Boolean} bullets show bullets for the panels
16398  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16399  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16400  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16401  * 
16402  * @constructor
16403  * Create a new TabGroup
16404  * @param {Object} config The config object
16405  */
16406
16407 Roo.bootstrap.TabGroup = function(config){
16408     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16409     if (!this.navId) {
16410         this.navId = Roo.id();
16411     }
16412     this.tabs = [];
16413     Roo.bootstrap.TabGroup.register(this);
16414     
16415 };
16416
16417 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16418     
16419     carousel : false,
16420     transition : false,
16421     bullets : 0,
16422     timer : 0,
16423     autoslide : false,
16424     slideFn : false,
16425     slideOnTouch : false,
16426     
16427     getAutoCreate : function()
16428     {
16429         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16430         
16431         cfg.cls += ' tab-content';
16432         
16433         if (this.carousel) {
16434             cfg.cls += ' carousel slide';
16435             
16436             cfg.cn = [{
16437                cls : 'carousel-inner'
16438             }];
16439         
16440             if(this.bullets  && !Roo.isTouch){
16441                 
16442                 var bullets = {
16443                     cls : 'carousel-bullets',
16444                     cn : []
16445                 };
16446                
16447                 if(this.bullets_cls){
16448                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16449                 }
16450                  /*
16451                 for (var i = 0; i < this.bullets; i++){
16452                     bullets.cn.push({
16453                         cls : 'bullet bullet-' + i
16454                     });
16455                 }
16456                 */
16457                 bullets.cn.push({
16458                     cls : 'clear'
16459                 });
16460                 
16461                 cfg.cn[0].cn = bullets;
16462             }
16463         }
16464         
16465         return cfg;
16466     },
16467     
16468     initEvents:  function()
16469     {
16470         if(Roo.isTouch && this.slideOnTouch){
16471             this.el.on("touchstart", this.onTouchStart, this);
16472         }
16473         
16474         if(this.autoslide){
16475             var _this = this;
16476             
16477             this.slideFn = window.setInterval(function() {
16478                 _this.showPanelNext();
16479             }, this.timer);
16480         }
16481         
16482     },
16483     
16484     onTouchStart : function(e, el, o)
16485     {
16486         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16487             return;
16488         }
16489         
16490         this.showPanelNext();
16491     },
16492     
16493     getChildContainer : function()
16494     {
16495         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16496     },
16497     
16498     /**
16499     * register a Navigation item
16500     * @param {Roo.bootstrap.NavItem} the navitem to add
16501     */
16502     register : function(item)
16503     {
16504         this.tabs.push( item);
16505         item.navId = this.navId; // not really needed..
16506         this.addBullet();
16507     
16508     },
16509     
16510     getActivePanel : function()
16511     {
16512         var r = false;
16513         Roo.each(this.tabs, function(t) {
16514             if (t.active) {
16515                 r = t;
16516                 return false;
16517             }
16518             return null;
16519         });
16520         return r;
16521         
16522     },
16523     getPanelByName : function(n)
16524     {
16525         var r = false;
16526         Roo.each(this.tabs, function(t) {
16527             if (t.tabId == n) {
16528                 r = t;
16529                 return false;
16530             }
16531             return null;
16532         });
16533         return r;
16534     },
16535     indexOfPanel : function(p)
16536     {
16537         var r = false;
16538         Roo.each(this.tabs, function(t,i) {
16539             if (t.tabId == p.tabId) {
16540                 r = i;
16541                 return false;
16542             }
16543             return null;
16544         });
16545         return r;
16546     },
16547     /**
16548      * show a specific panel
16549      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16550      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16551      */
16552     showPanel : function (pan)
16553     {
16554         if(this.transition || typeof(pan) == 'undefined'){
16555             Roo.log("waiting for the transitionend");
16556             return;
16557         }
16558         
16559         if (typeof(pan) == 'number') {
16560             pan = this.tabs[pan];
16561         }
16562         
16563         if (typeof(pan) == 'string') {
16564             pan = this.getPanelByName(pan);
16565         }
16566         
16567         var cur = this.getActivePanel();
16568         
16569         if(!pan || !cur){
16570             Roo.log('pan or acitve pan is undefined');
16571             return false;
16572         }
16573         
16574         if (pan.tabId == this.getActivePanel().tabId) {
16575             return true;
16576         }
16577         
16578         if (false === cur.fireEvent('beforedeactivate')) {
16579             return false;
16580         }
16581         
16582         if(this.bullets > 0 && !Roo.isTouch){
16583             this.setActiveBullet(this.indexOfPanel(pan));
16584         }
16585         
16586         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16587             
16588             this.transition = true;
16589             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16590             var lr = dir == 'next' ? 'left' : 'right';
16591             pan.el.addClass(dir); // or prev
16592             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16593             cur.el.addClass(lr); // or right
16594             pan.el.addClass(lr);
16595             
16596             var _this = this;
16597             cur.el.on('transitionend', function() {
16598                 Roo.log("trans end?");
16599                 
16600                 pan.el.removeClass([lr,dir]);
16601                 pan.setActive(true);
16602                 
16603                 cur.el.removeClass([lr]);
16604                 cur.setActive(false);
16605                 
16606                 _this.transition = false;
16607                 
16608             }, this, { single:  true } );
16609             
16610             return true;
16611         }
16612         
16613         cur.setActive(false);
16614         pan.setActive(true);
16615         
16616         return true;
16617         
16618     },
16619     showPanelNext : function()
16620     {
16621         var i = this.indexOfPanel(this.getActivePanel());
16622         
16623         if (i >= this.tabs.length - 1 && !this.autoslide) {
16624             return;
16625         }
16626         
16627         if (i >= this.tabs.length - 1 && this.autoslide) {
16628             i = -1;
16629         }
16630         
16631         this.showPanel(this.tabs[i+1]);
16632     },
16633     
16634     showPanelPrev : function()
16635     {
16636         var i = this.indexOfPanel(this.getActivePanel());
16637         
16638         if (i  < 1 && !this.autoslide) {
16639             return;
16640         }
16641         
16642         if (i < 1 && this.autoslide) {
16643             i = this.tabs.length;
16644         }
16645         
16646         this.showPanel(this.tabs[i-1]);
16647     },
16648     
16649     
16650     addBullet: function()
16651     {
16652         if(!this.bullets || Roo.isTouch){
16653             return;
16654         }
16655         var ctr = this.el.select('.carousel-bullets',true).first();
16656         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16657         var bullet = ctr.createChild({
16658             cls : 'bullet bullet-' + i
16659         },ctr.dom.lastChild);
16660         
16661         
16662         var _this = this;
16663         
16664         bullet.on('click', (function(e, el, o, ii, t){
16665
16666             e.preventDefault();
16667
16668             this.showPanel(ii);
16669
16670             if(this.autoslide && this.slideFn){
16671                 clearInterval(this.slideFn);
16672                 this.slideFn = window.setInterval(function() {
16673                     _this.showPanelNext();
16674                 }, this.timer);
16675             }
16676
16677         }).createDelegate(this, [i, bullet], true));
16678                 
16679         
16680     },
16681      
16682     setActiveBullet : function(i)
16683     {
16684         if(Roo.isTouch){
16685             return;
16686         }
16687         
16688         Roo.each(this.el.select('.bullet', true).elements, function(el){
16689             el.removeClass('selected');
16690         });
16691
16692         var bullet = this.el.select('.bullet-' + i, true).first();
16693         
16694         if(!bullet){
16695             return;
16696         }
16697         
16698         bullet.addClass('selected');
16699     }
16700     
16701     
16702   
16703 });
16704
16705  
16706
16707  
16708  
16709 Roo.apply(Roo.bootstrap.TabGroup, {
16710     
16711     groups: {},
16712      /**
16713     * register a Navigation Group
16714     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16715     */
16716     register : function(navgrp)
16717     {
16718         this.groups[navgrp.navId] = navgrp;
16719         
16720     },
16721     /**
16722     * fetch a Navigation Group based on the navigation ID
16723     * if one does not exist , it will get created.
16724     * @param {string} the navgroup to add
16725     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16726     */
16727     get: function(navId) {
16728         if (typeof(this.groups[navId]) == 'undefined') {
16729             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16730         }
16731         return this.groups[navId] ;
16732     }
16733     
16734     
16735     
16736 });
16737
16738  /*
16739  * - LGPL
16740  *
16741  * TabPanel
16742  * 
16743  */
16744
16745 /**
16746  * @class Roo.bootstrap.TabPanel
16747  * @extends Roo.bootstrap.Component
16748  * Bootstrap TabPanel class
16749  * @cfg {Boolean} active panel active
16750  * @cfg {String} html panel content
16751  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16752  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16753  * 
16754  * 
16755  * @constructor
16756  * Create a new TabPanel
16757  * @param {Object} config The config object
16758  */
16759
16760 Roo.bootstrap.TabPanel = function(config){
16761     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16762     this.addEvents({
16763         /**
16764              * @event changed
16765              * Fires when the active status changes
16766              * @param {Roo.bootstrap.TabPanel} this
16767              * @param {Boolean} state the new state
16768             
16769          */
16770         'changed': true,
16771         /**
16772              * @event beforedeactivate
16773              * Fires before a tab is de-activated - can be used to do validation on a form.
16774              * @param {Roo.bootstrap.TabPanel} this
16775              * @return {Boolean} false if there is an error
16776             
16777          */
16778         'beforedeactivate': true
16779      });
16780     
16781     this.tabId = this.tabId || Roo.id();
16782   
16783 };
16784
16785 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16786     
16787     active: false,
16788     html: false,
16789     tabId: false,
16790     navId : false,
16791     
16792     getAutoCreate : function(){
16793         var cfg = {
16794             tag: 'div',
16795             // item is needed for carousel - not sure if it has any effect otherwise
16796             cls: 'tab-pane item',
16797             html: this.html || ''
16798         };
16799         
16800         if(this.active){
16801             cfg.cls += ' active';
16802         }
16803         
16804         if(this.tabId){
16805             cfg.tabId = this.tabId;
16806         }
16807         
16808         
16809         return cfg;
16810     },
16811     
16812     initEvents:  function()
16813     {
16814         var p = this.parent();
16815         this.navId = this.navId || p.navId;
16816         
16817         if (typeof(this.navId) != 'undefined') {
16818             // not really needed.. but just in case.. parent should be a NavGroup.
16819             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16820             
16821             tg.register(this);
16822             
16823             var i = tg.tabs.length - 1;
16824             
16825             if(this.active && tg.bullets > 0 && i < tg.bullets){
16826                 tg.setActiveBullet(i);
16827             }
16828         }
16829         
16830     },
16831     
16832     
16833     onRender : function(ct, position)
16834     {
16835        // Roo.log("Call onRender: " + this.xtype);
16836         
16837         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16838         
16839         
16840         
16841         
16842         
16843     },
16844     
16845     setActive: function(state)
16846     {
16847         Roo.log("panel - set active " + this.tabId + "=" + state);
16848         
16849         this.active = state;
16850         if (!state) {
16851             this.el.removeClass('active');
16852             
16853         } else  if (!this.el.hasClass('active')) {
16854             this.el.addClass('active');
16855         }
16856         
16857         this.fireEvent('changed', this, state);
16858     }
16859     
16860     
16861 });
16862  
16863
16864  
16865
16866  /*
16867  * - LGPL
16868  *
16869  * DateField
16870  * 
16871  */
16872
16873 /**
16874  * @class Roo.bootstrap.DateField
16875  * @extends Roo.bootstrap.Input
16876  * Bootstrap DateField class
16877  * @cfg {Number} weekStart default 0
16878  * @cfg {String} viewMode default empty, (months|years)
16879  * @cfg {String} minViewMode default empty, (months|years)
16880  * @cfg {Number} startDate default -Infinity
16881  * @cfg {Number} endDate default Infinity
16882  * @cfg {Boolean} todayHighlight default false
16883  * @cfg {Boolean} todayBtn default false
16884  * @cfg {Boolean} calendarWeeks default false
16885  * @cfg {Object} daysOfWeekDisabled default empty
16886  * @cfg {Boolean} singleMode default false (true | false)
16887  * 
16888  * @cfg {Boolean} keyboardNavigation default true
16889  * @cfg {String} language default en
16890  * 
16891  * @constructor
16892  * Create a new DateField
16893  * @param {Object} config The config object
16894  */
16895
16896 Roo.bootstrap.DateField = function(config){
16897     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16898      this.addEvents({
16899             /**
16900              * @event show
16901              * Fires when this field show.
16902              * @param {Roo.bootstrap.DateField} this
16903              * @param {Mixed} date The date value
16904              */
16905             show : true,
16906             /**
16907              * @event show
16908              * Fires when this field hide.
16909              * @param {Roo.bootstrap.DateField} this
16910              * @param {Mixed} date The date value
16911              */
16912             hide : true,
16913             /**
16914              * @event select
16915              * Fires when select a date.
16916              * @param {Roo.bootstrap.DateField} this
16917              * @param {Mixed} date The date value
16918              */
16919             select : true,
16920             /**
16921              * @event beforeselect
16922              * Fires when before select a date.
16923              * @param {Roo.bootstrap.DateField} this
16924              * @param {Mixed} date The date value
16925              */
16926             beforeselect : true
16927         });
16928 };
16929
16930 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16931     
16932     /**
16933      * @cfg {String} format
16934      * The default date format string which can be overriden for localization support.  The format must be
16935      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16936      */
16937     format : "m/d/y",
16938     /**
16939      * @cfg {String} altFormats
16940      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16941      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16942      */
16943     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16944     
16945     weekStart : 0,
16946     
16947     viewMode : '',
16948     
16949     minViewMode : '',
16950     
16951     todayHighlight : false,
16952     
16953     todayBtn: false,
16954     
16955     language: 'en',
16956     
16957     keyboardNavigation: true,
16958     
16959     calendarWeeks: false,
16960     
16961     startDate: -Infinity,
16962     
16963     endDate: Infinity,
16964     
16965     daysOfWeekDisabled: [],
16966     
16967     _events: [],
16968     
16969     singleMode : false,
16970     
16971     UTCDate: function()
16972     {
16973         return new Date(Date.UTC.apply(Date, arguments));
16974     },
16975     
16976     UTCToday: function()
16977     {
16978         var today = new Date();
16979         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16980     },
16981     
16982     getDate: function() {
16983             var d = this.getUTCDate();
16984             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16985     },
16986     
16987     getUTCDate: function() {
16988             return this.date;
16989     },
16990     
16991     setDate: function(d) {
16992             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16993     },
16994     
16995     setUTCDate: function(d) {
16996             this.date = d;
16997             this.setValue(this.formatDate(this.date));
16998     },
16999         
17000     onRender: function(ct, position)
17001     {
17002         
17003         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17004         
17005         this.language = this.language || 'en';
17006         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17007         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17008         
17009         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17010         this.format = this.format || 'm/d/y';
17011         this.isInline = false;
17012         this.isInput = true;
17013         this.component = this.el.select('.add-on', true).first() || false;
17014         this.component = (this.component && this.component.length === 0) ? false : this.component;
17015         this.hasInput = this.component && this.inputEL().length;
17016         
17017         if (typeof(this.minViewMode === 'string')) {
17018             switch (this.minViewMode) {
17019                 case 'months':
17020                     this.minViewMode = 1;
17021                     break;
17022                 case 'years':
17023                     this.minViewMode = 2;
17024                     break;
17025                 default:
17026                     this.minViewMode = 0;
17027                     break;
17028             }
17029         }
17030         
17031         if (typeof(this.viewMode === 'string')) {
17032             switch (this.viewMode) {
17033                 case 'months':
17034                     this.viewMode = 1;
17035                     break;
17036                 case 'years':
17037                     this.viewMode = 2;
17038                     break;
17039                 default:
17040                     this.viewMode = 0;
17041                     break;
17042             }
17043         }
17044                 
17045         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17046         
17047 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17048         
17049         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17050         
17051         this.picker().on('mousedown', this.onMousedown, this);
17052         this.picker().on('click', this.onClick, this);
17053         
17054         this.picker().addClass('datepicker-dropdown');
17055         
17056         this.startViewMode = this.viewMode;
17057         
17058         if(this.singleMode){
17059             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17060                 v.setVisibilityMode(Roo.Element.DISPLAY);
17061                 v.hide();
17062             });
17063             
17064             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17065                 v.setStyle('width', '189px');
17066             });
17067         }
17068         
17069         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17070             if(!this.calendarWeeks){
17071                 v.remove();
17072                 return;
17073             }
17074             
17075             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17076             v.attr('colspan', function(i, val){
17077                 return parseInt(val) + 1;
17078             });
17079         });
17080                         
17081         
17082         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17083         
17084         this.setStartDate(this.startDate);
17085         this.setEndDate(this.endDate);
17086         
17087         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17088         
17089         this.fillDow();
17090         this.fillMonths();
17091         this.update();
17092         this.showMode();
17093         
17094         if(this.isInline) {
17095             this.show();
17096         }
17097     },
17098     
17099     picker : function()
17100     {
17101         return this.pickerEl;
17102 //        return this.el.select('.datepicker', true).first();
17103     },
17104     
17105     fillDow: function()
17106     {
17107         var dowCnt = this.weekStart;
17108         
17109         var dow = {
17110             tag: 'tr',
17111             cn: [
17112                 
17113             ]
17114         };
17115         
17116         if(this.calendarWeeks){
17117             dow.cn.push({
17118                 tag: 'th',
17119                 cls: 'cw',
17120                 html: '&nbsp;'
17121             })
17122         }
17123         
17124         while (dowCnt < this.weekStart + 7) {
17125             dow.cn.push({
17126                 tag: 'th',
17127                 cls: 'dow',
17128                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17129             });
17130         }
17131         
17132         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17133     },
17134     
17135     fillMonths: function()
17136     {    
17137         var i = 0;
17138         var months = this.picker().select('>.datepicker-months td', true).first();
17139         
17140         months.dom.innerHTML = '';
17141         
17142         while (i < 12) {
17143             var month = {
17144                 tag: 'span',
17145                 cls: 'month',
17146                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17147             };
17148             
17149             months.createChild(month);
17150         }
17151         
17152     },
17153     
17154     update: function()
17155     {
17156         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;
17157         
17158         if (this.date < this.startDate) {
17159             this.viewDate = new Date(this.startDate);
17160         } else if (this.date > this.endDate) {
17161             this.viewDate = new Date(this.endDate);
17162         } else {
17163             this.viewDate = new Date(this.date);
17164         }
17165         
17166         this.fill();
17167     },
17168     
17169     fill: function() 
17170     {
17171         var d = new Date(this.viewDate),
17172                 year = d.getUTCFullYear(),
17173                 month = d.getUTCMonth(),
17174                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17175                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17176                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17177                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17178                 currentDate = this.date && this.date.valueOf(),
17179                 today = this.UTCToday();
17180         
17181         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17182         
17183 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17184         
17185 //        this.picker.select('>tfoot th.today').
17186 //                                              .text(dates[this.language].today)
17187 //                                              .toggle(this.todayBtn !== false);
17188     
17189         this.updateNavArrows();
17190         this.fillMonths();
17191                                                 
17192         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17193         
17194         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17195          
17196         prevMonth.setUTCDate(day);
17197         
17198         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17199         
17200         var nextMonth = new Date(prevMonth);
17201         
17202         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17203         
17204         nextMonth = nextMonth.valueOf();
17205         
17206         var fillMonths = false;
17207         
17208         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17209         
17210         while(prevMonth.valueOf() < nextMonth) {
17211             var clsName = '';
17212             
17213             if (prevMonth.getUTCDay() === this.weekStart) {
17214                 if(fillMonths){
17215                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17216                 }
17217                     
17218                 fillMonths = {
17219                     tag: 'tr',
17220                     cn: []
17221                 };
17222                 
17223                 if(this.calendarWeeks){
17224                     // ISO 8601: First week contains first thursday.
17225                     // ISO also states week starts on Monday, but we can be more abstract here.
17226                     var
17227                     // Start of current week: based on weekstart/current date
17228                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17229                     // Thursday of this week
17230                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17231                     // First Thursday of year, year from thursday
17232                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17233                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17234                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17235                     
17236                     fillMonths.cn.push({
17237                         tag: 'td',
17238                         cls: 'cw',
17239                         html: calWeek
17240                     });
17241                 }
17242             }
17243             
17244             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17245                 clsName += ' old';
17246             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17247                 clsName += ' new';
17248             }
17249             if (this.todayHighlight &&
17250                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17251                 prevMonth.getUTCMonth() == today.getMonth() &&
17252                 prevMonth.getUTCDate() == today.getDate()) {
17253                 clsName += ' today';
17254             }
17255             
17256             if (currentDate && prevMonth.valueOf() === currentDate) {
17257                 clsName += ' active';
17258             }
17259             
17260             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17261                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17262                     clsName += ' disabled';
17263             }
17264             
17265             fillMonths.cn.push({
17266                 tag: 'td',
17267                 cls: 'day ' + clsName,
17268                 html: prevMonth.getDate()
17269             });
17270             
17271             prevMonth.setDate(prevMonth.getDate()+1);
17272         }
17273           
17274         var currentYear = this.date && this.date.getUTCFullYear();
17275         var currentMonth = this.date && this.date.getUTCMonth();
17276         
17277         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17278         
17279         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17280             v.removeClass('active');
17281             
17282             if(currentYear === year && k === currentMonth){
17283                 v.addClass('active');
17284             }
17285             
17286             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17287                 v.addClass('disabled');
17288             }
17289             
17290         });
17291         
17292         
17293         year = parseInt(year/10, 10) * 10;
17294         
17295         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17296         
17297         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17298         
17299         year -= 1;
17300         for (var i = -1; i < 11; i++) {
17301             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17302                 tag: 'span',
17303                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17304                 html: year
17305             });
17306             
17307             year += 1;
17308         }
17309     },
17310     
17311     showMode: function(dir) 
17312     {
17313         if (dir) {
17314             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17315         }
17316         
17317         Roo.each(this.picker().select('>div',true).elements, function(v){
17318             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17319             v.hide();
17320         });
17321         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17322     },
17323     
17324     place: function()
17325     {
17326         if(this.isInline) {
17327             return;
17328         }
17329         
17330         this.picker().removeClass(['bottom', 'top']);
17331         
17332         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17333             /*
17334              * place to the top of element!
17335              *
17336              */
17337             
17338             this.picker().addClass('top');
17339             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17340             
17341             return;
17342         }
17343         
17344         this.picker().addClass('bottom');
17345         
17346         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17347     },
17348     
17349     parseDate : function(value)
17350     {
17351         if(!value || value instanceof Date){
17352             return value;
17353         }
17354         var v = Date.parseDate(value, this.format);
17355         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17356             v = Date.parseDate(value, 'Y-m-d');
17357         }
17358         if(!v && this.altFormats){
17359             if(!this.altFormatsArray){
17360                 this.altFormatsArray = this.altFormats.split("|");
17361             }
17362             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17363                 v = Date.parseDate(value, this.altFormatsArray[i]);
17364             }
17365         }
17366         return v;
17367     },
17368     
17369     formatDate : function(date, fmt)
17370     {   
17371         return (!date || !(date instanceof Date)) ?
17372         date : date.dateFormat(fmt || this.format);
17373     },
17374     
17375     onFocus : function()
17376     {
17377         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17378         this.show();
17379     },
17380     
17381     onBlur : function()
17382     {
17383         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17384         
17385         var d = this.inputEl().getValue();
17386         
17387         this.setValue(d);
17388                 
17389         this.hide();
17390     },
17391     
17392     show : function()
17393     {
17394         this.picker().show();
17395         this.update();
17396         this.place();
17397         
17398         this.fireEvent('show', this, this.date);
17399     },
17400     
17401     hide : function()
17402     {
17403         if(this.isInline) {
17404             return;
17405         }
17406         this.picker().hide();
17407         this.viewMode = this.startViewMode;
17408         this.showMode();
17409         
17410         this.fireEvent('hide', this, this.date);
17411         
17412     },
17413     
17414     onMousedown: function(e)
17415     {
17416         e.stopPropagation();
17417         e.preventDefault();
17418     },
17419     
17420     keyup: function(e)
17421     {
17422         Roo.bootstrap.DateField.superclass.keyup.call(this);
17423         this.update();
17424     },
17425
17426     setValue: function(v)
17427     {
17428         if(this.fireEvent('beforeselect', this, v) !== false){
17429             var d = new Date(this.parseDate(v) ).clearTime();
17430         
17431             if(isNaN(d.getTime())){
17432                 this.date = this.viewDate = '';
17433                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17434                 return;
17435             }
17436
17437             v = this.formatDate(d);
17438
17439             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17440
17441             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17442
17443             this.update();
17444
17445             this.fireEvent('select', this, this.date);
17446         }
17447     },
17448     
17449     getValue: function()
17450     {
17451         return this.formatDate(this.date);
17452     },
17453     
17454     fireKey: function(e)
17455     {
17456         if (!this.picker().isVisible()){
17457             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17458                 this.show();
17459             }
17460             return;
17461         }
17462         
17463         var dateChanged = false,
17464         dir, day, month,
17465         newDate, newViewDate;
17466         
17467         switch(e.keyCode){
17468             case 27: // escape
17469                 this.hide();
17470                 e.preventDefault();
17471                 break;
17472             case 37: // left
17473             case 39: // right
17474                 if (!this.keyboardNavigation) {
17475                     break;
17476                 }
17477                 dir = e.keyCode == 37 ? -1 : 1;
17478                 
17479                 if (e.ctrlKey){
17480                     newDate = this.moveYear(this.date, dir);
17481                     newViewDate = this.moveYear(this.viewDate, dir);
17482                 } else if (e.shiftKey){
17483                     newDate = this.moveMonth(this.date, dir);
17484                     newViewDate = this.moveMonth(this.viewDate, dir);
17485                 } else {
17486                     newDate = new Date(this.date);
17487                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17488                     newViewDate = new Date(this.viewDate);
17489                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17490                 }
17491                 if (this.dateWithinRange(newDate)){
17492                     this.date = newDate;
17493                     this.viewDate = newViewDate;
17494                     this.setValue(this.formatDate(this.date));
17495 //                    this.update();
17496                     e.preventDefault();
17497                     dateChanged = true;
17498                 }
17499                 break;
17500             case 38: // up
17501             case 40: // down
17502                 if (!this.keyboardNavigation) {
17503                     break;
17504                 }
17505                 dir = e.keyCode == 38 ? -1 : 1;
17506                 if (e.ctrlKey){
17507                     newDate = this.moveYear(this.date, dir);
17508                     newViewDate = this.moveYear(this.viewDate, dir);
17509                 } else if (e.shiftKey){
17510                     newDate = this.moveMonth(this.date, dir);
17511                     newViewDate = this.moveMonth(this.viewDate, dir);
17512                 } else {
17513                     newDate = new Date(this.date);
17514                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17515                     newViewDate = new Date(this.viewDate);
17516                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17517                 }
17518                 if (this.dateWithinRange(newDate)){
17519                     this.date = newDate;
17520                     this.viewDate = newViewDate;
17521                     this.setValue(this.formatDate(this.date));
17522 //                    this.update();
17523                     e.preventDefault();
17524                     dateChanged = true;
17525                 }
17526                 break;
17527             case 13: // enter
17528                 this.setValue(this.formatDate(this.date));
17529                 this.hide();
17530                 e.preventDefault();
17531                 break;
17532             case 9: // tab
17533                 this.setValue(this.formatDate(this.date));
17534                 this.hide();
17535                 break;
17536             case 16: // shift
17537             case 17: // ctrl
17538             case 18: // alt
17539                 break;
17540             default :
17541                 this.hide();
17542                 
17543         }
17544     },
17545     
17546     
17547     onClick: function(e) 
17548     {
17549         e.stopPropagation();
17550         e.preventDefault();
17551         
17552         var target = e.getTarget();
17553         
17554         if(target.nodeName.toLowerCase() === 'i'){
17555             target = Roo.get(target).dom.parentNode;
17556         }
17557         
17558         var nodeName = target.nodeName;
17559         var className = target.className;
17560         var html = target.innerHTML;
17561         //Roo.log(nodeName);
17562         
17563         switch(nodeName.toLowerCase()) {
17564             case 'th':
17565                 switch(className) {
17566                     case 'switch':
17567                         this.showMode(1);
17568                         break;
17569                     case 'prev':
17570                     case 'next':
17571                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17572                         switch(this.viewMode){
17573                                 case 0:
17574                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17575                                         break;
17576                                 case 1:
17577                                 case 2:
17578                                         this.viewDate = this.moveYear(this.viewDate, dir);
17579                                         break;
17580                         }
17581                         this.fill();
17582                         break;
17583                     case 'today':
17584                         var date = new Date();
17585                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17586 //                        this.fill()
17587                         this.setValue(this.formatDate(this.date));
17588                         
17589                         this.hide();
17590                         break;
17591                 }
17592                 break;
17593             case 'span':
17594                 if (className.indexOf('disabled') < 0) {
17595                     this.viewDate.setUTCDate(1);
17596                     if (className.indexOf('month') > -1) {
17597                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17598                     } else {
17599                         var year = parseInt(html, 10) || 0;
17600                         this.viewDate.setUTCFullYear(year);
17601                         
17602                     }
17603                     
17604                     if(this.singleMode){
17605                         this.setValue(this.formatDate(this.viewDate));
17606                         this.hide();
17607                         return;
17608                     }
17609                     
17610                     this.showMode(-1);
17611                     this.fill();
17612                 }
17613                 break;
17614                 
17615             case 'td':
17616                 //Roo.log(className);
17617                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17618                     var day = parseInt(html, 10) || 1;
17619                     var year = this.viewDate.getUTCFullYear(),
17620                         month = this.viewDate.getUTCMonth();
17621
17622                     if (className.indexOf('old') > -1) {
17623                         if(month === 0 ){
17624                             month = 11;
17625                             year -= 1;
17626                         }else{
17627                             month -= 1;
17628                         }
17629                     } else if (className.indexOf('new') > -1) {
17630                         if (month == 11) {
17631                             month = 0;
17632                             year += 1;
17633                         } else {
17634                             month += 1;
17635                         }
17636                     }
17637                     //Roo.log([year,month,day]);
17638                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17639                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17640 //                    this.fill();
17641                     //Roo.log(this.formatDate(this.date));
17642                     this.setValue(this.formatDate(this.date));
17643                     this.hide();
17644                 }
17645                 break;
17646         }
17647     },
17648     
17649     setStartDate: function(startDate)
17650     {
17651         this.startDate = startDate || -Infinity;
17652         if (this.startDate !== -Infinity) {
17653             this.startDate = this.parseDate(this.startDate);
17654         }
17655         this.update();
17656         this.updateNavArrows();
17657     },
17658
17659     setEndDate: function(endDate)
17660     {
17661         this.endDate = endDate || Infinity;
17662         if (this.endDate !== Infinity) {
17663             this.endDate = this.parseDate(this.endDate);
17664         }
17665         this.update();
17666         this.updateNavArrows();
17667     },
17668     
17669     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17670     {
17671         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17672         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17673             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17674         }
17675         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17676             return parseInt(d, 10);
17677         });
17678         this.update();
17679         this.updateNavArrows();
17680     },
17681     
17682     updateNavArrows: function() 
17683     {
17684         if(this.singleMode){
17685             return;
17686         }
17687         
17688         var d = new Date(this.viewDate),
17689         year = d.getUTCFullYear(),
17690         month = d.getUTCMonth();
17691         
17692         Roo.each(this.picker().select('.prev', true).elements, function(v){
17693             v.show();
17694             switch (this.viewMode) {
17695                 case 0:
17696
17697                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17698                         v.hide();
17699                     }
17700                     break;
17701                 case 1:
17702                 case 2:
17703                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17704                         v.hide();
17705                     }
17706                     break;
17707             }
17708         });
17709         
17710         Roo.each(this.picker().select('.next', true).elements, function(v){
17711             v.show();
17712             switch (this.viewMode) {
17713                 case 0:
17714
17715                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17716                         v.hide();
17717                     }
17718                     break;
17719                 case 1:
17720                 case 2:
17721                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17722                         v.hide();
17723                     }
17724                     break;
17725             }
17726         })
17727     },
17728     
17729     moveMonth: function(date, dir)
17730     {
17731         if (!dir) {
17732             return date;
17733         }
17734         var new_date = new Date(date.valueOf()),
17735         day = new_date.getUTCDate(),
17736         month = new_date.getUTCMonth(),
17737         mag = Math.abs(dir),
17738         new_month, test;
17739         dir = dir > 0 ? 1 : -1;
17740         if (mag == 1){
17741             test = dir == -1
17742             // If going back one month, make sure month is not current month
17743             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17744             ? function(){
17745                 return new_date.getUTCMonth() == month;
17746             }
17747             // If going forward one month, make sure month is as expected
17748             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17749             : function(){
17750                 return new_date.getUTCMonth() != new_month;
17751             };
17752             new_month = month + dir;
17753             new_date.setUTCMonth(new_month);
17754             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17755             if (new_month < 0 || new_month > 11) {
17756                 new_month = (new_month + 12) % 12;
17757             }
17758         } else {
17759             // For magnitudes >1, move one month at a time...
17760             for (var i=0; i<mag; i++) {
17761                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17762                 new_date = this.moveMonth(new_date, dir);
17763             }
17764             // ...then reset the day, keeping it in the new month
17765             new_month = new_date.getUTCMonth();
17766             new_date.setUTCDate(day);
17767             test = function(){
17768                 return new_month != new_date.getUTCMonth();
17769             };
17770         }
17771         // Common date-resetting loop -- if date is beyond end of month, make it
17772         // end of month
17773         while (test()){
17774             new_date.setUTCDate(--day);
17775             new_date.setUTCMonth(new_month);
17776         }
17777         return new_date;
17778     },
17779
17780     moveYear: function(date, dir)
17781     {
17782         return this.moveMonth(date, dir*12);
17783     },
17784
17785     dateWithinRange: function(date)
17786     {
17787         return date >= this.startDate && date <= this.endDate;
17788     },
17789
17790     
17791     remove: function() 
17792     {
17793         this.picker().remove();
17794     }
17795    
17796 });
17797
17798 Roo.apply(Roo.bootstrap.DateField,  {
17799     
17800     head : {
17801         tag: 'thead',
17802         cn: [
17803         {
17804             tag: 'tr',
17805             cn: [
17806             {
17807                 tag: 'th',
17808                 cls: 'prev',
17809                 html: '<i class="fa fa-arrow-left"/>'
17810             },
17811             {
17812                 tag: 'th',
17813                 cls: 'switch',
17814                 colspan: '5'
17815             },
17816             {
17817                 tag: 'th',
17818                 cls: 'next',
17819                 html: '<i class="fa fa-arrow-right"/>'
17820             }
17821
17822             ]
17823         }
17824         ]
17825     },
17826     
17827     content : {
17828         tag: 'tbody',
17829         cn: [
17830         {
17831             tag: 'tr',
17832             cn: [
17833             {
17834                 tag: 'td',
17835                 colspan: '7'
17836             }
17837             ]
17838         }
17839         ]
17840     },
17841     
17842     footer : {
17843         tag: 'tfoot',
17844         cn: [
17845         {
17846             tag: 'tr',
17847             cn: [
17848             {
17849                 tag: 'th',
17850                 colspan: '7',
17851                 cls: 'today'
17852             }
17853                     
17854             ]
17855         }
17856         ]
17857     },
17858     
17859     dates:{
17860         en: {
17861             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17862             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17863             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17864             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17865             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17866             today: "Today"
17867         }
17868     },
17869     
17870     modes: [
17871     {
17872         clsName: 'days',
17873         navFnc: 'Month',
17874         navStep: 1
17875     },
17876     {
17877         clsName: 'months',
17878         navFnc: 'FullYear',
17879         navStep: 1
17880     },
17881     {
17882         clsName: 'years',
17883         navFnc: 'FullYear',
17884         navStep: 10
17885     }]
17886 });
17887
17888 Roo.apply(Roo.bootstrap.DateField,  {
17889   
17890     template : {
17891         tag: 'div',
17892         cls: 'datepicker dropdown-menu roo-dynamic',
17893         cn: [
17894         {
17895             tag: 'div',
17896             cls: 'datepicker-days',
17897             cn: [
17898             {
17899                 tag: 'table',
17900                 cls: 'table-condensed',
17901                 cn:[
17902                 Roo.bootstrap.DateField.head,
17903                 {
17904                     tag: 'tbody'
17905                 },
17906                 Roo.bootstrap.DateField.footer
17907                 ]
17908             }
17909             ]
17910         },
17911         {
17912             tag: 'div',
17913             cls: 'datepicker-months',
17914             cn: [
17915             {
17916                 tag: 'table',
17917                 cls: 'table-condensed',
17918                 cn:[
17919                 Roo.bootstrap.DateField.head,
17920                 Roo.bootstrap.DateField.content,
17921                 Roo.bootstrap.DateField.footer
17922                 ]
17923             }
17924             ]
17925         },
17926         {
17927             tag: 'div',
17928             cls: 'datepicker-years',
17929             cn: [
17930             {
17931                 tag: 'table',
17932                 cls: 'table-condensed',
17933                 cn:[
17934                 Roo.bootstrap.DateField.head,
17935                 Roo.bootstrap.DateField.content,
17936                 Roo.bootstrap.DateField.footer
17937                 ]
17938             }
17939             ]
17940         }
17941         ]
17942     }
17943 });
17944
17945  
17946
17947  /*
17948  * - LGPL
17949  *
17950  * TimeField
17951  * 
17952  */
17953
17954 /**
17955  * @class Roo.bootstrap.TimeField
17956  * @extends Roo.bootstrap.Input
17957  * Bootstrap DateField class
17958  * 
17959  * 
17960  * @constructor
17961  * Create a new TimeField
17962  * @param {Object} config The config object
17963  */
17964
17965 Roo.bootstrap.TimeField = function(config){
17966     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17967     this.addEvents({
17968             /**
17969              * @event show
17970              * Fires when this field show.
17971              * @param {Roo.bootstrap.DateField} thisthis
17972              * @param {Mixed} date The date value
17973              */
17974             show : true,
17975             /**
17976              * @event show
17977              * Fires when this field hide.
17978              * @param {Roo.bootstrap.DateField} this
17979              * @param {Mixed} date The date value
17980              */
17981             hide : true,
17982             /**
17983              * @event select
17984              * Fires when select a date.
17985              * @param {Roo.bootstrap.DateField} this
17986              * @param {Mixed} date The date value
17987              */
17988             select : true
17989         });
17990 };
17991
17992 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17993     
17994     /**
17995      * @cfg {String} format
17996      * The default time format string which can be overriden for localization support.  The format must be
17997      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17998      */
17999     format : "H:i",
18000        
18001     onRender: function(ct, position)
18002     {
18003         
18004         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18005                 
18006         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18007         
18008         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18009         
18010         this.pop = this.picker().select('>.datepicker-time',true).first();
18011         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18012         
18013         this.picker().on('mousedown', this.onMousedown, this);
18014         this.picker().on('click', this.onClick, this);
18015         
18016         this.picker().addClass('datepicker-dropdown');
18017     
18018         this.fillTime();
18019         this.update();
18020             
18021         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18022         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18023         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18024         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18025         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18026         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18027
18028     },
18029     
18030     fireKey: function(e){
18031         if (!this.picker().isVisible()){
18032             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18033                 this.show();
18034             }
18035             return;
18036         }
18037
18038         e.preventDefault();
18039         
18040         switch(e.keyCode){
18041             case 27: // escape
18042                 this.hide();
18043                 break;
18044             case 37: // left
18045             case 39: // right
18046                 this.onTogglePeriod();
18047                 break;
18048             case 38: // up
18049                 this.onIncrementMinutes();
18050                 break;
18051             case 40: // down
18052                 this.onDecrementMinutes();
18053                 break;
18054             case 13: // enter
18055             case 9: // tab
18056                 this.setTime();
18057                 break;
18058         }
18059     },
18060     
18061     onClick: function(e) {
18062         e.stopPropagation();
18063         e.preventDefault();
18064     },
18065     
18066     picker : function()
18067     {
18068         return this.el.select('.datepicker', true).first();
18069     },
18070     
18071     fillTime: function()
18072     {    
18073         var time = this.pop.select('tbody', true).first();
18074         
18075         time.dom.innerHTML = '';
18076         
18077         time.createChild({
18078             tag: 'tr',
18079             cn: [
18080                 {
18081                     tag: 'td',
18082                     cn: [
18083                         {
18084                             tag: 'a',
18085                             href: '#',
18086                             cls: 'btn',
18087                             cn: [
18088                                 {
18089                                     tag: 'span',
18090                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18091                                 }
18092                             ]
18093                         } 
18094                     ]
18095                 },
18096                 {
18097                     tag: 'td',
18098                     cls: 'separator'
18099                 },
18100                 {
18101                     tag: 'td',
18102                     cn: [
18103                         {
18104                             tag: 'a',
18105                             href: '#',
18106                             cls: 'btn',
18107                             cn: [
18108                                 {
18109                                     tag: 'span',
18110                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18111                                 }
18112                             ]
18113                         }
18114                     ]
18115                 },
18116                 {
18117                     tag: 'td',
18118                     cls: 'separator'
18119                 }
18120             ]
18121         });
18122         
18123         time.createChild({
18124             tag: 'tr',
18125             cn: [
18126                 {
18127                     tag: 'td',
18128                     cn: [
18129                         {
18130                             tag: 'span',
18131                             cls: 'timepicker-hour',
18132                             html: '00'
18133                         }  
18134                     ]
18135                 },
18136                 {
18137                     tag: 'td',
18138                     cls: 'separator',
18139                     html: ':'
18140                 },
18141                 {
18142                     tag: 'td',
18143                     cn: [
18144                         {
18145                             tag: 'span',
18146                             cls: 'timepicker-minute',
18147                             html: '00'
18148                         }  
18149                     ]
18150                 },
18151                 {
18152                     tag: 'td',
18153                     cls: 'separator'
18154                 },
18155                 {
18156                     tag: 'td',
18157                     cn: [
18158                         {
18159                             tag: 'button',
18160                             type: 'button',
18161                             cls: 'btn btn-primary period',
18162                             html: 'AM'
18163                             
18164                         }
18165                     ]
18166                 }
18167             ]
18168         });
18169         
18170         time.createChild({
18171             tag: 'tr',
18172             cn: [
18173                 {
18174                     tag: 'td',
18175                     cn: [
18176                         {
18177                             tag: 'a',
18178                             href: '#',
18179                             cls: 'btn',
18180                             cn: [
18181                                 {
18182                                     tag: 'span',
18183                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18184                                 }
18185                             ]
18186                         }
18187                     ]
18188                 },
18189                 {
18190                     tag: 'td',
18191                     cls: 'separator'
18192                 },
18193                 {
18194                     tag: 'td',
18195                     cn: [
18196                         {
18197                             tag: 'a',
18198                             href: '#',
18199                             cls: 'btn',
18200                             cn: [
18201                                 {
18202                                     tag: 'span',
18203                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18204                                 }
18205                             ]
18206                         }
18207                     ]
18208                 },
18209                 {
18210                     tag: 'td',
18211                     cls: 'separator'
18212                 }
18213             ]
18214         });
18215         
18216     },
18217     
18218     update: function()
18219     {
18220         
18221         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18222         
18223         this.fill();
18224     },
18225     
18226     fill: function() 
18227     {
18228         var hours = this.time.getHours();
18229         var minutes = this.time.getMinutes();
18230         var period = 'AM';
18231         
18232         if(hours > 11){
18233             period = 'PM';
18234         }
18235         
18236         if(hours == 0){
18237             hours = 12;
18238         }
18239         
18240         
18241         if(hours > 12){
18242             hours = hours - 12;
18243         }
18244         
18245         if(hours < 10){
18246             hours = '0' + hours;
18247         }
18248         
18249         if(minutes < 10){
18250             minutes = '0' + minutes;
18251         }
18252         
18253         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18254         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18255         this.pop.select('button', true).first().dom.innerHTML = period;
18256         
18257     },
18258     
18259     place: function()
18260     {   
18261         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18262         
18263         var cls = ['bottom'];
18264         
18265         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18266             cls.pop();
18267             cls.push('top');
18268         }
18269         
18270         cls.push('right');
18271         
18272         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18273             cls.pop();
18274             cls.push('left');
18275         }
18276         
18277         this.picker().addClass(cls.join('-'));
18278         
18279         var _this = this;
18280         
18281         Roo.each(cls, function(c){
18282             if(c == 'bottom'){
18283                 _this.picker().setTop(_this.inputEl().getHeight());
18284                 return;
18285             }
18286             if(c == 'top'){
18287                 _this.picker().setTop(0 - _this.picker().getHeight());
18288                 return;
18289             }
18290             
18291             if(c == 'left'){
18292                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18293                 return;
18294             }
18295             if(c == 'right'){
18296                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18297                 return;
18298             }
18299         });
18300         
18301     },
18302   
18303     onFocus : function()
18304     {
18305         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18306         this.show();
18307     },
18308     
18309     onBlur : function()
18310     {
18311         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18312         this.hide();
18313     },
18314     
18315     show : function()
18316     {
18317         this.picker().show();
18318         this.pop.show();
18319         this.update();
18320         this.place();
18321         
18322         this.fireEvent('show', this, this.date);
18323     },
18324     
18325     hide : function()
18326     {
18327         this.picker().hide();
18328         this.pop.hide();
18329         
18330         this.fireEvent('hide', this, this.date);
18331     },
18332     
18333     setTime : function()
18334     {
18335         this.hide();
18336         this.setValue(this.time.format(this.format));
18337         
18338         this.fireEvent('select', this, this.date);
18339         
18340         
18341     },
18342     
18343     onMousedown: function(e){
18344         e.stopPropagation();
18345         e.preventDefault();
18346     },
18347     
18348     onIncrementHours: function()
18349     {
18350         Roo.log('onIncrementHours');
18351         this.time = this.time.add(Date.HOUR, 1);
18352         this.update();
18353         
18354     },
18355     
18356     onDecrementHours: function()
18357     {
18358         Roo.log('onDecrementHours');
18359         this.time = this.time.add(Date.HOUR, -1);
18360         this.update();
18361     },
18362     
18363     onIncrementMinutes: function()
18364     {
18365         Roo.log('onIncrementMinutes');
18366         this.time = this.time.add(Date.MINUTE, 1);
18367         this.update();
18368     },
18369     
18370     onDecrementMinutes: function()
18371     {
18372         Roo.log('onDecrementMinutes');
18373         this.time = this.time.add(Date.MINUTE, -1);
18374         this.update();
18375     },
18376     
18377     onTogglePeriod: function()
18378     {
18379         Roo.log('onTogglePeriod');
18380         this.time = this.time.add(Date.HOUR, 12);
18381         this.update();
18382     }
18383     
18384    
18385 });
18386
18387 Roo.apply(Roo.bootstrap.TimeField,  {
18388     
18389     content : {
18390         tag: 'tbody',
18391         cn: [
18392             {
18393                 tag: 'tr',
18394                 cn: [
18395                 {
18396                     tag: 'td',
18397                     colspan: '7'
18398                 }
18399                 ]
18400             }
18401         ]
18402     },
18403     
18404     footer : {
18405         tag: 'tfoot',
18406         cn: [
18407             {
18408                 tag: 'tr',
18409                 cn: [
18410                 {
18411                     tag: 'th',
18412                     colspan: '7',
18413                     cls: '',
18414                     cn: [
18415                         {
18416                             tag: 'button',
18417                             cls: 'btn btn-info ok',
18418                             html: 'OK'
18419                         }
18420                     ]
18421                 }
18422
18423                 ]
18424             }
18425         ]
18426     }
18427 });
18428
18429 Roo.apply(Roo.bootstrap.TimeField,  {
18430   
18431     template : {
18432         tag: 'div',
18433         cls: 'datepicker dropdown-menu',
18434         cn: [
18435             {
18436                 tag: 'div',
18437                 cls: 'datepicker-time',
18438                 cn: [
18439                 {
18440                     tag: 'table',
18441                     cls: 'table-condensed',
18442                     cn:[
18443                     Roo.bootstrap.TimeField.content,
18444                     Roo.bootstrap.TimeField.footer
18445                     ]
18446                 }
18447                 ]
18448             }
18449         ]
18450     }
18451 });
18452
18453  
18454
18455  /*
18456  * - LGPL
18457  *
18458  * MonthField
18459  * 
18460  */
18461
18462 /**
18463  * @class Roo.bootstrap.MonthField
18464  * @extends Roo.bootstrap.Input
18465  * Bootstrap MonthField class
18466  * 
18467  * @cfg {String} language default en
18468  * 
18469  * @constructor
18470  * Create a new MonthField
18471  * @param {Object} config The config object
18472  */
18473
18474 Roo.bootstrap.MonthField = function(config){
18475     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18476     
18477     this.addEvents({
18478         /**
18479          * @event show
18480          * Fires when this field show.
18481          * @param {Roo.bootstrap.MonthField} this
18482          * @param {Mixed} date The date value
18483          */
18484         show : true,
18485         /**
18486          * @event show
18487          * Fires when this field hide.
18488          * @param {Roo.bootstrap.MonthField} this
18489          * @param {Mixed} date The date value
18490          */
18491         hide : true,
18492         /**
18493          * @event select
18494          * Fires when select a date.
18495          * @param {Roo.bootstrap.MonthField} this
18496          * @param {String} oldvalue The old value
18497          * @param {String} newvalue The new value
18498          */
18499         select : true
18500     });
18501 };
18502
18503 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18504     
18505     onRender: function(ct, position)
18506     {
18507         
18508         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18509         
18510         this.language = this.language || 'en';
18511         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18512         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18513         
18514         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18515         this.isInline = false;
18516         this.isInput = true;
18517         this.component = this.el.select('.add-on', true).first() || false;
18518         this.component = (this.component && this.component.length === 0) ? false : this.component;
18519         this.hasInput = this.component && this.inputEL().length;
18520         
18521         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18522         
18523         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18524         
18525         this.picker().on('mousedown', this.onMousedown, this);
18526         this.picker().on('click', this.onClick, this);
18527         
18528         this.picker().addClass('datepicker-dropdown');
18529         
18530         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18531             v.setStyle('width', '189px');
18532         });
18533         
18534         this.fillMonths();
18535         
18536         this.update();
18537         
18538         if(this.isInline) {
18539             this.show();
18540         }
18541         
18542     },
18543     
18544     setValue: function(v, suppressEvent)
18545     {   
18546         var o = this.getValue();
18547         
18548         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18549         
18550         this.update();
18551
18552         if(suppressEvent !== true){
18553             this.fireEvent('select', this, o, v);
18554         }
18555         
18556     },
18557     
18558     getValue: function()
18559     {
18560         return this.value;
18561     },
18562     
18563     onClick: function(e) 
18564     {
18565         e.stopPropagation();
18566         e.preventDefault();
18567         
18568         var target = e.getTarget();
18569         
18570         if(target.nodeName.toLowerCase() === 'i'){
18571             target = Roo.get(target).dom.parentNode;
18572         }
18573         
18574         var nodeName = target.nodeName;
18575         var className = target.className;
18576         var html = target.innerHTML;
18577         
18578         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18579             return;
18580         }
18581         
18582         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18583         
18584         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18585         
18586         this.hide();
18587                         
18588     },
18589     
18590     picker : function()
18591     {
18592         return this.pickerEl;
18593     },
18594     
18595     fillMonths: function()
18596     {    
18597         var i = 0;
18598         var months = this.picker().select('>.datepicker-months td', true).first();
18599         
18600         months.dom.innerHTML = '';
18601         
18602         while (i < 12) {
18603             var month = {
18604                 tag: 'span',
18605                 cls: 'month',
18606                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18607             };
18608             
18609             months.createChild(month);
18610         }
18611         
18612     },
18613     
18614     update: function()
18615     {
18616         var _this = this;
18617         
18618         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18619             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18620         }
18621         
18622         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18623             e.removeClass('active');
18624             
18625             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18626                 e.addClass('active');
18627             }
18628         })
18629     },
18630     
18631     place: function()
18632     {
18633         if(this.isInline) {
18634             return;
18635         }
18636         
18637         this.picker().removeClass(['bottom', 'top']);
18638         
18639         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18640             /*
18641              * place to the top of element!
18642              *
18643              */
18644             
18645             this.picker().addClass('top');
18646             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18647             
18648             return;
18649         }
18650         
18651         this.picker().addClass('bottom');
18652         
18653         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18654     },
18655     
18656     onFocus : function()
18657     {
18658         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18659         this.show();
18660     },
18661     
18662     onBlur : function()
18663     {
18664         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18665         
18666         var d = this.inputEl().getValue();
18667         
18668         this.setValue(d);
18669                 
18670         this.hide();
18671     },
18672     
18673     show : function()
18674     {
18675         this.picker().show();
18676         this.picker().select('>.datepicker-months', true).first().show();
18677         this.update();
18678         this.place();
18679         
18680         this.fireEvent('show', this, this.date);
18681     },
18682     
18683     hide : function()
18684     {
18685         if(this.isInline) {
18686             return;
18687         }
18688         this.picker().hide();
18689         this.fireEvent('hide', this, this.date);
18690         
18691     },
18692     
18693     onMousedown: function(e)
18694     {
18695         e.stopPropagation();
18696         e.preventDefault();
18697     },
18698     
18699     keyup: function(e)
18700     {
18701         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18702         this.update();
18703     },
18704
18705     fireKey: function(e)
18706     {
18707         if (!this.picker().isVisible()){
18708             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18709                 this.show();
18710             }
18711             return;
18712         }
18713         
18714         var dir;
18715         
18716         switch(e.keyCode){
18717             case 27: // escape
18718                 this.hide();
18719                 e.preventDefault();
18720                 break;
18721             case 37: // left
18722             case 39: // right
18723                 dir = e.keyCode == 37 ? -1 : 1;
18724                 
18725                 this.vIndex = this.vIndex + dir;
18726                 
18727                 if(this.vIndex < 0){
18728                     this.vIndex = 0;
18729                 }
18730                 
18731                 if(this.vIndex > 11){
18732                     this.vIndex = 11;
18733                 }
18734                 
18735                 if(isNaN(this.vIndex)){
18736                     this.vIndex = 0;
18737                 }
18738                 
18739                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18740                 
18741                 break;
18742             case 38: // up
18743             case 40: // down
18744                 
18745                 dir = e.keyCode == 38 ? -1 : 1;
18746                 
18747                 this.vIndex = this.vIndex + dir * 4;
18748                 
18749                 if(this.vIndex < 0){
18750                     this.vIndex = 0;
18751                 }
18752                 
18753                 if(this.vIndex > 11){
18754                     this.vIndex = 11;
18755                 }
18756                 
18757                 if(isNaN(this.vIndex)){
18758                     this.vIndex = 0;
18759                 }
18760                 
18761                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18762                 break;
18763                 
18764             case 13: // enter
18765                 
18766                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18767                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18768                 }
18769                 
18770                 this.hide();
18771                 e.preventDefault();
18772                 break;
18773             case 9: // tab
18774                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18775                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18776                 }
18777                 this.hide();
18778                 break;
18779             case 16: // shift
18780             case 17: // ctrl
18781             case 18: // alt
18782                 break;
18783             default :
18784                 this.hide();
18785                 
18786         }
18787     },
18788     
18789     remove: function() 
18790     {
18791         this.picker().remove();
18792     }
18793    
18794 });
18795
18796 Roo.apply(Roo.bootstrap.MonthField,  {
18797     
18798     content : {
18799         tag: 'tbody',
18800         cn: [
18801         {
18802             tag: 'tr',
18803             cn: [
18804             {
18805                 tag: 'td',
18806                 colspan: '7'
18807             }
18808             ]
18809         }
18810         ]
18811     },
18812     
18813     dates:{
18814         en: {
18815             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18816             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18817         }
18818     }
18819 });
18820
18821 Roo.apply(Roo.bootstrap.MonthField,  {
18822   
18823     template : {
18824         tag: 'div',
18825         cls: 'datepicker dropdown-menu roo-dynamic',
18826         cn: [
18827             {
18828                 tag: 'div',
18829                 cls: 'datepicker-months',
18830                 cn: [
18831                 {
18832                     tag: 'table',
18833                     cls: 'table-condensed',
18834                     cn:[
18835                         Roo.bootstrap.DateField.content
18836                     ]
18837                 }
18838                 ]
18839             }
18840         ]
18841     }
18842 });
18843
18844  
18845
18846  
18847  /*
18848  * - LGPL
18849  *
18850  * CheckBox
18851  * 
18852  */
18853
18854 /**
18855  * @class Roo.bootstrap.CheckBox
18856  * @extends Roo.bootstrap.Input
18857  * Bootstrap CheckBox class
18858  * 
18859  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18860  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18861  * @cfg {String} boxLabel The text that appears beside the checkbox
18862  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18863  * @cfg {Boolean} checked initnal the element
18864  * @cfg {Boolean} inline inline the element (default false)
18865  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18866  * 
18867  * @constructor
18868  * Create a new CheckBox
18869  * @param {Object} config The config object
18870  */
18871
18872 Roo.bootstrap.CheckBox = function(config){
18873     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18874    
18875     this.addEvents({
18876         /**
18877         * @event check
18878         * Fires when the element is checked or unchecked.
18879         * @param {Roo.bootstrap.CheckBox} this This input
18880         * @param {Boolean} checked The new checked value
18881         */
18882        check : true
18883     });
18884     
18885 };
18886
18887 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18888   
18889     inputType: 'checkbox',
18890     inputValue: 1,
18891     valueOff: 0,
18892     boxLabel: false,
18893     checked: false,
18894     weight : false,
18895     inline: false,
18896     
18897     getAutoCreate : function()
18898     {
18899         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18900         
18901         var id = Roo.id();
18902         
18903         var cfg = {};
18904         
18905         cfg.cls = 'form-group ' + this.inputType; //input-group
18906         
18907         if(this.inline){
18908             cfg.cls += ' ' + this.inputType + '-inline';
18909         }
18910         
18911         var input =  {
18912             tag: 'input',
18913             id : id,
18914             type : this.inputType,
18915             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18916             cls : 'roo-' + this.inputType, //'form-box',
18917             placeholder : this.placeholder || ''
18918             
18919         };
18920         
18921         if (this.weight) { // Validity check?
18922             cfg.cls += " " + this.inputType + "-" + this.weight;
18923         }
18924         
18925         if (this.disabled) {
18926             input.disabled=true;
18927         }
18928         
18929         if(this.checked){
18930             input.checked = this.checked;
18931         }
18932         
18933         if (this.name) {
18934             input.name = this.name;
18935         }
18936         
18937         if (this.size) {
18938             input.cls += ' input-' + this.size;
18939         }
18940         
18941         var settings=this;
18942         
18943         ['xs','sm','md','lg'].map(function(size){
18944             if (settings[size]) {
18945                 cfg.cls += ' col-' + size + '-' + settings[size];
18946             }
18947         });
18948         
18949         var inputblock = input;
18950          
18951         if (this.before || this.after) {
18952             
18953             inputblock = {
18954                 cls : 'input-group',
18955                 cn :  [] 
18956             };
18957             
18958             if (this.before) {
18959                 inputblock.cn.push({
18960                     tag :'span',
18961                     cls : 'input-group-addon',
18962                     html : this.before
18963                 });
18964             }
18965             
18966             inputblock.cn.push(input);
18967             
18968             if (this.after) {
18969                 inputblock.cn.push({
18970                     tag :'span',
18971                     cls : 'input-group-addon',
18972                     html : this.after
18973                 });
18974             }
18975             
18976         }
18977         
18978         if (align ==='left' && this.fieldLabel.length) {
18979 //                Roo.log("left and has label");
18980                 cfg.cn = [
18981                     
18982                     {
18983                         tag: 'label',
18984                         'for' :  id,
18985                         cls : 'control-label col-md-' + this.labelWidth,
18986                         html : this.fieldLabel
18987                         
18988                     },
18989                     {
18990                         cls : "col-md-" + (12 - this.labelWidth), 
18991                         cn: [
18992                             inputblock
18993                         ]
18994                     }
18995                     
18996                 ];
18997         } else if ( this.fieldLabel.length) {
18998 //                Roo.log(" label");
18999                 cfg.cn = [
19000                    
19001                     {
19002                         tag: this.boxLabel ? 'span' : 'label',
19003                         'for': id,
19004                         cls: 'control-label box-input-label',
19005                         //cls : 'input-group-addon',
19006                         html : this.fieldLabel
19007                         
19008                     },
19009                     
19010                     inputblock
19011                     
19012                 ];
19013
19014         } else {
19015             
19016 //                Roo.log(" no label && no align");
19017                 cfg.cn = [  inputblock ] ;
19018                 
19019                 
19020         }
19021         
19022         if(this.boxLabel){
19023              var boxLabelCfg = {
19024                 tag: 'label',
19025                 //'for': id, // box label is handled by onclick - so no for...
19026                 cls: 'box-label',
19027                 html: this.boxLabel
19028             };
19029             
19030             if(this.tooltip){
19031                 boxLabelCfg.tooltip = this.tooltip;
19032             }
19033              
19034             cfg.cn.push(boxLabelCfg);
19035         }
19036         
19037         
19038        
19039         return cfg;
19040         
19041     },
19042     
19043     /**
19044      * return the real input element.
19045      */
19046     inputEl: function ()
19047     {
19048         return this.el.select('input.roo-' + this.inputType,true).first();
19049     },
19050     
19051     labelEl: function()
19052     {
19053         return this.el.select('label.control-label',true).first();
19054     },
19055     /* depricated... */
19056     
19057     label: function()
19058     {
19059         return this.labelEl();
19060     },
19061     
19062     boxLabelEl: function()
19063     {
19064         return this.el.select('label.box-label',true).first();
19065     },
19066     
19067     initEvents : function()
19068     {
19069 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19070         
19071         this.inputEl().on('click', this.onClick,  this);
19072         
19073         if (this.boxLabel) { 
19074             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19075         }
19076         
19077         this.startValue = this.getValue();
19078         
19079         if(this.groupId){
19080             Roo.bootstrap.CheckBox.register(this);
19081         }
19082     },
19083     
19084     onClick : function()
19085     {   
19086         this.setChecked(!this.checked);
19087     },
19088     
19089     setChecked : function(state,suppressEvent)
19090     {
19091         this.startValue = this.getValue();
19092         
19093         if(this.inputType == 'radio'){
19094             
19095             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19096                 e.dom.checked = false;
19097             });
19098             
19099             this.inputEl().dom.checked = true;
19100             
19101             this.inputEl().dom.value = this.inputValue;
19102             
19103             if(suppressEvent !== true){
19104                 this.fireEvent('check', this, true);
19105             }
19106             
19107             this.validate();
19108             
19109             return;
19110         }
19111         
19112         this.checked = state;
19113         
19114         this.inputEl().dom.checked = state;
19115         
19116         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19117         
19118         if(suppressEvent !== true){
19119             this.fireEvent('check', this, state);
19120         }
19121         
19122         this.validate();
19123     },
19124     
19125     getValue : function()
19126     {
19127         if(this.inputType == 'radio'){
19128             return this.getGroupValue();
19129         }
19130         
19131         return this.inputEl().getValue();
19132         
19133     },
19134     
19135     getGroupValue : function()
19136     {
19137         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19138             return '';
19139         }
19140         
19141         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19142     },
19143     
19144     setValue : function(v,suppressEvent)
19145     {
19146         if(this.inputType == 'radio'){
19147             this.setGroupValue(v, suppressEvent);
19148             return;
19149         }
19150         
19151         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19152         
19153         this.validate();
19154     },
19155     
19156     setGroupValue : function(v, suppressEvent)
19157     {
19158         this.startValue = this.getValue();
19159         
19160         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19161             e.dom.checked = false;
19162             
19163             if(e.dom.value == v){
19164                 e.dom.checked = true;
19165             }
19166         });
19167         
19168         if(suppressEvent !== true){
19169             this.fireEvent('check', this, true);
19170         }
19171
19172         this.validate();
19173         
19174         return;
19175     },
19176     
19177     validate : function()
19178     {
19179         if(
19180                 this.disabled || 
19181                 (this.inputType == 'radio' && this.validateRadio()) ||
19182                 (this.inputType == 'checkbox' && this.validateCheckbox())
19183         ){
19184             this.markValid();
19185             return true;
19186         }
19187         
19188         this.markInvalid();
19189         return false;
19190     },
19191     
19192     validateRadio : function()
19193     {
19194         var valid = false;
19195         
19196         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19197             if(!e.dom.checked){
19198                 return;
19199             }
19200             
19201             valid = true;
19202             
19203             return false;
19204         });
19205         
19206         return valid;
19207     },
19208     
19209     validateCheckbox : function()
19210     {
19211         if(!this.groupId){
19212             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19213         }
19214         
19215         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19216         
19217         if(!group){
19218             return false;
19219         }
19220         
19221         var r = false;
19222         
19223         for(var i in group){
19224             if(r){
19225                 break;
19226             }
19227             
19228             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19229         }
19230         
19231         return r;
19232     },
19233     
19234     /**
19235      * Mark this field as valid
19236      */
19237     markValid : function()
19238     {
19239         if(this.allowBlank){
19240             return;
19241         }
19242         
19243         var _this = this;
19244         
19245         this.fireEvent('valid', this);
19246         
19247         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19248         
19249         if(this.groupId){
19250             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19251         }
19252         
19253         if(label){
19254             label.markValid();
19255         }
19256         
19257         if(this.inputType == 'radio'){
19258             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19259                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19260                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19261             });
19262             
19263             return;
19264         }
19265         
19266         if(!this.groupId){
19267             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19268             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19269             return;
19270         }
19271         
19272         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19273             
19274         if(!group){
19275             return;
19276         }
19277         
19278         for(var i in group){
19279             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19280             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19281         }
19282     },
19283     
19284      /**
19285      * Mark this field as invalid
19286      * @param {String} msg The validation message
19287      */
19288     markInvalid : function(msg)
19289     {
19290         if(this.allowBlank){
19291             return;
19292         }
19293         
19294         var _this = this;
19295         
19296         this.fireEvent('invalid', this, msg);
19297         
19298         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19299         
19300         if(this.groupId){
19301             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19302         }
19303         
19304         if(label){
19305             label.markInvalid();
19306         }
19307             
19308         if(this.inputType == 'radio'){
19309             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19310                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19311                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19312             });
19313             
19314             return;
19315         }
19316         
19317         if(!this.groupId){
19318             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19319             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19320             return;
19321         }
19322         
19323         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19324         
19325         if(!group){
19326             return;
19327         }
19328         
19329         for(var i in group){
19330             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19331             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19332         }
19333         
19334     }
19335     
19336 });
19337
19338 Roo.apply(Roo.bootstrap.CheckBox, {
19339     
19340     groups: {},
19341     
19342      /**
19343     * register a CheckBox Group
19344     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19345     */
19346     register : function(checkbox)
19347     {
19348         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19349             this.groups[checkbox.groupId] = {};
19350         }
19351         
19352         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19353             return;
19354         }
19355         
19356         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19357         
19358     },
19359     /**
19360     * fetch a CheckBox Group based on the group ID
19361     * @param {string} the group ID
19362     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19363     */
19364     get: function(groupId) {
19365         if (typeof(this.groups[groupId]) == 'undefined') {
19366             return false;
19367         }
19368         
19369         return this.groups[groupId] ;
19370     }
19371     
19372     
19373 });
19374 /*
19375  * - LGPL
19376  *
19377  * Radio
19378  *
19379  *
19380  * not inline
19381  *<div class="radio">
19382   <label>
19383     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19384     Option one is this and that&mdash;be sure to include why it's great
19385   </label>
19386 </div>
19387  *
19388  *
19389  *inline
19390  *<span>
19391  *<label class="radio-inline">fieldLabel</label>
19392  *<label class="radio-inline">
19393   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19394 </label>
19395 <span>
19396  * 
19397  * 
19398  */
19399
19400 /**
19401  * @class Roo.bootstrap.Radio
19402  * @extends Roo.bootstrap.CheckBox
19403  * Bootstrap Radio class
19404
19405  * @constructor
19406  * Create a new Radio
19407  * @param {Object} config The config object
19408  */
19409
19410 Roo.bootstrap.Radio = function(config){
19411     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19412    
19413 };
19414
19415 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19416     
19417     inputType: 'radio',
19418     inputValue: '',
19419     valueOff: '',
19420     
19421     getAutoCreate : function()
19422     {
19423         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19424         align = align || 'left'; // default...
19425         
19426         
19427         
19428         var id = Roo.id();
19429         
19430         var cfg = {
19431                 tag : this.inline ? 'span' : 'div',
19432                 cls : '',
19433                 cn : []
19434         };
19435         
19436         var inline = this.inline ? ' radio-inline' : '';
19437         
19438         var lbl = {
19439                 tag: 'label' ,
19440                 // does not need for, as we wrap the input with it..
19441                 'for' : id,
19442                 cls : 'control-label box-label' + inline,
19443                 cn : []
19444         };
19445         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19446         
19447         var fieldLabel = {
19448             tag: 'label' ,
19449             //cls : 'control-label' + inline,
19450             html : this.fieldLabel,
19451             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19452         };
19453         
19454  
19455         
19456         
19457         var input =  {
19458             tag: 'input',
19459             id : id,
19460             type : this.inputType,
19461             //value : (!this.checked) ? this.valueOff : this.inputValue,
19462             value : this.inputValue,
19463             cls : 'roo-radio',
19464             placeholder : this.placeholder || '' // ?? needed????
19465             
19466         };
19467         if (this.weight) { // Validity check?
19468             input.cls += " radio-" + this.weight;
19469         }
19470         if (this.disabled) {
19471             input.disabled=true;
19472         }
19473         
19474         if(this.checked){
19475             input.checked = this.checked;
19476         }
19477         
19478         if (this.name) {
19479             input.name = this.name;
19480         }
19481         
19482         if (this.size) {
19483             input.cls += ' input-' + this.size;
19484         }
19485         
19486         //?? can span's inline have a width??
19487         
19488         var settings=this;
19489         ['xs','sm','md','lg'].map(function(size){
19490             if (settings[size]) {
19491                 cfg.cls += ' col-' + size + '-' + settings[size];
19492             }
19493         });
19494         
19495         var inputblock = input;
19496         
19497         if (this.before || this.after) {
19498             
19499             inputblock = {
19500                 cls : 'input-group',
19501                 tag : 'span',
19502                 cn :  [] 
19503             };
19504             if (this.before) {
19505                 inputblock.cn.push({
19506                     tag :'span',
19507                     cls : 'input-group-addon',
19508                     html : this.before
19509                 });
19510             }
19511             inputblock.cn.push(input);
19512             if (this.after) {
19513                 inputblock.cn.push({
19514                     tag :'span',
19515                     cls : 'input-group-addon',
19516                     html : this.after
19517                 });
19518             }
19519             
19520         };
19521         
19522         
19523         if (this.fieldLabel && this.fieldLabel.length) {
19524             cfg.cn.push(fieldLabel);
19525         }
19526        
19527         // normal bootstrap puts the input inside the label.
19528         // however with our styled version - it has to go after the input.
19529        
19530         //lbl.cn.push(inputblock);
19531         
19532         var lblwrap =  {
19533             tag: 'span',
19534             cls: 'radio' + inline,
19535             cn: [
19536                 inputblock,
19537                 lbl
19538             ]
19539         };
19540         
19541         cfg.cn.push( lblwrap);
19542         
19543         if(this.boxLabel){
19544             lbl.cn.push({
19545                 tag: 'span',
19546                 html: this.boxLabel
19547             })
19548         }
19549          
19550         
19551         return cfg;
19552         
19553     },
19554     
19555     initEvents : function()
19556     {
19557 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19558         
19559         this.inputEl().on('click', this.onClick,  this);
19560         if (this.boxLabel) {
19561             //Roo.log('find label');
19562             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19563         }
19564         
19565     },
19566     
19567     inputEl: function ()
19568     {
19569         return this.el.select('input.roo-radio',true).first();
19570     },
19571     onClick : function()
19572     {   
19573         Roo.log("click");
19574         this.setChecked(true);
19575     },
19576     
19577     setChecked : function(state,suppressEvent)
19578     {
19579         if(state){
19580             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19581                 v.dom.checked = false;
19582             });
19583         }
19584         Roo.log(this.inputEl().dom);
19585         this.checked = state;
19586         this.inputEl().dom.checked = state;
19587         
19588         if(suppressEvent !== true){
19589             this.fireEvent('check', this, state);
19590         }
19591         
19592         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19593         
19594     },
19595     
19596     getGroupValue : function()
19597     {
19598         var value = '';
19599         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19600             if(v.dom.checked == true){
19601                 value = v.dom.value;
19602             }
19603         });
19604         
19605         return value;
19606     },
19607     
19608     /**
19609      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19610      * @return {Mixed} value The field value
19611      */
19612     getValue : function(){
19613         return this.getGroupValue();
19614     }
19615     
19616 });
19617
19618  
19619 //<script type="text/javascript">
19620
19621 /*
19622  * Based  Ext JS Library 1.1.1
19623  * Copyright(c) 2006-2007, Ext JS, LLC.
19624  * LGPL
19625  *
19626  */
19627  
19628 /**
19629  * @class Roo.HtmlEditorCore
19630  * @extends Roo.Component
19631  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19632  *
19633  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19634  */
19635
19636 Roo.HtmlEditorCore = function(config){
19637     
19638     
19639     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19640     
19641     
19642     this.addEvents({
19643         /**
19644          * @event initialize
19645          * Fires when the editor is fully initialized (including the iframe)
19646          * @param {Roo.HtmlEditorCore} this
19647          */
19648         initialize: true,
19649         /**
19650          * @event activate
19651          * Fires when the editor is first receives the focus. Any insertion must wait
19652          * until after this event.
19653          * @param {Roo.HtmlEditorCore} this
19654          */
19655         activate: true,
19656          /**
19657          * @event beforesync
19658          * Fires before the textarea is updated with content from the editor iframe. Return false
19659          * to cancel the sync.
19660          * @param {Roo.HtmlEditorCore} this
19661          * @param {String} html
19662          */
19663         beforesync: true,
19664          /**
19665          * @event beforepush
19666          * Fires before the iframe editor is updated with content from the textarea. Return false
19667          * to cancel the push.
19668          * @param {Roo.HtmlEditorCore} this
19669          * @param {String} html
19670          */
19671         beforepush: true,
19672          /**
19673          * @event sync
19674          * Fires when the textarea is updated with content from the editor iframe.
19675          * @param {Roo.HtmlEditorCore} this
19676          * @param {String} html
19677          */
19678         sync: true,
19679          /**
19680          * @event push
19681          * Fires when the iframe editor is updated with content from the textarea.
19682          * @param {Roo.HtmlEditorCore} this
19683          * @param {String} html
19684          */
19685         push: true,
19686         
19687         /**
19688          * @event editorevent
19689          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19690          * @param {Roo.HtmlEditorCore} this
19691          */
19692         editorevent: true
19693         
19694     });
19695     
19696     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19697     
19698     // defaults : white / black...
19699     this.applyBlacklists();
19700     
19701     
19702     
19703 };
19704
19705
19706 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19707
19708
19709      /**
19710      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19711      */
19712     
19713     owner : false,
19714     
19715      /**
19716      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19717      *                        Roo.resizable.
19718      */
19719     resizable : false,
19720      /**
19721      * @cfg {Number} height (in pixels)
19722      */   
19723     height: 300,
19724    /**
19725      * @cfg {Number} width (in pixels)
19726      */   
19727     width: 500,
19728     
19729     /**
19730      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19731      * 
19732      */
19733     stylesheets: false,
19734     
19735     // id of frame..
19736     frameId: false,
19737     
19738     // private properties
19739     validationEvent : false,
19740     deferHeight: true,
19741     initialized : false,
19742     activated : false,
19743     sourceEditMode : false,
19744     onFocus : Roo.emptyFn,
19745     iframePad:3,
19746     hideMode:'offsets',
19747     
19748     clearUp: true,
19749     
19750     // blacklist + whitelisted elements..
19751     black: false,
19752     white: false,
19753      
19754     
19755
19756     /**
19757      * Protected method that will not generally be called directly. It
19758      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19759      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19760      */
19761     getDocMarkup : function(){
19762         // body styles..
19763         var st = '';
19764         
19765         // inherit styels from page...?? 
19766         if (this.stylesheets === false) {
19767             
19768             Roo.get(document.head).select('style').each(function(node) {
19769                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19770             });
19771             
19772             Roo.get(document.head).select('link').each(function(node) { 
19773                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19774             });
19775             
19776         } else if (!this.stylesheets.length) {
19777                 // simple..
19778                 st = '<style type="text/css">' +
19779                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19780                    '</style>';
19781         } else { 
19782             
19783         }
19784         
19785         st +=  '<style type="text/css">' +
19786             'IMG { cursor: pointer } ' +
19787         '</style>';
19788
19789         
19790         return '<html><head>' + st  +
19791             //<style type="text/css">' +
19792             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19793             //'</style>' +
19794             ' </head><body class="roo-htmleditor-body"></body></html>';
19795     },
19796
19797     // private
19798     onRender : function(ct, position)
19799     {
19800         var _t = this;
19801         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19802         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19803         
19804         
19805         this.el.dom.style.border = '0 none';
19806         this.el.dom.setAttribute('tabIndex', -1);
19807         this.el.addClass('x-hidden hide');
19808         
19809         
19810         
19811         if(Roo.isIE){ // fix IE 1px bogus margin
19812             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19813         }
19814        
19815         
19816         this.frameId = Roo.id();
19817         
19818          
19819         
19820         var iframe = this.owner.wrap.createChild({
19821             tag: 'iframe',
19822             cls: 'form-control', // bootstrap..
19823             id: this.frameId,
19824             name: this.frameId,
19825             frameBorder : 'no',
19826             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19827         }, this.el
19828         );
19829         
19830         
19831         this.iframe = iframe.dom;
19832
19833          this.assignDocWin();
19834         
19835         this.doc.designMode = 'on';
19836        
19837         this.doc.open();
19838         this.doc.write(this.getDocMarkup());
19839         this.doc.close();
19840
19841         
19842         var task = { // must defer to wait for browser to be ready
19843             run : function(){
19844                 //console.log("run task?" + this.doc.readyState);
19845                 this.assignDocWin();
19846                 if(this.doc.body || this.doc.readyState == 'complete'){
19847                     try {
19848                         this.doc.designMode="on";
19849                     } catch (e) {
19850                         return;
19851                     }
19852                     Roo.TaskMgr.stop(task);
19853                     this.initEditor.defer(10, this);
19854                 }
19855             },
19856             interval : 10,
19857             duration: 10000,
19858             scope: this
19859         };
19860         Roo.TaskMgr.start(task);
19861
19862     },
19863
19864     // private
19865     onResize : function(w, h)
19866     {
19867          Roo.log('resize: ' +w + ',' + h );
19868         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19869         if(!this.iframe){
19870             return;
19871         }
19872         if(typeof w == 'number'){
19873             
19874             this.iframe.style.width = w + 'px';
19875         }
19876         if(typeof h == 'number'){
19877             
19878             this.iframe.style.height = h + 'px';
19879             if(this.doc){
19880                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19881             }
19882         }
19883         
19884     },
19885
19886     /**
19887      * Toggles the editor between standard and source edit mode.
19888      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19889      */
19890     toggleSourceEdit : function(sourceEditMode){
19891         
19892         this.sourceEditMode = sourceEditMode === true;
19893         
19894         if(this.sourceEditMode){
19895  
19896             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19897             
19898         }else{
19899             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19900             //this.iframe.className = '';
19901             this.deferFocus();
19902         }
19903         //this.setSize(this.owner.wrap.getSize());
19904         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19905     },
19906
19907     
19908   
19909
19910     /**
19911      * Protected method that will not generally be called directly. If you need/want
19912      * custom HTML cleanup, this is the method you should override.
19913      * @param {String} html The HTML to be cleaned
19914      * return {String} The cleaned HTML
19915      */
19916     cleanHtml : function(html){
19917         html = String(html);
19918         if(html.length > 5){
19919             if(Roo.isSafari){ // strip safari nonsense
19920                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19921             }
19922         }
19923         if(html == '&nbsp;'){
19924             html = '';
19925         }
19926         return html;
19927     },
19928
19929     /**
19930      * HTML Editor -> Textarea
19931      * Protected method that will not generally be called directly. Syncs the contents
19932      * of the editor iframe with the textarea.
19933      */
19934     syncValue : function(){
19935         if(this.initialized){
19936             var bd = (this.doc.body || this.doc.documentElement);
19937             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19938             var html = bd.innerHTML;
19939             if(Roo.isSafari){
19940                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19941                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19942                 if(m && m[1]){
19943                     html = '<div style="'+m[0]+'">' + html + '</div>';
19944                 }
19945             }
19946             html = this.cleanHtml(html);
19947             // fix up the special chars.. normaly like back quotes in word...
19948             // however we do not want to do this with chinese..
19949             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19950                 var cc = b.charCodeAt();
19951                 if (
19952                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19953                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19954                     (cc >= 0xf900 && cc < 0xfb00 )
19955                 ) {
19956                         return b;
19957                 }
19958                 return "&#"+cc+";" 
19959             });
19960             if(this.owner.fireEvent('beforesync', this, html) !== false){
19961                 this.el.dom.value = html;
19962                 this.owner.fireEvent('sync', this, html);
19963             }
19964         }
19965     },
19966
19967     /**
19968      * Protected method that will not generally be called directly. Pushes the value of the textarea
19969      * into the iframe editor.
19970      */
19971     pushValue : function(){
19972         if(this.initialized){
19973             var v = this.el.dom.value.trim();
19974             
19975 //            if(v.length < 1){
19976 //                v = '&#160;';
19977 //            }
19978             
19979             if(this.owner.fireEvent('beforepush', this, v) !== false){
19980                 var d = (this.doc.body || this.doc.documentElement);
19981                 d.innerHTML = v;
19982                 this.cleanUpPaste();
19983                 this.el.dom.value = d.innerHTML;
19984                 this.owner.fireEvent('push', this, v);
19985             }
19986         }
19987     },
19988
19989     // private
19990     deferFocus : function(){
19991         this.focus.defer(10, this);
19992     },
19993
19994     // doc'ed in Field
19995     focus : function(){
19996         if(this.win && !this.sourceEditMode){
19997             this.win.focus();
19998         }else{
19999             this.el.focus();
20000         }
20001     },
20002     
20003     assignDocWin: function()
20004     {
20005         var iframe = this.iframe;
20006         
20007          if(Roo.isIE){
20008             this.doc = iframe.contentWindow.document;
20009             this.win = iframe.contentWindow;
20010         } else {
20011 //            if (!Roo.get(this.frameId)) {
20012 //                return;
20013 //            }
20014 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20015 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20016             
20017             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20018                 return;
20019             }
20020             
20021             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20022             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20023         }
20024     },
20025     
20026     // private
20027     initEditor : function(){
20028         //console.log("INIT EDITOR");
20029         this.assignDocWin();
20030         
20031         
20032         
20033         this.doc.designMode="on";
20034         this.doc.open();
20035         this.doc.write(this.getDocMarkup());
20036         this.doc.close();
20037         
20038         var dbody = (this.doc.body || this.doc.documentElement);
20039         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20040         // this copies styles from the containing element into thsi one..
20041         // not sure why we need all of this..
20042         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20043         
20044         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20045         //ss['background-attachment'] = 'fixed'; // w3c
20046         dbody.bgProperties = 'fixed'; // ie
20047         //Roo.DomHelper.applyStyles(dbody, ss);
20048         Roo.EventManager.on(this.doc, {
20049             //'mousedown': this.onEditorEvent,
20050             'mouseup': this.onEditorEvent,
20051             'dblclick': this.onEditorEvent,
20052             'click': this.onEditorEvent,
20053             'keyup': this.onEditorEvent,
20054             buffer:100,
20055             scope: this
20056         });
20057         if(Roo.isGecko){
20058             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20059         }
20060         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20061             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20062         }
20063         this.initialized = true;
20064
20065         this.owner.fireEvent('initialize', this);
20066         this.pushValue();
20067     },
20068
20069     // private
20070     onDestroy : function(){
20071         
20072         
20073         
20074         if(this.rendered){
20075             
20076             //for (var i =0; i < this.toolbars.length;i++) {
20077             //    // fixme - ask toolbars for heights?
20078             //    this.toolbars[i].onDestroy();
20079            // }
20080             
20081             //this.wrap.dom.innerHTML = '';
20082             //this.wrap.remove();
20083         }
20084     },
20085
20086     // private
20087     onFirstFocus : function(){
20088         
20089         this.assignDocWin();
20090         
20091         
20092         this.activated = true;
20093          
20094     
20095         if(Roo.isGecko){ // prevent silly gecko errors
20096             this.win.focus();
20097             var s = this.win.getSelection();
20098             if(!s.focusNode || s.focusNode.nodeType != 3){
20099                 var r = s.getRangeAt(0);
20100                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20101                 r.collapse(true);
20102                 this.deferFocus();
20103             }
20104             try{
20105                 this.execCmd('useCSS', true);
20106                 this.execCmd('styleWithCSS', false);
20107             }catch(e){}
20108         }
20109         this.owner.fireEvent('activate', this);
20110     },
20111
20112     // private
20113     adjustFont: function(btn){
20114         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20115         //if(Roo.isSafari){ // safari
20116         //    adjust *= 2;
20117        // }
20118         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20119         if(Roo.isSafari){ // safari
20120             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20121             v =  (v < 10) ? 10 : v;
20122             v =  (v > 48) ? 48 : v;
20123             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20124             
20125         }
20126         
20127         
20128         v = Math.max(1, v+adjust);
20129         
20130         this.execCmd('FontSize', v  );
20131     },
20132
20133     onEditorEvent : function(e)
20134     {
20135         this.owner.fireEvent('editorevent', this, e);
20136       //  this.updateToolbar();
20137         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20138     },
20139
20140     insertTag : function(tg)
20141     {
20142         // could be a bit smarter... -> wrap the current selected tRoo..
20143         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20144             
20145             range = this.createRange(this.getSelection());
20146             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20147             wrappingNode.appendChild(range.extractContents());
20148             range.insertNode(wrappingNode);
20149
20150             return;
20151             
20152             
20153             
20154         }
20155         this.execCmd("formatblock",   tg);
20156         
20157     },
20158     
20159     insertText : function(txt)
20160     {
20161         
20162         
20163         var range = this.createRange();
20164         range.deleteContents();
20165                //alert(Sender.getAttribute('label'));
20166                
20167         range.insertNode(this.doc.createTextNode(txt));
20168     } ,
20169     
20170      
20171
20172     /**
20173      * Executes a Midas editor command on the editor document and performs necessary focus and
20174      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20175      * @param {String} cmd The Midas command
20176      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20177      */
20178     relayCmd : function(cmd, value){
20179         this.win.focus();
20180         this.execCmd(cmd, value);
20181         this.owner.fireEvent('editorevent', this);
20182         //this.updateToolbar();
20183         this.owner.deferFocus();
20184     },
20185
20186     /**
20187      * Executes a Midas editor command directly on the editor document.
20188      * For visual commands, you should use {@link #relayCmd} instead.
20189      * <b>This should only be called after the editor is initialized.</b>
20190      * @param {String} cmd The Midas command
20191      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20192      */
20193     execCmd : function(cmd, value){
20194         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20195         this.syncValue();
20196     },
20197  
20198  
20199    
20200     /**
20201      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20202      * to insert tRoo.
20203      * @param {String} text | dom node.. 
20204      */
20205     insertAtCursor : function(text)
20206     {
20207         
20208         
20209         
20210         if(!this.activated){
20211             return;
20212         }
20213         /*
20214         if(Roo.isIE){
20215             this.win.focus();
20216             var r = this.doc.selection.createRange();
20217             if(r){
20218                 r.collapse(true);
20219                 r.pasteHTML(text);
20220                 this.syncValue();
20221                 this.deferFocus();
20222             
20223             }
20224             return;
20225         }
20226         */
20227         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20228             this.win.focus();
20229             
20230             
20231             // from jquery ui (MIT licenced)
20232             var range, node;
20233             var win = this.win;
20234             
20235             if (win.getSelection && win.getSelection().getRangeAt) {
20236                 range = win.getSelection().getRangeAt(0);
20237                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20238                 range.insertNode(node);
20239             } else if (win.document.selection && win.document.selection.createRange) {
20240                 // no firefox support
20241                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20242                 win.document.selection.createRange().pasteHTML(txt);
20243             } else {
20244                 // no firefox support
20245                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20246                 this.execCmd('InsertHTML', txt);
20247             } 
20248             
20249             this.syncValue();
20250             
20251             this.deferFocus();
20252         }
20253     },
20254  // private
20255     mozKeyPress : function(e){
20256         if(e.ctrlKey){
20257             var c = e.getCharCode(), cmd;
20258           
20259             if(c > 0){
20260                 c = String.fromCharCode(c).toLowerCase();
20261                 switch(c){
20262                     case 'b':
20263                         cmd = 'bold';
20264                         break;
20265                     case 'i':
20266                         cmd = 'italic';
20267                         break;
20268                     
20269                     case 'u':
20270                         cmd = 'underline';
20271                         break;
20272                     
20273                     case 'v':
20274                         this.cleanUpPaste.defer(100, this);
20275                         return;
20276                         
20277                 }
20278                 if(cmd){
20279                     this.win.focus();
20280                     this.execCmd(cmd);
20281                     this.deferFocus();
20282                     e.preventDefault();
20283                 }
20284                 
20285             }
20286         }
20287     },
20288
20289     // private
20290     fixKeys : function(){ // load time branching for fastest keydown performance
20291         if(Roo.isIE){
20292             return function(e){
20293                 var k = e.getKey(), r;
20294                 if(k == e.TAB){
20295                     e.stopEvent();
20296                     r = this.doc.selection.createRange();
20297                     if(r){
20298                         r.collapse(true);
20299                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20300                         this.deferFocus();
20301                     }
20302                     return;
20303                 }
20304                 
20305                 if(k == e.ENTER){
20306                     r = this.doc.selection.createRange();
20307                     if(r){
20308                         var target = r.parentElement();
20309                         if(!target || target.tagName.toLowerCase() != 'li'){
20310                             e.stopEvent();
20311                             r.pasteHTML('<br />');
20312                             r.collapse(false);
20313                             r.select();
20314                         }
20315                     }
20316                 }
20317                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20318                     this.cleanUpPaste.defer(100, this);
20319                     return;
20320                 }
20321                 
20322                 
20323             };
20324         }else if(Roo.isOpera){
20325             return function(e){
20326                 var k = e.getKey();
20327                 if(k == e.TAB){
20328                     e.stopEvent();
20329                     this.win.focus();
20330                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20331                     this.deferFocus();
20332                 }
20333                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20334                     this.cleanUpPaste.defer(100, this);
20335                     return;
20336                 }
20337                 
20338             };
20339         }else if(Roo.isSafari){
20340             return function(e){
20341                 var k = e.getKey();
20342                 
20343                 if(k == e.TAB){
20344                     e.stopEvent();
20345                     this.execCmd('InsertText','\t');
20346                     this.deferFocus();
20347                     return;
20348                 }
20349                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20350                     this.cleanUpPaste.defer(100, this);
20351                     return;
20352                 }
20353                 
20354              };
20355         }
20356     }(),
20357     
20358     getAllAncestors: function()
20359     {
20360         var p = this.getSelectedNode();
20361         var a = [];
20362         if (!p) {
20363             a.push(p); // push blank onto stack..
20364             p = this.getParentElement();
20365         }
20366         
20367         
20368         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20369             a.push(p);
20370             p = p.parentNode;
20371         }
20372         a.push(this.doc.body);
20373         return a;
20374     },
20375     lastSel : false,
20376     lastSelNode : false,
20377     
20378     
20379     getSelection : function() 
20380     {
20381         this.assignDocWin();
20382         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20383     },
20384     
20385     getSelectedNode: function() 
20386     {
20387         // this may only work on Gecko!!!
20388         
20389         // should we cache this!!!!
20390         
20391         
20392         
20393          
20394         var range = this.createRange(this.getSelection()).cloneRange();
20395         
20396         if (Roo.isIE) {
20397             var parent = range.parentElement();
20398             while (true) {
20399                 var testRange = range.duplicate();
20400                 testRange.moveToElementText(parent);
20401                 if (testRange.inRange(range)) {
20402                     break;
20403                 }
20404                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20405                     break;
20406                 }
20407                 parent = parent.parentElement;
20408             }
20409             return parent;
20410         }
20411         
20412         // is ancestor a text element.
20413         var ac =  range.commonAncestorContainer;
20414         if (ac.nodeType == 3) {
20415             ac = ac.parentNode;
20416         }
20417         
20418         var ar = ac.childNodes;
20419          
20420         var nodes = [];
20421         var other_nodes = [];
20422         var has_other_nodes = false;
20423         for (var i=0;i<ar.length;i++) {
20424             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20425                 continue;
20426             }
20427             // fullly contained node.
20428             
20429             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20430                 nodes.push(ar[i]);
20431                 continue;
20432             }
20433             
20434             // probably selected..
20435             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20436                 other_nodes.push(ar[i]);
20437                 continue;
20438             }
20439             // outer..
20440             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20441                 continue;
20442             }
20443             
20444             
20445             has_other_nodes = true;
20446         }
20447         if (!nodes.length && other_nodes.length) {
20448             nodes= other_nodes;
20449         }
20450         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20451             return false;
20452         }
20453         
20454         return nodes[0];
20455     },
20456     createRange: function(sel)
20457     {
20458         // this has strange effects when using with 
20459         // top toolbar - not sure if it's a great idea.
20460         //this.editor.contentWindow.focus();
20461         if (typeof sel != "undefined") {
20462             try {
20463                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20464             } catch(e) {
20465                 return this.doc.createRange();
20466             }
20467         } else {
20468             return this.doc.createRange();
20469         }
20470     },
20471     getParentElement: function()
20472     {
20473         
20474         this.assignDocWin();
20475         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20476         
20477         var range = this.createRange(sel);
20478          
20479         try {
20480             var p = range.commonAncestorContainer;
20481             while (p.nodeType == 3) { // text node
20482                 p = p.parentNode;
20483             }
20484             return p;
20485         } catch (e) {
20486             return null;
20487         }
20488     
20489     },
20490     /***
20491      *
20492      * Range intersection.. the hard stuff...
20493      *  '-1' = before
20494      *  '0' = hits..
20495      *  '1' = after.
20496      *         [ -- selected range --- ]
20497      *   [fail]                        [fail]
20498      *
20499      *    basically..
20500      *      if end is before start or  hits it. fail.
20501      *      if start is after end or hits it fail.
20502      *
20503      *   if either hits (but other is outside. - then it's not 
20504      *   
20505      *    
20506      **/
20507     
20508     
20509     // @see http://www.thismuchiknow.co.uk/?p=64.
20510     rangeIntersectsNode : function(range, node)
20511     {
20512         var nodeRange = node.ownerDocument.createRange();
20513         try {
20514             nodeRange.selectNode(node);
20515         } catch (e) {
20516             nodeRange.selectNodeContents(node);
20517         }
20518     
20519         var rangeStartRange = range.cloneRange();
20520         rangeStartRange.collapse(true);
20521     
20522         var rangeEndRange = range.cloneRange();
20523         rangeEndRange.collapse(false);
20524     
20525         var nodeStartRange = nodeRange.cloneRange();
20526         nodeStartRange.collapse(true);
20527     
20528         var nodeEndRange = nodeRange.cloneRange();
20529         nodeEndRange.collapse(false);
20530     
20531         return rangeStartRange.compareBoundaryPoints(
20532                  Range.START_TO_START, nodeEndRange) == -1 &&
20533                rangeEndRange.compareBoundaryPoints(
20534                  Range.START_TO_START, nodeStartRange) == 1;
20535         
20536          
20537     },
20538     rangeCompareNode : function(range, node)
20539     {
20540         var nodeRange = node.ownerDocument.createRange();
20541         try {
20542             nodeRange.selectNode(node);
20543         } catch (e) {
20544             nodeRange.selectNodeContents(node);
20545         }
20546         
20547         
20548         range.collapse(true);
20549     
20550         nodeRange.collapse(true);
20551      
20552         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20553         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20554          
20555         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20556         
20557         var nodeIsBefore   =  ss == 1;
20558         var nodeIsAfter    = ee == -1;
20559         
20560         if (nodeIsBefore && nodeIsAfter) {
20561             return 0; // outer
20562         }
20563         if (!nodeIsBefore && nodeIsAfter) {
20564             return 1; //right trailed.
20565         }
20566         
20567         if (nodeIsBefore && !nodeIsAfter) {
20568             return 2;  // left trailed.
20569         }
20570         // fully contined.
20571         return 3;
20572     },
20573
20574     // private? - in a new class?
20575     cleanUpPaste :  function()
20576     {
20577         // cleans up the whole document..
20578         Roo.log('cleanuppaste');
20579         
20580         this.cleanUpChildren(this.doc.body);
20581         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20582         if (clean != this.doc.body.innerHTML) {
20583             this.doc.body.innerHTML = clean;
20584         }
20585         
20586     },
20587     
20588     cleanWordChars : function(input) {// change the chars to hex code
20589         var he = Roo.HtmlEditorCore;
20590         
20591         var output = input;
20592         Roo.each(he.swapCodes, function(sw) { 
20593             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20594             
20595             output = output.replace(swapper, sw[1]);
20596         });
20597         
20598         return output;
20599     },
20600     
20601     
20602     cleanUpChildren : function (n)
20603     {
20604         if (!n.childNodes.length) {
20605             return;
20606         }
20607         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20608            this.cleanUpChild(n.childNodes[i]);
20609         }
20610     },
20611     
20612     
20613         
20614     
20615     cleanUpChild : function (node)
20616     {
20617         var ed = this;
20618         //console.log(node);
20619         if (node.nodeName == "#text") {
20620             // clean up silly Windows -- stuff?
20621             return; 
20622         }
20623         if (node.nodeName == "#comment") {
20624             node.parentNode.removeChild(node);
20625             // clean up silly Windows -- stuff?
20626             return; 
20627         }
20628         var lcname = node.tagName.toLowerCase();
20629         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20630         // whitelist of tags..
20631         
20632         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20633             // remove node.
20634             node.parentNode.removeChild(node);
20635             return;
20636             
20637         }
20638         
20639         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20640         
20641         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20642         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20643         
20644         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20645         //    remove_keep_children = true;
20646         //}
20647         
20648         if (remove_keep_children) {
20649             this.cleanUpChildren(node);
20650             // inserts everything just before this node...
20651             while (node.childNodes.length) {
20652                 var cn = node.childNodes[0];
20653                 node.removeChild(cn);
20654                 node.parentNode.insertBefore(cn, node);
20655             }
20656             node.parentNode.removeChild(node);
20657             return;
20658         }
20659         
20660         if (!node.attributes || !node.attributes.length) {
20661             this.cleanUpChildren(node);
20662             return;
20663         }
20664         
20665         function cleanAttr(n,v)
20666         {
20667             
20668             if (v.match(/^\./) || v.match(/^\//)) {
20669                 return;
20670             }
20671             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20672                 return;
20673             }
20674             if (v.match(/^#/)) {
20675                 return;
20676             }
20677 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20678             node.removeAttribute(n);
20679             
20680         }
20681         
20682         var cwhite = this.cwhite;
20683         var cblack = this.cblack;
20684             
20685         function cleanStyle(n,v)
20686         {
20687             if (v.match(/expression/)) { //XSS?? should we even bother..
20688                 node.removeAttribute(n);
20689                 return;
20690             }
20691             
20692             var parts = v.split(/;/);
20693             var clean = [];
20694             
20695             Roo.each(parts, function(p) {
20696                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20697                 if (!p.length) {
20698                     return true;
20699                 }
20700                 var l = p.split(':').shift().replace(/\s+/g,'');
20701                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20702                 
20703                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20704 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20705                     //node.removeAttribute(n);
20706                     return true;
20707                 }
20708                 //Roo.log()
20709                 // only allow 'c whitelisted system attributes'
20710                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20711 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20712                     //node.removeAttribute(n);
20713                     return true;
20714                 }
20715                 
20716                 
20717                  
20718                 
20719                 clean.push(p);
20720                 return true;
20721             });
20722             if (clean.length) { 
20723                 node.setAttribute(n, clean.join(';'));
20724             } else {
20725                 node.removeAttribute(n);
20726             }
20727             
20728         }
20729         
20730         
20731         for (var i = node.attributes.length-1; i > -1 ; i--) {
20732             var a = node.attributes[i];
20733             //console.log(a);
20734             
20735             if (a.name.toLowerCase().substr(0,2)=='on')  {
20736                 node.removeAttribute(a.name);
20737                 continue;
20738             }
20739             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20740                 node.removeAttribute(a.name);
20741                 continue;
20742             }
20743             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20744                 cleanAttr(a.name,a.value); // fixme..
20745                 continue;
20746             }
20747             if (a.name == 'style') {
20748                 cleanStyle(a.name,a.value);
20749                 continue;
20750             }
20751             /// clean up MS crap..
20752             // tecnically this should be a list of valid class'es..
20753             
20754             
20755             if (a.name == 'class') {
20756                 if (a.value.match(/^Mso/)) {
20757                     node.className = '';
20758                 }
20759                 
20760                 if (a.value.match(/body/)) {
20761                     node.className = '';
20762                 }
20763                 continue;
20764             }
20765             
20766             // style cleanup!?
20767             // class cleanup?
20768             
20769         }
20770         
20771         
20772         this.cleanUpChildren(node);
20773         
20774         
20775     },
20776     
20777     /**
20778      * Clean up MS wordisms...
20779      */
20780     cleanWord : function(node)
20781     {
20782         
20783         
20784         if (!node) {
20785             this.cleanWord(this.doc.body);
20786             return;
20787         }
20788         if (node.nodeName == "#text") {
20789             // clean up silly Windows -- stuff?
20790             return; 
20791         }
20792         if (node.nodeName == "#comment") {
20793             node.parentNode.removeChild(node);
20794             // clean up silly Windows -- stuff?
20795             return; 
20796         }
20797         
20798         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20799             node.parentNode.removeChild(node);
20800             return;
20801         }
20802         
20803         // remove - but keep children..
20804         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20805             while (node.childNodes.length) {
20806                 var cn = node.childNodes[0];
20807                 node.removeChild(cn);
20808                 node.parentNode.insertBefore(cn, node);
20809             }
20810             node.parentNode.removeChild(node);
20811             this.iterateChildren(node, this.cleanWord);
20812             return;
20813         }
20814         // clean styles
20815         if (node.className.length) {
20816             
20817             var cn = node.className.split(/\W+/);
20818             var cna = [];
20819             Roo.each(cn, function(cls) {
20820                 if (cls.match(/Mso[a-zA-Z]+/)) {
20821                     return;
20822                 }
20823                 cna.push(cls);
20824             });
20825             node.className = cna.length ? cna.join(' ') : '';
20826             if (!cna.length) {
20827                 node.removeAttribute("class");
20828             }
20829         }
20830         
20831         if (node.hasAttribute("lang")) {
20832             node.removeAttribute("lang");
20833         }
20834         
20835         if (node.hasAttribute("style")) {
20836             
20837             var styles = node.getAttribute("style").split(";");
20838             var nstyle = [];
20839             Roo.each(styles, function(s) {
20840                 if (!s.match(/:/)) {
20841                     return;
20842                 }
20843                 var kv = s.split(":");
20844                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20845                     return;
20846                 }
20847                 // what ever is left... we allow.
20848                 nstyle.push(s);
20849             });
20850             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20851             if (!nstyle.length) {
20852                 node.removeAttribute('style');
20853             }
20854         }
20855         this.iterateChildren(node, this.cleanWord);
20856         
20857         
20858         
20859     },
20860     /**
20861      * iterateChildren of a Node, calling fn each time, using this as the scole..
20862      * @param {DomNode} node node to iterate children of.
20863      * @param {Function} fn method of this class to call on each item.
20864      */
20865     iterateChildren : function(node, fn)
20866     {
20867         if (!node.childNodes.length) {
20868                 return;
20869         }
20870         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20871            fn.call(this, node.childNodes[i])
20872         }
20873     },
20874     
20875     
20876     /**
20877      * cleanTableWidths.
20878      *
20879      * Quite often pasting from word etc.. results in tables with column and widths.
20880      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20881      *
20882      */
20883     cleanTableWidths : function(node)
20884     {
20885          
20886          
20887         if (!node) {
20888             this.cleanTableWidths(this.doc.body);
20889             return;
20890         }
20891         
20892         // ignore list...
20893         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20894             return; 
20895         }
20896         Roo.log(node.tagName);
20897         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20898             this.iterateChildren(node, this.cleanTableWidths);
20899             return;
20900         }
20901         if (node.hasAttribute('width')) {
20902             node.removeAttribute('width');
20903         }
20904         
20905          
20906         if (node.hasAttribute("style")) {
20907             // pretty basic...
20908             
20909             var styles = node.getAttribute("style").split(";");
20910             var nstyle = [];
20911             Roo.each(styles, function(s) {
20912                 if (!s.match(/:/)) {
20913                     return;
20914                 }
20915                 var kv = s.split(":");
20916                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20917                     return;
20918                 }
20919                 // what ever is left... we allow.
20920                 nstyle.push(s);
20921             });
20922             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20923             if (!nstyle.length) {
20924                 node.removeAttribute('style');
20925             }
20926         }
20927         
20928         this.iterateChildren(node, this.cleanTableWidths);
20929         
20930         
20931     },
20932     
20933     
20934     
20935     
20936     domToHTML : function(currentElement, depth, nopadtext) {
20937         
20938         depth = depth || 0;
20939         nopadtext = nopadtext || false;
20940     
20941         if (!currentElement) {
20942             return this.domToHTML(this.doc.body);
20943         }
20944         
20945         //Roo.log(currentElement);
20946         var j;
20947         var allText = false;
20948         var nodeName = currentElement.nodeName;
20949         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20950         
20951         if  (nodeName == '#text') {
20952             
20953             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20954         }
20955         
20956         
20957         var ret = '';
20958         if (nodeName != 'BODY') {
20959              
20960             var i = 0;
20961             // Prints the node tagName, such as <A>, <IMG>, etc
20962             if (tagName) {
20963                 var attr = [];
20964                 for(i = 0; i < currentElement.attributes.length;i++) {
20965                     // quoting?
20966                     var aname = currentElement.attributes.item(i).name;
20967                     if (!currentElement.attributes.item(i).value.length) {
20968                         continue;
20969                     }
20970                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20971                 }
20972                 
20973                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20974             } 
20975             else {
20976                 
20977                 // eack
20978             }
20979         } else {
20980             tagName = false;
20981         }
20982         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20983             return ret;
20984         }
20985         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20986             nopadtext = true;
20987         }
20988         
20989         
20990         // Traverse the tree
20991         i = 0;
20992         var currentElementChild = currentElement.childNodes.item(i);
20993         var allText = true;
20994         var innerHTML  = '';
20995         lastnode = '';
20996         while (currentElementChild) {
20997             // Formatting code (indent the tree so it looks nice on the screen)
20998             var nopad = nopadtext;
20999             if (lastnode == 'SPAN') {
21000                 nopad  = true;
21001             }
21002             // text
21003             if  (currentElementChild.nodeName == '#text') {
21004                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21005                 toadd = nopadtext ? toadd : toadd.trim();
21006                 if (!nopad && toadd.length > 80) {
21007                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21008                 }
21009                 innerHTML  += toadd;
21010                 
21011                 i++;
21012                 currentElementChild = currentElement.childNodes.item(i);
21013                 lastNode = '';
21014                 continue;
21015             }
21016             allText = false;
21017             
21018             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21019                 
21020             // Recursively traverse the tree structure of the child node
21021             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21022             lastnode = currentElementChild.nodeName;
21023             i++;
21024             currentElementChild=currentElement.childNodes.item(i);
21025         }
21026         
21027         ret += innerHTML;
21028         
21029         if (!allText) {
21030                 // The remaining code is mostly for formatting the tree
21031             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21032         }
21033         
21034         
21035         if (tagName) {
21036             ret+= "</"+tagName+">";
21037         }
21038         return ret;
21039         
21040     },
21041         
21042     applyBlacklists : function()
21043     {
21044         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21045         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21046         
21047         this.white = [];
21048         this.black = [];
21049         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21050             if (b.indexOf(tag) > -1) {
21051                 return;
21052             }
21053             this.white.push(tag);
21054             
21055         }, this);
21056         
21057         Roo.each(w, function(tag) {
21058             if (b.indexOf(tag) > -1) {
21059                 return;
21060             }
21061             if (this.white.indexOf(tag) > -1) {
21062                 return;
21063             }
21064             this.white.push(tag);
21065             
21066         }, this);
21067         
21068         
21069         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21070             if (w.indexOf(tag) > -1) {
21071                 return;
21072             }
21073             this.black.push(tag);
21074             
21075         }, this);
21076         
21077         Roo.each(b, function(tag) {
21078             if (w.indexOf(tag) > -1) {
21079                 return;
21080             }
21081             if (this.black.indexOf(tag) > -1) {
21082                 return;
21083             }
21084             this.black.push(tag);
21085             
21086         }, this);
21087         
21088         
21089         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21090         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21091         
21092         this.cwhite = [];
21093         this.cblack = [];
21094         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21095             if (b.indexOf(tag) > -1) {
21096                 return;
21097             }
21098             this.cwhite.push(tag);
21099             
21100         }, this);
21101         
21102         Roo.each(w, function(tag) {
21103             if (b.indexOf(tag) > -1) {
21104                 return;
21105             }
21106             if (this.cwhite.indexOf(tag) > -1) {
21107                 return;
21108             }
21109             this.cwhite.push(tag);
21110             
21111         }, this);
21112         
21113         
21114         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21115             if (w.indexOf(tag) > -1) {
21116                 return;
21117             }
21118             this.cblack.push(tag);
21119             
21120         }, this);
21121         
21122         Roo.each(b, function(tag) {
21123             if (w.indexOf(tag) > -1) {
21124                 return;
21125             }
21126             if (this.cblack.indexOf(tag) > -1) {
21127                 return;
21128             }
21129             this.cblack.push(tag);
21130             
21131         }, this);
21132     },
21133     
21134     setStylesheets : function(stylesheets)
21135     {
21136         if(typeof(stylesheets) == 'string'){
21137             Roo.get(this.iframe.contentDocument.head).createChild({
21138                 tag : 'link',
21139                 rel : 'stylesheet',
21140                 type : 'text/css',
21141                 href : stylesheets
21142             });
21143             
21144             return;
21145         }
21146         var _this = this;
21147      
21148         Roo.each(stylesheets, function(s) {
21149             if(!s.length){
21150                 return;
21151             }
21152             
21153             Roo.get(_this.iframe.contentDocument.head).createChild({
21154                 tag : 'link',
21155                 rel : 'stylesheet',
21156                 type : 'text/css',
21157                 href : s
21158             });
21159         });
21160
21161         
21162     },
21163     
21164     removeStylesheets : function()
21165     {
21166         var _this = this;
21167         
21168         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21169             s.remove();
21170         });
21171     }
21172     
21173     // hide stuff that is not compatible
21174     /**
21175      * @event blur
21176      * @hide
21177      */
21178     /**
21179      * @event change
21180      * @hide
21181      */
21182     /**
21183      * @event focus
21184      * @hide
21185      */
21186     /**
21187      * @event specialkey
21188      * @hide
21189      */
21190     /**
21191      * @cfg {String} fieldClass @hide
21192      */
21193     /**
21194      * @cfg {String} focusClass @hide
21195      */
21196     /**
21197      * @cfg {String} autoCreate @hide
21198      */
21199     /**
21200      * @cfg {String} inputType @hide
21201      */
21202     /**
21203      * @cfg {String} invalidClass @hide
21204      */
21205     /**
21206      * @cfg {String} invalidText @hide
21207      */
21208     /**
21209      * @cfg {String} msgFx @hide
21210      */
21211     /**
21212      * @cfg {String} validateOnBlur @hide
21213      */
21214 });
21215
21216 Roo.HtmlEditorCore.white = [
21217         'area', 'br', 'img', 'input', 'hr', 'wbr',
21218         
21219        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21220        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21221        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21222        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21223        'table',   'ul',         'xmp', 
21224        
21225        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21226       'thead',   'tr', 
21227      
21228       'dir', 'menu', 'ol', 'ul', 'dl',
21229        
21230       'embed',  'object'
21231 ];
21232
21233
21234 Roo.HtmlEditorCore.black = [
21235     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21236         'applet', // 
21237         'base',   'basefont', 'bgsound', 'blink',  'body', 
21238         'frame',  'frameset', 'head',    'html',   'ilayer', 
21239         'iframe', 'layer',  'link',     'meta',    'object',   
21240         'script', 'style' ,'title',  'xml' // clean later..
21241 ];
21242 Roo.HtmlEditorCore.clean = [
21243     'script', 'style', 'title', 'xml'
21244 ];
21245 Roo.HtmlEditorCore.remove = [
21246     'font'
21247 ];
21248 // attributes..
21249
21250 Roo.HtmlEditorCore.ablack = [
21251     'on'
21252 ];
21253     
21254 Roo.HtmlEditorCore.aclean = [ 
21255     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21256 ];
21257
21258 // protocols..
21259 Roo.HtmlEditorCore.pwhite= [
21260         'http',  'https',  'mailto'
21261 ];
21262
21263 // white listed style attributes.
21264 Roo.HtmlEditorCore.cwhite= [
21265       //  'text-align', /// default is to allow most things..
21266       
21267          
21268 //        'font-size'//??
21269 ];
21270
21271 // black listed style attributes.
21272 Roo.HtmlEditorCore.cblack= [
21273       //  'font-size' -- this can be set by the project 
21274 ];
21275
21276
21277 Roo.HtmlEditorCore.swapCodes   =[ 
21278     [    8211, "--" ], 
21279     [    8212, "--" ], 
21280     [    8216,  "'" ],  
21281     [    8217, "'" ],  
21282     [    8220, '"' ],  
21283     [    8221, '"' ],  
21284     [    8226, "*" ],  
21285     [    8230, "..." ]
21286 ]; 
21287
21288     /*
21289  * - LGPL
21290  *
21291  * HtmlEditor
21292  * 
21293  */
21294
21295 /**
21296  * @class Roo.bootstrap.HtmlEditor
21297  * @extends Roo.bootstrap.TextArea
21298  * Bootstrap HtmlEditor class
21299
21300  * @constructor
21301  * Create a new HtmlEditor
21302  * @param {Object} config The config object
21303  */
21304
21305 Roo.bootstrap.HtmlEditor = function(config){
21306     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21307     if (!this.toolbars) {
21308         this.toolbars = [];
21309     }
21310     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21311     this.addEvents({
21312             /**
21313              * @event initialize
21314              * Fires when the editor is fully initialized (including the iframe)
21315              * @param {HtmlEditor} this
21316              */
21317             initialize: true,
21318             /**
21319              * @event activate
21320              * Fires when the editor is first receives the focus. Any insertion must wait
21321              * until after this event.
21322              * @param {HtmlEditor} this
21323              */
21324             activate: true,
21325              /**
21326              * @event beforesync
21327              * Fires before the textarea is updated with content from the editor iframe. Return false
21328              * to cancel the sync.
21329              * @param {HtmlEditor} this
21330              * @param {String} html
21331              */
21332             beforesync: true,
21333              /**
21334              * @event beforepush
21335              * Fires before the iframe editor is updated with content from the textarea. Return false
21336              * to cancel the push.
21337              * @param {HtmlEditor} this
21338              * @param {String} html
21339              */
21340             beforepush: true,
21341              /**
21342              * @event sync
21343              * Fires when the textarea is updated with content from the editor iframe.
21344              * @param {HtmlEditor} this
21345              * @param {String} html
21346              */
21347             sync: true,
21348              /**
21349              * @event push
21350              * Fires when the iframe editor is updated with content from the textarea.
21351              * @param {HtmlEditor} this
21352              * @param {String} html
21353              */
21354             push: true,
21355              /**
21356              * @event editmodechange
21357              * Fires when the editor switches edit modes
21358              * @param {HtmlEditor} this
21359              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21360              */
21361             editmodechange: true,
21362             /**
21363              * @event editorevent
21364              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21365              * @param {HtmlEditor} this
21366              */
21367             editorevent: true,
21368             /**
21369              * @event firstfocus
21370              * Fires when on first focus - needed by toolbars..
21371              * @param {HtmlEditor} this
21372              */
21373             firstfocus: true,
21374             /**
21375              * @event autosave
21376              * Auto save the htmlEditor value as a file into Events
21377              * @param {HtmlEditor} this
21378              */
21379             autosave: true,
21380             /**
21381              * @event savedpreview
21382              * preview the saved version of htmlEditor
21383              * @param {HtmlEditor} this
21384              */
21385             savedpreview: true
21386         });
21387 };
21388
21389
21390 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21391     
21392     
21393       /**
21394      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21395      */
21396     toolbars : false,
21397    
21398      /**
21399      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21400      *                        Roo.resizable.
21401      */
21402     resizable : false,
21403      /**
21404      * @cfg {Number} height (in pixels)
21405      */   
21406     height: 300,
21407    /**
21408      * @cfg {Number} width (in pixels)
21409      */   
21410     width: false,
21411     
21412     /**
21413      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21414      * 
21415      */
21416     stylesheets: false,
21417     
21418     // id of frame..
21419     frameId: false,
21420     
21421     // private properties
21422     validationEvent : false,
21423     deferHeight: true,
21424     initialized : false,
21425     activated : false,
21426     
21427     onFocus : Roo.emptyFn,
21428     iframePad:3,
21429     hideMode:'offsets',
21430     
21431     
21432     tbContainer : false,
21433     
21434     toolbarContainer :function() {
21435         return this.wrap.select('.x-html-editor-tb',true).first();
21436     },
21437
21438     /**
21439      * Protected method that will not generally be called directly. It
21440      * is called when the editor creates its toolbar. Override this method if you need to
21441      * add custom toolbar buttons.
21442      * @param {HtmlEditor} editor
21443      */
21444     createToolbar : function(){
21445         
21446         Roo.log("create toolbars");
21447         
21448         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21449         this.toolbars[0].render(this.toolbarContainer());
21450         
21451         return;
21452         
21453 //        if (!editor.toolbars || !editor.toolbars.length) {
21454 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21455 //        }
21456 //        
21457 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21458 //            editor.toolbars[i] = Roo.factory(
21459 //                    typeof(editor.toolbars[i]) == 'string' ?
21460 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21461 //                Roo.bootstrap.HtmlEditor);
21462 //            editor.toolbars[i].init(editor);
21463 //        }
21464     },
21465
21466      
21467     // private
21468     onRender : function(ct, position)
21469     {
21470        // Roo.log("Call onRender: " + this.xtype);
21471         var _t = this;
21472         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21473       
21474         this.wrap = this.inputEl().wrap({
21475             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21476         });
21477         
21478         this.editorcore.onRender(ct, position);
21479          
21480         if (this.resizable) {
21481             this.resizeEl = new Roo.Resizable(this.wrap, {
21482                 pinned : true,
21483                 wrap: true,
21484                 dynamic : true,
21485                 minHeight : this.height,
21486                 height: this.height,
21487                 handles : this.resizable,
21488                 width: this.width,
21489                 listeners : {
21490                     resize : function(r, w, h) {
21491                         _t.onResize(w,h); // -something
21492                     }
21493                 }
21494             });
21495             
21496         }
21497         this.createToolbar(this);
21498        
21499         
21500         if(!this.width && this.resizable){
21501             this.setSize(this.wrap.getSize());
21502         }
21503         if (this.resizeEl) {
21504             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21505             // should trigger onReize..
21506         }
21507         
21508     },
21509
21510     // private
21511     onResize : function(w, h)
21512     {
21513         Roo.log('resize: ' +w + ',' + h );
21514         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21515         var ew = false;
21516         var eh = false;
21517         
21518         if(this.inputEl() ){
21519             if(typeof w == 'number'){
21520                 var aw = w - this.wrap.getFrameWidth('lr');
21521                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21522                 ew = aw;
21523             }
21524             if(typeof h == 'number'){
21525                  var tbh = -11;  // fixme it needs to tool bar size!
21526                 for (var i =0; i < this.toolbars.length;i++) {
21527                     // fixme - ask toolbars for heights?
21528                     tbh += this.toolbars[i].el.getHeight();
21529                     //if (this.toolbars[i].footer) {
21530                     //    tbh += this.toolbars[i].footer.el.getHeight();
21531                     //}
21532                 }
21533               
21534                 
21535                 
21536                 
21537                 
21538                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21539                 ah -= 5; // knock a few pixes off for look..
21540                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21541                 var eh = ah;
21542             }
21543         }
21544         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21545         this.editorcore.onResize(ew,eh);
21546         
21547     },
21548
21549     /**
21550      * Toggles the editor between standard and source edit mode.
21551      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21552      */
21553     toggleSourceEdit : function(sourceEditMode)
21554     {
21555         this.editorcore.toggleSourceEdit(sourceEditMode);
21556         
21557         if(this.editorcore.sourceEditMode){
21558             Roo.log('editor - showing textarea');
21559             
21560 //            Roo.log('in');
21561 //            Roo.log(this.syncValue());
21562             this.syncValue();
21563             this.inputEl().removeClass(['hide', 'x-hidden']);
21564             this.inputEl().dom.removeAttribute('tabIndex');
21565             this.inputEl().focus();
21566         }else{
21567             Roo.log('editor - hiding textarea');
21568 //            Roo.log('out')
21569 //            Roo.log(this.pushValue()); 
21570             this.pushValue();
21571             
21572             this.inputEl().addClass(['hide', 'x-hidden']);
21573             this.inputEl().dom.setAttribute('tabIndex', -1);
21574             //this.deferFocus();
21575         }
21576          
21577         if(this.resizable){
21578             this.setSize(this.wrap.getSize());
21579         }
21580         
21581         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21582     },
21583  
21584     // private (for BoxComponent)
21585     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21586
21587     // private (for BoxComponent)
21588     getResizeEl : function(){
21589         return this.wrap;
21590     },
21591
21592     // private (for BoxComponent)
21593     getPositionEl : function(){
21594         return this.wrap;
21595     },
21596
21597     // private
21598     initEvents : function(){
21599         this.originalValue = this.getValue();
21600     },
21601
21602 //    /**
21603 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21604 //     * @method
21605 //     */
21606 //    markInvalid : Roo.emptyFn,
21607 //    /**
21608 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21609 //     * @method
21610 //     */
21611 //    clearInvalid : Roo.emptyFn,
21612
21613     setValue : function(v){
21614         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21615         this.editorcore.pushValue();
21616     },
21617
21618      
21619     // private
21620     deferFocus : function(){
21621         this.focus.defer(10, this);
21622     },
21623
21624     // doc'ed in Field
21625     focus : function(){
21626         this.editorcore.focus();
21627         
21628     },
21629       
21630
21631     // private
21632     onDestroy : function(){
21633         
21634         
21635         
21636         if(this.rendered){
21637             
21638             for (var i =0; i < this.toolbars.length;i++) {
21639                 // fixme - ask toolbars for heights?
21640                 this.toolbars[i].onDestroy();
21641             }
21642             
21643             this.wrap.dom.innerHTML = '';
21644             this.wrap.remove();
21645         }
21646     },
21647
21648     // private
21649     onFirstFocus : function(){
21650         //Roo.log("onFirstFocus");
21651         this.editorcore.onFirstFocus();
21652          for (var i =0; i < this.toolbars.length;i++) {
21653             this.toolbars[i].onFirstFocus();
21654         }
21655         
21656     },
21657     
21658     // private
21659     syncValue : function()
21660     {   
21661         this.editorcore.syncValue();
21662     },
21663     
21664     pushValue : function()
21665     {   
21666         this.editorcore.pushValue();
21667     }
21668      
21669     
21670     // hide stuff that is not compatible
21671     /**
21672      * @event blur
21673      * @hide
21674      */
21675     /**
21676      * @event change
21677      * @hide
21678      */
21679     /**
21680      * @event focus
21681      * @hide
21682      */
21683     /**
21684      * @event specialkey
21685      * @hide
21686      */
21687     /**
21688      * @cfg {String} fieldClass @hide
21689      */
21690     /**
21691      * @cfg {String} focusClass @hide
21692      */
21693     /**
21694      * @cfg {String} autoCreate @hide
21695      */
21696     /**
21697      * @cfg {String} inputType @hide
21698      */
21699     /**
21700      * @cfg {String} invalidClass @hide
21701      */
21702     /**
21703      * @cfg {String} invalidText @hide
21704      */
21705     /**
21706      * @cfg {String} msgFx @hide
21707      */
21708     /**
21709      * @cfg {String} validateOnBlur @hide
21710      */
21711 });
21712  
21713     
21714    
21715    
21716    
21717       
21718 Roo.namespace('Roo.bootstrap.htmleditor');
21719 /**
21720  * @class Roo.bootstrap.HtmlEditorToolbar1
21721  * Basic Toolbar
21722  * 
21723  * Usage:
21724  *
21725  new Roo.bootstrap.HtmlEditor({
21726     ....
21727     toolbars : [
21728         new Roo.bootstrap.HtmlEditorToolbar1({
21729             disable : { fonts: 1 , format: 1, ..., ... , ...],
21730             btns : [ .... ]
21731         })
21732     }
21733      
21734  * 
21735  * @cfg {Object} disable List of elements to disable..
21736  * @cfg {Array} btns List of additional buttons.
21737  * 
21738  * 
21739  * NEEDS Extra CSS? 
21740  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21741  */
21742  
21743 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21744 {
21745     
21746     Roo.apply(this, config);
21747     
21748     // default disabled, based on 'good practice'..
21749     this.disable = this.disable || {};
21750     Roo.applyIf(this.disable, {
21751         fontSize : true,
21752         colors : true,
21753         specialElements : true
21754     });
21755     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21756     
21757     this.editor = config.editor;
21758     this.editorcore = config.editor.editorcore;
21759     
21760     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21761     
21762     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21763     // dont call parent... till later.
21764 }
21765 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21766      
21767     bar : true,
21768     
21769     editor : false,
21770     editorcore : false,
21771     
21772     
21773     formats : [
21774         "p" ,  
21775         "h1","h2","h3","h4","h5","h6", 
21776         "pre", "code", 
21777         "abbr", "acronym", "address", "cite", "samp", "var",
21778         'div','span'
21779     ],
21780     
21781     onRender : function(ct, position)
21782     {
21783        // Roo.log("Call onRender: " + this.xtype);
21784         
21785        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21786        Roo.log(this.el);
21787        this.el.dom.style.marginBottom = '0';
21788        var _this = this;
21789        var editorcore = this.editorcore;
21790        var editor= this.editor;
21791        
21792        var children = [];
21793        var btn = function(id,cmd , toggle, handler){
21794        
21795             var  event = toggle ? 'toggle' : 'click';
21796        
21797             var a = {
21798                 size : 'sm',
21799                 xtype: 'Button',
21800                 xns: Roo.bootstrap,
21801                 glyphicon : id,
21802                 cmd : id || cmd,
21803                 enableToggle:toggle !== false,
21804                 //html : 'submit'
21805                 pressed : toggle ? false : null,
21806                 listeners : {}
21807             };
21808             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21809                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21810             };
21811             children.push(a);
21812             return a;
21813        }
21814         
21815         var style = {
21816                 xtype: 'Button',
21817                 size : 'sm',
21818                 xns: Roo.bootstrap,
21819                 glyphicon : 'font',
21820                 //html : 'submit'
21821                 menu : {
21822                     xtype: 'Menu',
21823                     xns: Roo.bootstrap,
21824                     items:  []
21825                 }
21826         };
21827         Roo.each(this.formats, function(f) {
21828             style.menu.items.push({
21829                 xtype :'MenuItem',
21830                 xns: Roo.bootstrap,
21831                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21832                 tagname : f,
21833                 listeners : {
21834                     click : function()
21835                     {
21836                         editorcore.insertTag(this.tagname);
21837                         editor.focus();
21838                     }
21839                 }
21840                 
21841             });
21842         });
21843          children.push(style);   
21844             
21845             
21846         btn('bold',false,true);
21847         btn('italic',false,true);
21848         btn('align-left', 'justifyleft',true);
21849         btn('align-center', 'justifycenter',true);
21850         btn('align-right' , 'justifyright',true);
21851         btn('link', false, false, function(btn) {
21852             //Roo.log("create link?");
21853             var url = prompt(this.createLinkText, this.defaultLinkValue);
21854             if(url && url != 'http:/'+'/'){
21855                 this.editorcore.relayCmd('createlink', url);
21856             }
21857         }),
21858         btn('list','insertunorderedlist',true);
21859         btn('pencil', false,true, function(btn){
21860                 Roo.log(this);
21861                 
21862                 this.toggleSourceEdit(btn.pressed);
21863         });
21864         /*
21865         var cog = {
21866                 xtype: 'Button',
21867                 size : 'sm',
21868                 xns: Roo.bootstrap,
21869                 glyphicon : 'cog',
21870                 //html : 'submit'
21871                 menu : {
21872                     xtype: 'Menu',
21873                     xns: Roo.bootstrap,
21874                     items:  []
21875                 }
21876         };
21877         
21878         cog.menu.items.push({
21879             xtype :'MenuItem',
21880             xns: Roo.bootstrap,
21881             html : Clean styles,
21882             tagname : f,
21883             listeners : {
21884                 click : function()
21885                 {
21886                     editorcore.insertTag(this.tagname);
21887                     editor.focus();
21888                 }
21889             }
21890             
21891         });
21892        */
21893         
21894          
21895        this.xtype = 'NavSimplebar';
21896         
21897         for(var i=0;i< children.length;i++) {
21898             
21899             this.buttons.add(this.addxtypeChild(children[i]));
21900             
21901         }
21902         
21903         editor.on('editorevent', this.updateToolbar, this);
21904     },
21905     onBtnClick : function(id)
21906     {
21907        this.editorcore.relayCmd(id);
21908        this.editorcore.focus();
21909     },
21910     
21911     /**
21912      * Protected method that will not generally be called directly. It triggers
21913      * a toolbar update by reading the markup state of the current selection in the editor.
21914      */
21915     updateToolbar: function(){
21916
21917         if(!this.editorcore.activated){
21918             this.editor.onFirstFocus(); // is this neeed?
21919             return;
21920         }
21921
21922         var btns = this.buttons; 
21923         var doc = this.editorcore.doc;
21924         btns.get('bold').setActive(doc.queryCommandState('bold'));
21925         btns.get('italic').setActive(doc.queryCommandState('italic'));
21926         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21927         
21928         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21929         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21930         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21931         
21932         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21933         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21934          /*
21935         
21936         var ans = this.editorcore.getAllAncestors();
21937         if (this.formatCombo) {
21938             
21939             
21940             var store = this.formatCombo.store;
21941             this.formatCombo.setValue("");
21942             for (var i =0; i < ans.length;i++) {
21943                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21944                     // select it..
21945                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21946                     break;
21947                 }
21948             }
21949         }
21950         
21951         
21952         
21953         // hides menus... - so this cant be on a menu...
21954         Roo.bootstrap.MenuMgr.hideAll();
21955         */
21956         Roo.bootstrap.MenuMgr.hideAll();
21957         //this.editorsyncValue();
21958     },
21959     onFirstFocus: function() {
21960         this.buttons.each(function(item){
21961            item.enable();
21962         });
21963     },
21964     toggleSourceEdit : function(sourceEditMode){
21965         
21966           
21967         if(sourceEditMode){
21968             Roo.log("disabling buttons");
21969            this.buttons.each( function(item){
21970                 if(item.cmd != 'pencil'){
21971                     item.disable();
21972                 }
21973             });
21974           
21975         }else{
21976             Roo.log("enabling buttons");
21977             if(this.editorcore.initialized){
21978                 this.buttons.each( function(item){
21979                     item.enable();
21980                 });
21981             }
21982             
21983         }
21984         Roo.log("calling toggole on editor");
21985         // tell the editor that it's been pressed..
21986         this.editor.toggleSourceEdit(sourceEditMode);
21987        
21988     }
21989 });
21990
21991
21992
21993
21994
21995 /**
21996  * @class Roo.bootstrap.Table.AbstractSelectionModel
21997  * @extends Roo.util.Observable
21998  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21999  * implemented by descendant classes.  This class should not be directly instantiated.
22000  * @constructor
22001  */
22002 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22003     this.locked = false;
22004     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22005 };
22006
22007
22008 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22009     /** @ignore Called by the grid automatically. Do not call directly. */
22010     init : function(grid){
22011         this.grid = grid;
22012         this.initEvents();
22013     },
22014
22015     /**
22016      * Locks the selections.
22017      */
22018     lock : function(){
22019         this.locked = true;
22020     },
22021
22022     /**
22023      * Unlocks the selections.
22024      */
22025     unlock : function(){
22026         this.locked = false;
22027     },
22028
22029     /**
22030      * Returns true if the selections are locked.
22031      * @return {Boolean}
22032      */
22033     isLocked : function(){
22034         return this.locked;
22035     }
22036 });
22037 /**
22038  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22039  * @class Roo.bootstrap.Table.RowSelectionModel
22040  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22041  * It supports multiple selections and keyboard selection/navigation. 
22042  * @constructor
22043  * @param {Object} config
22044  */
22045
22046 Roo.bootstrap.Table.RowSelectionModel = function(config){
22047     Roo.apply(this, config);
22048     this.selections = new Roo.util.MixedCollection(false, function(o){
22049         return o.id;
22050     });
22051
22052     this.last = false;
22053     this.lastActive = false;
22054
22055     this.addEvents({
22056         /**
22057              * @event selectionchange
22058              * Fires when the selection changes
22059              * @param {SelectionModel} this
22060              */
22061             "selectionchange" : true,
22062         /**
22063              * @event afterselectionchange
22064              * Fires after the selection changes (eg. by key press or clicking)
22065              * @param {SelectionModel} this
22066              */
22067             "afterselectionchange" : true,
22068         /**
22069              * @event beforerowselect
22070              * Fires when a row is selected being selected, return false to cancel.
22071              * @param {SelectionModel} this
22072              * @param {Number} rowIndex The selected index
22073              * @param {Boolean} keepExisting False if other selections will be cleared
22074              */
22075             "beforerowselect" : true,
22076         /**
22077              * @event rowselect
22078              * Fires when a row is selected.
22079              * @param {SelectionModel} this
22080              * @param {Number} rowIndex The selected index
22081              * @param {Roo.data.Record} r The record
22082              */
22083             "rowselect" : true,
22084         /**
22085              * @event rowdeselect
22086              * Fires when a row is deselected.
22087              * @param {SelectionModel} this
22088              * @param {Number} rowIndex The selected index
22089              */
22090         "rowdeselect" : true
22091     });
22092     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22093     this.locked = false;
22094 };
22095
22096 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22097     /**
22098      * @cfg {Boolean} singleSelect
22099      * True to allow selection of only one row at a time (defaults to false)
22100      */
22101     singleSelect : false,
22102
22103     // private
22104     initEvents : function(){
22105
22106         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22107             this.grid.on("mousedown", this.handleMouseDown, this);
22108         }else{ // allow click to work like normal
22109             this.grid.on("rowclick", this.handleDragableRowClick, this);
22110         }
22111
22112         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22113             "up" : function(e){
22114                 if(!e.shiftKey){
22115                     this.selectPrevious(e.shiftKey);
22116                 }else if(this.last !== false && this.lastActive !== false){
22117                     var last = this.last;
22118                     this.selectRange(this.last,  this.lastActive-1);
22119                     this.grid.getView().focusRow(this.lastActive);
22120                     if(last !== false){
22121                         this.last = last;
22122                     }
22123                 }else{
22124                     this.selectFirstRow();
22125                 }
22126                 this.fireEvent("afterselectionchange", this);
22127             },
22128             "down" : function(e){
22129                 if(!e.shiftKey){
22130                     this.selectNext(e.shiftKey);
22131                 }else if(this.last !== false && this.lastActive !== false){
22132                     var last = this.last;
22133                     this.selectRange(this.last,  this.lastActive+1);
22134                     this.grid.getView().focusRow(this.lastActive);
22135                     if(last !== false){
22136                         this.last = last;
22137                     }
22138                 }else{
22139                     this.selectFirstRow();
22140                 }
22141                 this.fireEvent("afterselectionchange", this);
22142             },
22143             scope: this
22144         });
22145
22146         var view = this.grid.view;
22147         view.on("refresh", this.onRefresh, this);
22148         view.on("rowupdated", this.onRowUpdated, this);
22149         view.on("rowremoved", this.onRemove, this);
22150     },
22151
22152     // private
22153     onRefresh : function(){
22154         var ds = this.grid.dataSource, i, v = this.grid.view;
22155         var s = this.selections;
22156         s.each(function(r){
22157             if((i = ds.indexOfId(r.id)) != -1){
22158                 v.onRowSelect(i);
22159             }else{
22160                 s.remove(r);
22161             }
22162         });
22163     },
22164
22165     // private
22166     onRemove : function(v, index, r){
22167         this.selections.remove(r);
22168     },
22169
22170     // private
22171     onRowUpdated : function(v, index, r){
22172         if(this.isSelected(r)){
22173             v.onRowSelect(index);
22174         }
22175     },
22176
22177     /**
22178      * Select records.
22179      * @param {Array} records The records to select
22180      * @param {Boolean} keepExisting (optional) True to keep existing selections
22181      */
22182     selectRecords : function(records, keepExisting){
22183         if(!keepExisting){
22184             this.clearSelections();
22185         }
22186         var ds = this.grid.dataSource;
22187         for(var i = 0, len = records.length; i < len; i++){
22188             this.selectRow(ds.indexOf(records[i]), true);
22189         }
22190     },
22191
22192     /**
22193      * Gets the number of selected rows.
22194      * @return {Number}
22195      */
22196     getCount : function(){
22197         return this.selections.length;
22198     },
22199
22200     /**
22201      * Selects the first row in the grid.
22202      */
22203     selectFirstRow : function(){
22204         this.selectRow(0);
22205     },
22206
22207     /**
22208      * Select the last row.
22209      * @param {Boolean} keepExisting (optional) True to keep existing selections
22210      */
22211     selectLastRow : function(keepExisting){
22212         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22213     },
22214
22215     /**
22216      * Selects the row immediately following the last selected row.
22217      * @param {Boolean} keepExisting (optional) True to keep existing selections
22218      */
22219     selectNext : function(keepExisting){
22220         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22221             this.selectRow(this.last+1, keepExisting);
22222             this.grid.getView().focusRow(this.last);
22223         }
22224     },
22225
22226     /**
22227      * Selects the row that precedes the last selected row.
22228      * @param {Boolean} keepExisting (optional) True to keep existing selections
22229      */
22230     selectPrevious : function(keepExisting){
22231         if(this.last){
22232             this.selectRow(this.last-1, keepExisting);
22233             this.grid.getView().focusRow(this.last);
22234         }
22235     },
22236
22237     /**
22238      * Returns the selected records
22239      * @return {Array} Array of selected records
22240      */
22241     getSelections : function(){
22242         return [].concat(this.selections.items);
22243     },
22244
22245     /**
22246      * Returns the first selected record.
22247      * @return {Record}
22248      */
22249     getSelected : function(){
22250         return this.selections.itemAt(0);
22251     },
22252
22253
22254     /**
22255      * Clears all selections.
22256      */
22257     clearSelections : function(fast){
22258         if(this.locked) {
22259             return;
22260         }
22261         if(fast !== true){
22262             var ds = this.grid.dataSource;
22263             var s = this.selections;
22264             s.each(function(r){
22265                 this.deselectRow(ds.indexOfId(r.id));
22266             }, this);
22267             s.clear();
22268         }else{
22269             this.selections.clear();
22270         }
22271         this.last = false;
22272     },
22273
22274
22275     /**
22276      * Selects all rows.
22277      */
22278     selectAll : function(){
22279         if(this.locked) {
22280             return;
22281         }
22282         this.selections.clear();
22283         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22284             this.selectRow(i, true);
22285         }
22286     },
22287
22288     /**
22289      * Returns True if there is a selection.
22290      * @return {Boolean}
22291      */
22292     hasSelection : function(){
22293         return this.selections.length > 0;
22294     },
22295
22296     /**
22297      * Returns True if the specified row is selected.
22298      * @param {Number/Record} record The record or index of the record to check
22299      * @return {Boolean}
22300      */
22301     isSelected : function(index){
22302         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22303         return (r && this.selections.key(r.id) ? true : false);
22304     },
22305
22306     /**
22307      * Returns True if the specified record id is selected.
22308      * @param {String} id The id of record to check
22309      * @return {Boolean}
22310      */
22311     isIdSelected : function(id){
22312         return (this.selections.key(id) ? true : false);
22313     },
22314
22315     // private
22316     handleMouseDown : function(e, t){
22317         var view = this.grid.getView(), rowIndex;
22318         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22319             return;
22320         };
22321         if(e.shiftKey && this.last !== false){
22322             var last = this.last;
22323             this.selectRange(last, rowIndex, e.ctrlKey);
22324             this.last = last; // reset the last
22325             view.focusRow(rowIndex);
22326         }else{
22327             var isSelected = this.isSelected(rowIndex);
22328             if(e.button !== 0 && isSelected){
22329                 view.focusRow(rowIndex);
22330             }else if(e.ctrlKey && isSelected){
22331                 this.deselectRow(rowIndex);
22332             }else if(!isSelected){
22333                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22334                 view.focusRow(rowIndex);
22335             }
22336         }
22337         this.fireEvent("afterselectionchange", this);
22338     },
22339     // private
22340     handleDragableRowClick :  function(grid, rowIndex, e) 
22341     {
22342         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22343             this.selectRow(rowIndex, false);
22344             grid.view.focusRow(rowIndex);
22345              this.fireEvent("afterselectionchange", this);
22346         }
22347     },
22348     
22349     /**
22350      * Selects multiple rows.
22351      * @param {Array} rows Array of the indexes of the row to select
22352      * @param {Boolean} keepExisting (optional) True to keep existing selections
22353      */
22354     selectRows : function(rows, keepExisting){
22355         if(!keepExisting){
22356             this.clearSelections();
22357         }
22358         for(var i = 0, len = rows.length; i < len; i++){
22359             this.selectRow(rows[i], true);
22360         }
22361     },
22362
22363     /**
22364      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22365      * @param {Number} startRow The index of the first row in the range
22366      * @param {Number} endRow The index of the last row in the range
22367      * @param {Boolean} keepExisting (optional) True to retain existing selections
22368      */
22369     selectRange : function(startRow, endRow, keepExisting){
22370         if(this.locked) {
22371             return;
22372         }
22373         if(!keepExisting){
22374             this.clearSelections();
22375         }
22376         if(startRow <= endRow){
22377             for(var i = startRow; i <= endRow; i++){
22378                 this.selectRow(i, true);
22379             }
22380         }else{
22381             for(var i = startRow; i >= endRow; i--){
22382                 this.selectRow(i, true);
22383             }
22384         }
22385     },
22386
22387     /**
22388      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22389      * @param {Number} startRow The index of the first row in the range
22390      * @param {Number} endRow The index of the last row in the range
22391      */
22392     deselectRange : function(startRow, endRow, preventViewNotify){
22393         if(this.locked) {
22394             return;
22395         }
22396         for(var i = startRow; i <= endRow; i++){
22397             this.deselectRow(i, preventViewNotify);
22398         }
22399     },
22400
22401     /**
22402      * Selects a row.
22403      * @param {Number} row The index of the row to select
22404      * @param {Boolean} keepExisting (optional) True to keep existing selections
22405      */
22406     selectRow : function(index, keepExisting, preventViewNotify){
22407         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22408             return;
22409         }
22410         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22411             if(!keepExisting || this.singleSelect){
22412                 this.clearSelections();
22413             }
22414             var r = this.grid.dataSource.getAt(index);
22415             this.selections.add(r);
22416             this.last = this.lastActive = index;
22417             if(!preventViewNotify){
22418                 this.grid.getView().onRowSelect(index);
22419             }
22420             this.fireEvent("rowselect", this, index, r);
22421             this.fireEvent("selectionchange", this);
22422         }
22423     },
22424
22425     /**
22426      * Deselects a row.
22427      * @param {Number} row The index of the row to deselect
22428      */
22429     deselectRow : function(index, preventViewNotify){
22430         if(this.locked) {
22431             return;
22432         }
22433         if(this.last == index){
22434             this.last = false;
22435         }
22436         if(this.lastActive == index){
22437             this.lastActive = false;
22438         }
22439         var r = this.grid.dataSource.getAt(index);
22440         this.selections.remove(r);
22441         if(!preventViewNotify){
22442             this.grid.getView().onRowDeselect(index);
22443         }
22444         this.fireEvent("rowdeselect", this, index);
22445         this.fireEvent("selectionchange", this);
22446     },
22447
22448     // private
22449     restoreLast : function(){
22450         if(this._last){
22451             this.last = this._last;
22452         }
22453     },
22454
22455     // private
22456     acceptsNav : function(row, col, cm){
22457         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22458     },
22459
22460     // private
22461     onEditorKey : function(field, e){
22462         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22463         if(k == e.TAB){
22464             e.stopEvent();
22465             ed.completeEdit();
22466             if(e.shiftKey){
22467                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22468             }else{
22469                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22470             }
22471         }else if(k == e.ENTER && !e.ctrlKey){
22472             e.stopEvent();
22473             ed.completeEdit();
22474             if(e.shiftKey){
22475                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22476             }else{
22477                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22478             }
22479         }else if(k == e.ESC){
22480             ed.cancelEdit();
22481         }
22482         if(newCell){
22483             g.startEditing(newCell[0], newCell[1]);
22484         }
22485     }
22486 });/*
22487  * Based on:
22488  * Ext JS Library 1.1.1
22489  * Copyright(c) 2006-2007, Ext JS, LLC.
22490  *
22491  * Originally Released Under LGPL - original licence link has changed is not relivant.
22492  *
22493  * Fork - LGPL
22494  * <script type="text/javascript">
22495  */
22496  
22497 /**
22498  * @class Roo.bootstrap.PagingToolbar
22499  * @extends Roo.bootstrap.NavSimplebar
22500  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22501  * @constructor
22502  * Create a new PagingToolbar
22503  * @param {Object} config The config object
22504  * @param {Roo.data.Store} store
22505  */
22506 Roo.bootstrap.PagingToolbar = function(config)
22507 {
22508     // old args format still supported... - xtype is prefered..
22509         // created from xtype...
22510     
22511     this.ds = config.dataSource;
22512     
22513     if (config.store && !this.ds) {
22514         this.store= Roo.factory(config.store, Roo.data);
22515         this.ds = this.store;
22516         this.ds.xmodule = this.xmodule || false;
22517     }
22518     
22519     this.toolbarItems = [];
22520     if (config.items) {
22521         this.toolbarItems = config.items;
22522     }
22523     
22524     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22525     
22526     this.cursor = 0;
22527     
22528     if (this.ds) { 
22529         this.bind(this.ds);
22530     }
22531     
22532     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22533     
22534 };
22535
22536 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22537     /**
22538      * @cfg {Roo.data.Store} dataSource
22539      * The underlying data store providing the paged data
22540      */
22541     /**
22542      * @cfg {String/HTMLElement/Element} container
22543      * container The id or element that will contain the toolbar
22544      */
22545     /**
22546      * @cfg {Boolean} displayInfo
22547      * True to display the displayMsg (defaults to false)
22548      */
22549     /**
22550      * @cfg {Number} pageSize
22551      * The number of records to display per page (defaults to 20)
22552      */
22553     pageSize: 20,
22554     /**
22555      * @cfg {String} displayMsg
22556      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22557      */
22558     displayMsg : 'Displaying {0} - {1} of {2}',
22559     /**
22560      * @cfg {String} emptyMsg
22561      * The message to display when no records are found (defaults to "No data to display")
22562      */
22563     emptyMsg : 'No data to display',
22564     /**
22565      * Customizable piece of the default paging text (defaults to "Page")
22566      * @type String
22567      */
22568     beforePageText : "Page",
22569     /**
22570      * Customizable piece of the default paging text (defaults to "of %0")
22571      * @type String
22572      */
22573     afterPageText : "of {0}",
22574     /**
22575      * Customizable piece of the default paging text (defaults to "First Page")
22576      * @type String
22577      */
22578     firstText : "First Page",
22579     /**
22580      * Customizable piece of the default paging text (defaults to "Previous Page")
22581      * @type String
22582      */
22583     prevText : "Previous Page",
22584     /**
22585      * Customizable piece of the default paging text (defaults to "Next Page")
22586      * @type String
22587      */
22588     nextText : "Next Page",
22589     /**
22590      * Customizable piece of the default paging text (defaults to "Last Page")
22591      * @type String
22592      */
22593     lastText : "Last Page",
22594     /**
22595      * Customizable piece of the default paging text (defaults to "Refresh")
22596      * @type String
22597      */
22598     refreshText : "Refresh",
22599
22600     buttons : false,
22601     // private
22602     onRender : function(ct, position) 
22603     {
22604         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22605         this.navgroup.parentId = this.id;
22606         this.navgroup.onRender(this.el, null);
22607         // add the buttons to the navgroup
22608         
22609         if(this.displayInfo){
22610             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22611             this.displayEl = this.el.select('.x-paging-info', true).first();
22612 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22613 //            this.displayEl = navel.el.select('span',true).first();
22614         }
22615         
22616         var _this = this;
22617         
22618         if(this.buttons){
22619             Roo.each(_this.buttons, function(e){ // this might need to use render????
22620                Roo.factory(e).onRender(_this.el, null);
22621             });
22622         }
22623             
22624         Roo.each(_this.toolbarItems, function(e) {
22625             _this.navgroup.addItem(e);
22626         });
22627         
22628         
22629         this.first = this.navgroup.addItem({
22630             tooltip: this.firstText,
22631             cls: "prev",
22632             icon : 'fa fa-backward',
22633             disabled: true,
22634             preventDefault: true,
22635             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22636         });
22637         
22638         this.prev =  this.navgroup.addItem({
22639             tooltip: this.prevText,
22640             cls: "prev",
22641             icon : 'fa fa-step-backward',
22642             disabled: true,
22643             preventDefault: true,
22644             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22645         });
22646     //this.addSeparator();
22647         
22648         
22649         var field = this.navgroup.addItem( {
22650             tagtype : 'span',
22651             cls : 'x-paging-position',
22652             
22653             html : this.beforePageText  +
22654                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22655                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22656          } ); //?? escaped?
22657         
22658         this.field = field.el.select('input', true).first();
22659         this.field.on("keydown", this.onPagingKeydown, this);
22660         this.field.on("focus", function(){this.dom.select();});
22661     
22662     
22663         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22664         //this.field.setHeight(18);
22665         //this.addSeparator();
22666         this.next = this.navgroup.addItem({
22667             tooltip: this.nextText,
22668             cls: "next",
22669             html : ' <i class="fa fa-step-forward">',
22670             disabled: true,
22671             preventDefault: true,
22672             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22673         });
22674         this.last = this.navgroup.addItem({
22675             tooltip: this.lastText,
22676             icon : 'fa fa-forward',
22677             cls: "next",
22678             disabled: true,
22679             preventDefault: true,
22680             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22681         });
22682     //this.addSeparator();
22683         this.loading = this.navgroup.addItem({
22684             tooltip: this.refreshText,
22685             icon: 'fa fa-refresh',
22686             preventDefault: true,
22687             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22688         });
22689         
22690     },
22691
22692     // private
22693     updateInfo : function(){
22694         if(this.displayEl){
22695             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22696             var msg = count == 0 ?
22697                 this.emptyMsg :
22698                 String.format(
22699                     this.displayMsg,
22700                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22701                 );
22702             this.displayEl.update(msg);
22703         }
22704     },
22705
22706     // private
22707     onLoad : function(ds, r, o){
22708        this.cursor = o.params ? o.params.start : 0;
22709        var d = this.getPageData(),
22710             ap = d.activePage,
22711             ps = d.pages;
22712         
22713        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22714        this.field.dom.value = ap;
22715        this.first.setDisabled(ap == 1);
22716        this.prev.setDisabled(ap == 1);
22717        this.next.setDisabled(ap == ps);
22718        this.last.setDisabled(ap == ps);
22719        this.loading.enable();
22720        this.updateInfo();
22721     },
22722
22723     // private
22724     getPageData : function(){
22725         var total = this.ds.getTotalCount();
22726         return {
22727             total : total,
22728             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22729             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22730         };
22731     },
22732
22733     // private
22734     onLoadError : function(){
22735         this.loading.enable();
22736     },
22737
22738     // private
22739     onPagingKeydown : function(e){
22740         var k = e.getKey();
22741         var d = this.getPageData();
22742         if(k == e.RETURN){
22743             var v = this.field.dom.value, pageNum;
22744             if(!v || isNaN(pageNum = parseInt(v, 10))){
22745                 this.field.dom.value = d.activePage;
22746                 return;
22747             }
22748             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22749             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22750             e.stopEvent();
22751         }
22752         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))
22753         {
22754           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22755           this.field.dom.value = pageNum;
22756           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22757           e.stopEvent();
22758         }
22759         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22760         {
22761           var v = this.field.dom.value, pageNum; 
22762           var increment = (e.shiftKey) ? 10 : 1;
22763           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22764                 increment *= -1;
22765           }
22766           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22767             this.field.dom.value = d.activePage;
22768             return;
22769           }
22770           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22771           {
22772             this.field.dom.value = parseInt(v, 10) + increment;
22773             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22774             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22775           }
22776           e.stopEvent();
22777         }
22778     },
22779
22780     // private
22781     beforeLoad : function(){
22782         if(this.loading){
22783             this.loading.disable();
22784         }
22785     },
22786
22787     // private
22788     onClick : function(which){
22789         
22790         var ds = this.ds;
22791         if (!ds) {
22792             return;
22793         }
22794         
22795         switch(which){
22796             case "first":
22797                 ds.load({params:{start: 0, limit: this.pageSize}});
22798             break;
22799             case "prev":
22800                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22801             break;
22802             case "next":
22803                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22804             break;
22805             case "last":
22806                 var total = ds.getTotalCount();
22807                 var extra = total % this.pageSize;
22808                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22809                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22810             break;
22811             case "refresh":
22812                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22813             break;
22814         }
22815     },
22816
22817     /**
22818      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22819      * @param {Roo.data.Store} store The data store to unbind
22820      */
22821     unbind : function(ds){
22822         ds.un("beforeload", this.beforeLoad, this);
22823         ds.un("load", this.onLoad, this);
22824         ds.un("loadexception", this.onLoadError, this);
22825         ds.un("remove", this.updateInfo, this);
22826         ds.un("add", this.updateInfo, this);
22827         this.ds = undefined;
22828     },
22829
22830     /**
22831      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22832      * @param {Roo.data.Store} store The data store to bind
22833      */
22834     bind : function(ds){
22835         ds.on("beforeload", this.beforeLoad, this);
22836         ds.on("load", this.onLoad, this);
22837         ds.on("loadexception", this.onLoadError, this);
22838         ds.on("remove", this.updateInfo, this);
22839         ds.on("add", this.updateInfo, this);
22840         this.ds = ds;
22841     }
22842 });/*
22843  * - LGPL
22844  *
22845  * element
22846  * 
22847  */
22848
22849 /**
22850  * @class Roo.bootstrap.MessageBar
22851  * @extends Roo.bootstrap.Component
22852  * Bootstrap MessageBar class
22853  * @cfg {String} html contents of the MessageBar
22854  * @cfg {String} weight (info | success | warning | danger) default info
22855  * @cfg {String} beforeClass insert the bar before the given class
22856  * @cfg {Boolean} closable (true | false) default false
22857  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22858  * 
22859  * @constructor
22860  * Create a new Element
22861  * @param {Object} config The config object
22862  */
22863
22864 Roo.bootstrap.MessageBar = function(config){
22865     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22866 };
22867
22868 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22869     
22870     html: '',
22871     weight: 'info',
22872     closable: false,
22873     fixed: false,
22874     beforeClass: 'bootstrap-sticky-wrap',
22875     
22876     getAutoCreate : function(){
22877         
22878         var cfg = {
22879             tag: 'div',
22880             cls: 'alert alert-dismissable alert-' + this.weight,
22881             cn: [
22882                 {
22883                     tag: 'span',
22884                     cls: 'message',
22885                     html: this.html || ''
22886                 }
22887             ]
22888         };
22889         
22890         if(this.fixed){
22891             cfg.cls += ' alert-messages-fixed';
22892         }
22893         
22894         if(this.closable){
22895             cfg.cn.push({
22896                 tag: 'button',
22897                 cls: 'close',
22898                 html: 'x'
22899             });
22900         }
22901         
22902         return cfg;
22903     },
22904     
22905     onRender : function(ct, position)
22906     {
22907         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22908         
22909         if(!this.el){
22910             var cfg = Roo.apply({},  this.getAutoCreate());
22911             cfg.id = Roo.id();
22912             
22913             if (this.cls) {
22914                 cfg.cls += ' ' + this.cls;
22915             }
22916             if (this.style) {
22917                 cfg.style = this.style;
22918             }
22919             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22920             
22921             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22922         }
22923         
22924         this.el.select('>button.close').on('click', this.hide, this);
22925         
22926     },
22927     
22928     show : function()
22929     {
22930         if (!this.rendered) {
22931             this.render();
22932         }
22933         
22934         this.el.show();
22935         
22936         this.fireEvent('show', this);
22937         
22938     },
22939     
22940     hide : function()
22941     {
22942         if (!this.rendered) {
22943             this.render();
22944         }
22945         
22946         this.el.hide();
22947         
22948         this.fireEvent('hide', this);
22949     },
22950     
22951     update : function()
22952     {
22953 //        var e = this.el.dom.firstChild;
22954 //        
22955 //        if(this.closable){
22956 //            e = e.nextSibling;
22957 //        }
22958 //        
22959 //        e.data = this.html || '';
22960
22961         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22962     }
22963    
22964 });
22965
22966  
22967
22968      /*
22969  * - LGPL
22970  *
22971  * Graph
22972  * 
22973  */
22974
22975
22976 /**
22977  * @class Roo.bootstrap.Graph
22978  * @extends Roo.bootstrap.Component
22979  * Bootstrap Graph class
22980 > Prameters
22981  -sm {number} sm 4
22982  -md {number} md 5
22983  @cfg {String} graphtype  bar | vbar | pie
22984  @cfg {number} g_x coodinator | centre x (pie)
22985  @cfg {number} g_y coodinator | centre y (pie)
22986  @cfg {number} g_r radius (pie)
22987  @cfg {number} g_height height of the chart (respected by all elements in the set)
22988  @cfg {number} g_width width of the chart (respected by all elements in the set)
22989  @cfg {Object} title The title of the chart
22990     
22991  -{Array}  values
22992  -opts (object) options for the chart 
22993      o {
22994      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22995      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22996      o vgutter (number)
22997      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.
22998      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22999      o to
23000      o stretch (boolean)
23001      o }
23002  -opts (object) options for the pie
23003      o{
23004      o cut
23005      o startAngle (number)
23006      o endAngle (number)
23007      } 
23008  *
23009  * @constructor
23010  * Create a new Input
23011  * @param {Object} config The config object
23012  */
23013
23014 Roo.bootstrap.Graph = function(config){
23015     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23016     
23017     this.addEvents({
23018         // img events
23019         /**
23020          * @event click
23021          * The img click event for the img.
23022          * @param {Roo.EventObject} e
23023          */
23024         "click" : true
23025     });
23026 };
23027
23028 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23029     
23030     sm: 4,
23031     md: 5,
23032     graphtype: 'bar',
23033     g_height: 250,
23034     g_width: 400,
23035     g_x: 50,
23036     g_y: 50,
23037     g_r: 30,
23038     opts:{
23039         //g_colors: this.colors,
23040         g_type: 'soft',
23041         g_gutter: '20%'
23042
23043     },
23044     title : false,
23045
23046     getAutoCreate : function(){
23047         
23048         var cfg = {
23049             tag: 'div',
23050             html : null
23051         };
23052         
23053         
23054         return  cfg;
23055     },
23056
23057     onRender : function(ct,position){
23058         
23059         
23060         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23061         
23062         if (typeof(Raphael) == 'undefined') {
23063             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23064             return;
23065         }
23066         
23067         this.raphael = Raphael(this.el.dom);
23068         
23069                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23070                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23071                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23072                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23073                 /*
23074                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23075                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23076                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23077                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23078                 
23079                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23080                 r.barchart(330, 10, 300, 220, data1);
23081                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23082                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23083                 */
23084                 
23085                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23086                 // r.barchart(30, 30, 560, 250,  xdata, {
23087                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23088                 //     axis : "0 0 1 1",
23089                 //     axisxlabels :  xdata
23090                 //     //yvalues : cols,
23091                    
23092                 // });
23093 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23094 //        
23095 //        this.load(null,xdata,{
23096 //                axis : "0 0 1 1",
23097 //                axisxlabels :  xdata
23098 //                });
23099
23100     },
23101
23102     load : function(graphtype,xdata,opts)
23103     {
23104         this.raphael.clear();
23105         if(!graphtype) {
23106             graphtype = this.graphtype;
23107         }
23108         if(!opts){
23109             opts = this.opts;
23110         }
23111         var r = this.raphael,
23112             fin = function () {
23113                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23114             },
23115             fout = function () {
23116                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23117             },
23118             pfin = function() {
23119                 this.sector.stop();
23120                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23121
23122                 if (this.label) {
23123                     this.label[0].stop();
23124                     this.label[0].attr({ r: 7.5 });
23125                     this.label[1].attr({ "font-weight": 800 });
23126                 }
23127             },
23128             pfout = function() {
23129                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23130
23131                 if (this.label) {
23132                     this.label[0].animate({ r: 5 }, 500, "bounce");
23133                     this.label[1].attr({ "font-weight": 400 });
23134                 }
23135             };
23136
23137         switch(graphtype){
23138             case 'bar':
23139                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23140                 break;
23141             case 'hbar':
23142                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23143                 break;
23144             case 'pie':
23145 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23146 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23147 //            
23148                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23149                 
23150                 break;
23151
23152         }
23153         
23154         if(this.title){
23155             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23156         }
23157         
23158     },
23159     
23160     setTitle: function(o)
23161     {
23162         this.title = o;
23163     },
23164     
23165     initEvents: function() {
23166         
23167         if(!this.href){
23168             this.el.on('click', this.onClick, this);
23169         }
23170     },
23171     
23172     onClick : function(e)
23173     {
23174         Roo.log('img onclick');
23175         this.fireEvent('click', this, e);
23176     }
23177    
23178 });
23179
23180  
23181 /*
23182  * - LGPL
23183  *
23184  * numberBox
23185  * 
23186  */
23187 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23188
23189 /**
23190  * @class Roo.bootstrap.dash.NumberBox
23191  * @extends Roo.bootstrap.Component
23192  * Bootstrap NumberBox class
23193  * @cfg {String} headline Box headline
23194  * @cfg {String} content Box content
23195  * @cfg {String} icon Box icon
23196  * @cfg {String} footer Footer text
23197  * @cfg {String} fhref Footer href
23198  * 
23199  * @constructor
23200  * Create a new NumberBox
23201  * @param {Object} config The config object
23202  */
23203
23204
23205 Roo.bootstrap.dash.NumberBox = function(config){
23206     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23207     
23208 };
23209
23210 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23211     
23212     headline : '',
23213     content : '',
23214     icon : '',
23215     footer : '',
23216     fhref : '',
23217     ficon : '',
23218     
23219     getAutoCreate : function(){
23220         
23221         var cfg = {
23222             tag : 'div',
23223             cls : 'small-box ',
23224             cn : [
23225                 {
23226                     tag : 'div',
23227                     cls : 'inner',
23228                     cn :[
23229                         {
23230                             tag : 'h3',
23231                             cls : 'roo-headline',
23232                             html : this.headline
23233                         },
23234                         {
23235                             tag : 'p',
23236                             cls : 'roo-content',
23237                             html : this.content
23238                         }
23239                     ]
23240                 }
23241             ]
23242         };
23243         
23244         if(this.icon){
23245             cfg.cn.push({
23246                 tag : 'div',
23247                 cls : 'icon',
23248                 cn :[
23249                     {
23250                         tag : 'i',
23251                         cls : 'ion ' + this.icon
23252                     }
23253                 ]
23254             });
23255         }
23256         
23257         if(this.footer){
23258             var footer = {
23259                 tag : 'a',
23260                 cls : 'small-box-footer',
23261                 href : this.fhref || '#',
23262                 html : this.footer
23263             };
23264             
23265             cfg.cn.push(footer);
23266             
23267         }
23268         
23269         return  cfg;
23270     },
23271
23272     onRender : function(ct,position){
23273         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23274
23275
23276        
23277                 
23278     },
23279
23280     setHeadline: function (value)
23281     {
23282         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23283     },
23284     
23285     setFooter: function (value, href)
23286     {
23287         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23288         
23289         if(href){
23290             this.el.select('a.small-box-footer',true).first().attr('href', href);
23291         }
23292         
23293     },
23294
23295     setContent: function (value)
23296     {
23297         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23298     },
23299
23300     initEvents: function() 
23301     {   
23302         
23303     }
23304     
23305 });
23306
23307  
23308 /*
23309  * - LGPL
23310  *
23311  * TabBox
23312  * 
23313  */
23314 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23315
23316 /**
23317  * @class Roo.bootstrap.dash.TabBox
23318  * @extends Roo.bootstrap.Component
23319  * Bootstrap TabBox class
23320  * @cfg {String} title Title of the TabBox
23321  * @cfg {String} icon Icon of the TabBox
23322  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23323  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23324  * 
23325  * @constructor
23326  * Create a new TabBox
23327  * @param {Object} config The config object
23328  */
23329
23330
23331 Roo.bootstrap.dash.TabBox = function(config){
23332     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23333     this.addEvents({
23334         // raw events
23335         /**
23336          * @event addpane
23337          * When a pane is added
23338          * @param {Roo.bootstrap.dash.TabPane} pane
23339          */
23340         "addpane" : true,
23341         /**
23342          * @event activatepane
23343          * When a pane is activated
23344          * @param {Roo.bootstrap.dash.TabPane} pane
23345          */
23346         "activatepane" : true
23347         
23348          
23349     });
23350     
23351     this.panes = [];
23352 };
23353
23354 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23355
23356     title : '',
23357     icon : false,
23358     showtabs : true,
23359     tabScrollable : false,
23360     
23361     getChildContainer : function()
23362     {
23363         return this.el.select('.tab-content', true).first();
23364     },
23365     
23366     getAutoCreate : function(){
23367         
23368         var header = {
23369             tag: 'li',
23370             cls: 'pull-left header',
23371             html: this.title,
23372             cn : []
23373         };
23374         
23375         if(this.icon){
23376             header.cn.push({
23377                 tag: 'i',
23378                 cls: 'fa ' + this.icon
23379             });
23380         }
23381         
23382         var h = {
23383             tag: 'ul',
23384             cls: 'nav nav-tabs pull-right',
23385             cn: [
23386                 header
23387             ]
23388         };
23389         
23390         if(this.tabScrollable){
23391             h = {
23392                 tag: 'div',
23393                 cls: 'tab-header',
23394                 cn: [
23395                     {
23396                         tag: 'ul',
23397                         cls: 'nav nav-tabs pull-right',
23398                         cn: [
23399                             header
23400                         ]
23401                     }
23402                 ]
23403             };
23404         }
23405         
23406         var cfg = {
23407             tag: 'div',
23408             cls: 'nav-tabs-custom',
23409             cn: [
23410                 h,
23411                 {
23412                     tag: 'div',
23413                     cls: 'tab-content no-padding',
23414                     cn: []
23415                 }
23416             ]
23417         };
23418
23419         return  cfg;
23420     },
23421     initEvents : function()
23422     {
23423         //Roo.log('add add pane handler');
23424         this.on('addpane', this.onAddPane, this);
23425     },
23426      /**
23427      * Updates the box title
23428      * @param {String} html to set the title to.
23429      */
23430     setTitle : function(value)
23431     {
23432         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23433     },
23434     onAddPane : function(pane)
23435     {
23436         this.panes.push(pane);
23437         //Roo.log('addpane');
23438         //Roo.log(pane);
23439         // tabs are rendere left to right..
23440         if(!this.showtabs){
23441             return;
23442         }
23443         
23444         var ctr = this.el.select('.nav-tabs', true).first();
23445          
23446          
23447         var existing = ctr.select('.nav-tab',true);
23448         var qty = existing.getCount();;
23449         
23450         
23451         var tab = ctr.createChild({
23452             tag : 'li',
23453             cls : 'nav-tab' + (qty ? '' : ' active'),
23454             cn : [
23455                 {
23456                     tag : 'a',
23457                     href:'#',
23458                     html : pane.title
23459                 }
23460             ]
23461         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23462         pane.tab = tab;
23463         
23464         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23465         if (!qty) {
23466             pane.el.addClass('active');
23467         }
23468         
23469                 
23470     },
23471     onTabClick : function(ev,un,ob,pane)
23472     {
23473         //Roo.log('tab - prev default');
23474         ev.preventDefault();
23475         
23476         
23477         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23478         pane.tab.addClass('active');
23479         //Roo.log(pane.title);
23480         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23481         // technically we should have a deactivate event.. but maybe add later.
23482         // and it should not de-activate the selected tab...
23483         this.fireEvent('activatepane', pane);
23484         pane.el.addClass('active');
23485         pane.fireEvent('activate');
23486         
23487         
23488     },
23489     
23490     getActivePane : function()
23491     {
23492         var r = false;
23493         Roo.each(this.panes, function(p) {
23494             if(p.el.hasClass('active')){
23495                 r = p;
23496                 return false;
23497             }
23498             
23499             return;
23500         });
23501         
23502         return r;
23503     }
23504     
23505     
23506 });
23507
23508  
23509 /*
23510  * - LGPL
23511  *
23512  * Tab pane
23513  * 
23514  */
23515 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23516 /**
23517  * @class Roo.bootstrap.TabPane
23518  * @extends Roo.bootstrap.Component
23519  * Bootstrap TabPane class
23520  * @cfg {Boolean} active (false | true) Default false
23521  * @cfg {String} title title of panel
23522
23523  * 
23524  * @constructor
23525  * Create a new TabPane
23526  * @param {Object} config The config object
23527  */
23528
23529 Roo.bootstrap.dash.TabPane = function(config){
23530     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23531     
23532     this.addEvents({
23533         // raw events
23534         /**
23535          * @event activate
23536          * When a pane is activated
23537          * @param {Roo.bootstrap.dash.TabPane} pane
23538          */
23539         "activate" : true
23540          
23541     });
23542 };
23543
23544 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23545     
23546     active : false,
23547     title : '',
23548     
23549     // the tabBox that this is attached to.
23550     tab : false,
23551      
23552     getAutoCreate : function() 
23553     {
23554         var cfg = {
23555             tag: 'div',
23556             cls: 'tab-pane'
23557         };
23558         
23559         if(this.active){
23560             cfg.cls += ' active';
23561         }
23562         
23563         return cfg;
23564     },
23565     initEvents  : function()
23566     {
23567         //Roo.log('trigger add pane handler');
23568         this.parent().fireEvent('addpane', this)
23569     },
23570     
23571      /**
23572      * Updates the tab title 
23573      * @param {String} html to set the title to.
23574      */
23575     setTitle: function(str)
23576     {
23577         if (!this.tab) {
23578             return;
23579         }
23580         this.title = str;
23581         this.tab.select('a', true).first().dom.innerHTML = str;
23582         
23583     }
23584     
23585     
23586     
23587 });
23588
23589  
23590
23591
23592  /*
23593  * - LGPL
23594  *
23595  * menu
23596  * 
23597  */
23598 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23599
23600 /**
23601  * @class Roo.bootstrap.menu.Menu
23602  * @extends Roo.bootstrap.Component
23603  * Bootstrap Menu class - container for Menu
23604  * @cfg {String} html Text of the menu
23605  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23606  * @cfg {String} icon Font awesome icon
23607  * @cfg {String} pos Menu align to (top | bottom) default bottom
23608  * 
23609  * 
23610  * @constructor
23611  * Create a new Menu
23612  * @param {Object} config The config object
23613  */
23614
23615
23616 Roo.bootstrap.menu.Menu = function(config){
23617     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23618     
23619     this.addEvents({
23620         /**
23621          * @event beforeshow
23622          * Fires before this menu is displayed
23623          * @param {Roo.bootstrap.menu.Menu} this
23624          */
23625         beforeshow : true,
23626         /**
23627          * @event beforehide
23628          * Fires before this menu is hidden
23629          * @param {Roo.bootstrap.menu.Menu} this
23630          */
23631         beforehide : true,
23632         /**
23633          * @event show
23634          * Fires after this menu is displayed
23635          * @param {Roo.bootstrap.menu.Menu} this
23636          */
23637         show : true,
23638         /**
23639          * @event hide
23640          * Fires after this menu is hidden
23641          * @param {Roo.bootstrap.menu.Menu} this
23642          */
23643         hide : true,
23644         /**
23645          * @event click
23646          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23647          * @param {Roo.bootstrap.menu.Menu} this
23648          * @param {Roo.EventObject} e
23649          */
23650         click : true
23651     });
23652     
23653 };
23654
23655 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23656     
23657     submenu : false,
23658     html : '',
23659     weight : 'default',
23660     icon : false,
23661     pos : 'bottom',
23662     
23663     
23664     getChildContainer : function() {
23665         if(this.isSubMenu){
23666             return this.el;
23667         }
23668         
23669         return this.el.select('ul.dropdown-menu', true).first();  
23670     },
23671     
23672     getAutoCreate : function()
23673     {
23674         var text = [
23675             {
23676                 tag : 'span',
23677                 cls : 'roo-menu-text',
23678                 html : this.html
23679             }
23680         ];
23681         
23682         if(this.icon){
23683             text.unshift({
23684                 tag : 'i',
23685                 cls : 'fa ' + this.icon
23686             })
23687         }
23688         
23689         
23690         var cfg = {
23691             tag : 'div',
23692             cls : 'btn-group',
23693             cn : [
23694                 {
23695                     tag : 'button',
23696                     cls : 'dropdown-button btn btn-' + this.weight,
23697                     cn : text
23698                 },
23699                 {
23700                     tag : 'button',
23701                     cls : 'dropdown-toggle btn btn-' + this.weight,
23702                     cn : [
23703                         {
23704                             tag : 'span',
23705                             cls : 'caret'
23706                         }
23707                     ]
23708                 },
23709                 {
23710                     tag : 'ul',
23711                     cls : 'dropdown-menu'
23712                 }
23713             ]
23714             
23715         };
23716         
23717         if(this.pos == 'top'){
23718             cfg.cls += ' dropup';
23719         }
23720         
23721         if(this.isSubMenu){
23722             cfg = {
23723                 tag : 'ul',
23724                 cls : 'dropdown-menu'
23725             }
23726         }
23727         
23728         return cfg;
23729     },
23730     
23731     onRender : function(ct, position)
23732     {
23733         this.isSubMenu = ct.hasClass('dropdown-submenu');
23734         
23735         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23736     },
23737     
23738     initEvents : function() 
23739     {
23740         if(this.isSubMenu){
23741             return;
23742         }
23743         
23744         this.hidden = true;
23745         
23746         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23747         this.triggerEl.on('click', this.onTriggerPress, this);
23748         
23749         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23750         this.buttonEl.on('click', this.onClick, this);
23751         
23752     },
23753     
23754     list : function()
23755     {
23756         if(this.isSubMenu){
23757             return this.el;
23758         }
23759         
23760         return this.el.select('ul.dropdown-menu', true).first();
23761     },
23762     
23763     onClick : function(e)
23764     {
23765         this.fireEvent("click", this, e);
23766     },
23767     
23768     onTriggerPress  : function(e)
23769     {   
23770         if (this.isVisible()) {
23771             this.hide();
23772         } else {
23773             this.show();
23774         }
23775     },
23776     
23777     isVisible : function(){
23778         return !this.hidden;
23779     },
23780     
23781     show : function()
23782     {
23783         this.fireEvent("beforeshow", this);
23784         
23785         this.hidden = false;
23786         this.el.addClass('open');
23787         
23788         Roo.get(document).on("mouseup", this.onMouseUp, this);
23789         
23790         this.fireEvent("show", this);
23791         
23792         
23793     },
23794     
23795     hide : function()
23796     {
23797         this.fireEvent("beforehide", this);
23798         
23799         this.hidden = true;
23800         this.el.removeClass('open');
23801         
23802         Roo.get(document).un("mouseup", this.onMouseUp);
23803         
23804         this.fireEvent("hide", this);
23805     },
23806     
23807     onMouseUp : function()
23808     {
23809         this.hide();
23810     }
23811     
23812 });
23813
23814  
23815  /*
23816  * - LGPL
23817  *
23818  * menu item
23819  * 
23820  */
23821 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23822
23823 /**
23824  * @class Roo.bootstrap.menu.Item
23825  * @extends Roo.bootstrap.Component
23826  * Bootstrap MenuItem class
23827  * @cfg {Boolean} submenu (true | false) default false
23828  * @cfg {String} html text of the item
23829  * @cfg {String} href the link
23830  * @cfg {Boolean} disable (true | false) default false
23831  * @cfg {Boolean} preventDefault (true | false) default true
23832  * @cfg {String} icon Font awesome icon
23833  * @cfg {String} pos Submenu align to (left | right) default right 
23834  * 
23835  * 
23836  * @constructor
23837  * Create a new Item
23838  * @param {Object} config The config object
23839  */
23840
23841
23842 Roo.bootstrap.menu.Item = function(config){
23843     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23844     this.addEvents({
23845         /**
23846          * @event mouseover
23847          * Fires when the mouse is hovering over this menu
23848          * @param {Roo.bootstrap.menu.Item} this
23849          * @param {Roo.EventObject} e
23850          */
23851         mouseover : true,
23852         /**
23853          * @event mouseout
23854          * Fires when the mouse exits this menu
23855          * @param {Roo.bootstrap.menu.Item} this
23856          * @param {Roo.EventObject} e
23857          */
23858         mouseout : true,
23859         // raw events
23860         /**
23861          * @event click
23862          * The raw click event for the entire grid.
23863          * @param {Roo.EventObject} e
23864          */
23865         click : true
23866     });
23867 };
23868
23869 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23870     
23871     submenu : false,
23872     href : '',
23873     html : '',
23874     preventDefault: true,
23875     disable : false,
23876     icon : false,
23877     pos : 'right',
23878     
23879     getAutoCreate : function()
23880     {
23881         var text = [
23882             {
23883                 tag : 'span',
23884                 cls : 'roo-menu-item-text',
23885                 html : this.html
23886             }
23887         ];
23888         
23889         if(this.icon){
23890             text.unshift({
23891                 tag : 'i',
23892                 cls : 'fa ' + this.icon
23893             })
23894         }
23895         
23896         var cfg = {
23897             tag : 'li',
23898             cn : [
23899                 {
23900                     tag : 'a',
23901                     href : this.href || '#',
23902                     cn : text
23903                 }
23904             ]
23905         };
23906         
23907         if(this.disable){
23908             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23909         }
23910         
23911         if(this.submenu){
23912             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23913             
23914             if(this.pos == 'left'){
23915                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23916             }
23917         }
23918         
23919         return cfg;
23920     },
23921     
23922     initEvents : function() 
23923     {
23924         this.el.on('mouseover', this.onMouseOver, this);
23925         this.el.on('mouseout', this.onMouseOut, this);
23926         
23927         this.el.select('a', true).first().on('click', this.onClick, this);
23928         
23929     },
23930     
23931     onClick : function(e)
23932     {
23933         if(this.preventDefault){
23934             e.preventDefault();
23935         }
23936         
23937         this.fireEvent("click", this, e);
23938     },
23939     
23940     onMouseOver : function(e)
23941     {
23942         if(this.submenu && this.pos == 'left'){
23943             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23944         }
23945         
23946         this.fireEvent("mouseover", this, e);
23947     },
23948     
23949     onMouseOut : function(e)
23950     {
23951         this.fireEvent("mouseout", this, e);
23952     }
23953 });
23954
23955  
23956
23957  /*
23958  * - LGPL
23959  *
23960  * menu separator
23961  * 
23962  */
23963 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23964
23965 /**
23966  * @class Roo.bootstrap.menu.Separator
23967  * @extends Roo.bootstrap.Component
23968  * Bootstrap Separator class
23969  * 
23970  * @constructor
23971  * Create a new Separator
23972  * @param {Object} config The config object
23973  */
23974
23975
23976 Roo.bootstrap.menu.Separator = function(config){
23977     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23978 };
23979
23980 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23981     
23982     getAutoCreate : function(){
23983         var cfg = {
23984             tag : 'li',
23985             cls: 'divider'
23986         };
23987         
23988         return cfg;
23989     }
23990    
23991 });
23992
23993  
23994
23995  /*
23996  * - LGPL
23997  *
23998  * Tooltip
23999  * 
24000  */
24001
24002 /**
24003  * @class Roo.bootstrap.Tooltip
24004  * Bootstrap Tooltip class
24005  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24006  * to determine which dom element triggers the tooltip.
24007  * 
24008  * It needs to add support for additional attributes like tooltip-position
24009  * 
24010  * @constructor
24011  * Create a new Toolti
24012  * @param {Object} config The config object
24013  */
24014
24015 Roo.bootstrap.Tooltip = function(config){
24016     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24017 };
24018
24019 Roo.apply(Roo.bootstrap.Tooltip, {
24020     /**
24021      * @function init initialize tooltip monitoring.
24022      * @static
24023      */
24024     currentEl : false,
24025     currentTip : false,
24026     currentRegion : false,
24027     
24028     //  init : delay?
24029     
24030     init : function()
24031     {
24032         Roo.get(document).on('mouseover', this.enter ,this);
24033         Roo.get(document).on('mouseout', this.leave, this);
24034          
24035         
24036         this.currentTip = new Roo.bootstrap.Tooltip();
24037     },
24038     
24039     enter : function(ev)
24040     {
24041         var dom = ev.getTarget();
24042         
24043         //Roo.log(['enter',dom]);
24044         var el = Roo.fly(dom);
24045         if (this.currentEl) {
24046             //Roo.log(dom);
24047             //Roo.log(this.currentEl);
24048             //Roo.log(this.currentEl.contains(dom));
24049             if (this.currentEl == el) {
24050                 return;
24051             }
24052             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24053                 return;
24054             }
24055
24056         }
24057         
24058         if (this.currentTip.el) {
24059             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24060         }    
24061         //Roo.log(ev);
24062         var bindEl = el;
24063         
24064         // you can not look for children, as if el is the body.. then everythign is the child..
24065         if (!el.attr('tooltip')) { //
24066             if (!el.select("[tooltip]").elements.length) {
24067                 return;
24068             }
24069             // is the mouse over this child...?
24070             bindEl = el.select("[tooltip]").first();
24071             var xy = ev.getXY();
24072             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24073                 //Roo.log("not in region.");
24074                 return;
24075             }
24076             //Roo.log("child element over..");
24077             
24078         }
24079         this.currentEl = bindEl;
24080         this.currentTip.bind(bindEl);
24081         this.currentRegion = Roo.lib.Region.getRegion(dom);
24082         this.currentTip.enter();
24083         
24084     },
24085     leave : function(ev)
24086     {
24087         var dom = ev.getTarget();
24088         //Roo.log(['leave',dom]);
24089         if (!this.currentEl) {
24090             return;
24091         }
24092         
24093         
24094         if (dom != this.currentEl.dom) {
24095             return;
24096         }
24097         var xy = ev.getXY();
24098         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24099             return;
24100         }
24101         // only activate leave if mouse cursor is outside... bounding box..
24102         
24103         
24104         
24105         
24106         if (this.currentTip) {
24107             this.currentTip.leave();
24108         }
24109         //Roo.log('clear currentEl');
24110         this.currentEl = false;
24111         
24112         
24113     },
24114     alignment : {
24115         'left' : ['r-l', [-2,0], 'right'],
24116         'right' : ['l-r', [2,0], 'left'],
24117         'bottom' : ['t-b', [0,2], 'top'],
24118         'top' : [ 'b-t', [0,-2], 'bottom']
24119     }
24120     
24121 });
24122
24123
24124 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24125     
24126     
24127     bindEl : false,
24128     
24129     delay : null, // can be { show : 300 , hide: 500}
24130     
24131     timeout : null,
24132     
24133     hoverState : null, //???
24134     
24135     placement : 'bottom', 
24136     
24137     getAutoCreate : function(){
24138     
24139         var cfg = {
24140            cls : 'tooltip',
24141            role : 'tooltip',
24142            cn : [
24143                 {
24144                     cls : 'tooltip-arrow'
24145                 },
24146                 {
24147                     cls : 'tooltip-inner'
24148                 }
24149            ]
24150         };
24151         
24152         return cfg;
24153     },
24154     bind : function(el)
24155     {
24156         this.bindEl = el;
24157     },
24158       
24159     
24160     enter : function () {
24161        
24162         if (this.timeout != null) {
24163             clearTimeout(this.timeout);
24164         }
24165         
24166         this.hoverState = 'in';
24167          //Roo.log("enter - show");
24168         if (!this.delay || !this.delay.show) {
24169             this.show();
24170             return;
24171         }
24172         var _t = this;
24173         this.timeout = setTimeout(function () {
24174             if (_t.hoverState == 'in') {
24175                 _t.show();
24176             }
24177         }, this.delay.show);
24178     },
24179     leave : function()
24180     {
24181         clearTimeout(this.timeout);
24182     
24183         this.hoverState = 'out';
24184          if (!this.delay || !this.delay.hide) {
24185             this.hide();
24186             return;
24187         }
24188        
24189         var _t = this;
24190         this.timeout = setTimeout(function () {
24191             //Roo.log("leave - timeout");
24192             
24193             if (_t.hoverState == 'out') {
24194                 _t.hide();
24195                 Roo.bootstrap.Tooltip.currentEl = false;
24196             }
24197         }, delay);
24198     },
24199     
24200     show : function ()
24201     {
24202         if (!this.el) {
24203             this.render(document.body);
24204         }
24205         // set content.
24206         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24207         
24208         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24209         
24210         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24211         
24212         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24213         
24214         var placement = typeof this.placement == 'function' ?
24215             this.placement.call(this, this.el, on_el) :
24216             this.placement;
24217             
24218         var autoToken = /\s?auto?\s?/i;
24219         var autoPlace = autoToken.test(placement);
24220         if (autoPlace) {
24221             placement = placement.replace(autoToken, '') || 'top';
24222         }
24223         
24224         //this.el.detach()
24225         //this.el.setXY([0,0]);
24226         this.el.show();
24227         //this.el.dom.style.display='block';
24228         
24229         //this.el.appendTo(on_el);
24230         
24231         var p = this.getPosition();
24232         var box = this.el.getBox();
24233         
24234         if (autoPlace) {
24235             // fixme..
24236         }
24237         
24238         var align = Roo.bootstrap.Tooltip.alignment[placement];
24239         
24240         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24241         
24242         if(placement == 'top' || placement == 'bottom'){
24243             if(xy[0] < 0){
24244                 placement = 'right';
24245             }
24246             
24247             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24248                 placement = 'left';
24249             }
24250         }
24251         
24252         align = Roo.bootstrap.Tooltip.alignment[placement];
24253         
24254         this.el.alignTo(this.bindEl, align[0],align[1]);
24255         //var arrow = this.el.select('.arrow',true).first();
24256         //arrow.set(align[2], 
24257         
24258         this.el.addClass(placement);
24259         
24260         this.el.addClass('in fade');
24261         
24262         this.hoverState = null;
24263         
24264         if (this.el.hasClass('fade')) {
24265             // fade it?
24266         }
24267         
24268     },
24269     hide : function()
24270     {
24271          
24272         if (!this.el) {
24273             return;
24274         }
24275         //this.el.setXY([0,0]);
24276         this.el.removeClass('in');
24277         //this.el.hide();
24278         
24279     }
24280     
24281 });
24282  
24283
24284  /*
24285  * - LGPL
24286  *
24287  * Location Picker
24288  * 
24289  */
24290
24291 /**
24292  * @class Roo.bootstrap.LocationPicker
24293  * @extends Roo.bootstrap.Component
24294  * Bootstrap LocationPicker class
24295  * @cfg {Number} latitude Position when init default 0
24296  * @cfg {Number} longitude Position when init default 0
24297  * @cfg {Number} zoom default 15
24298  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24299  * @cfg {Boolean} mapTypeControl default false
24300  * @cfg {Boolean} disableDoubleClickZoom default false
24301  * @cfg {Boolean} scrollwheel default true
24302  * @cfg {Boolean} streetViewControl default false
24303  * @cfg {Number} radius default 0
24304  * @cfg {String} locationName
24305  * @cfg {Boolean} draggable default true
24306  * @cfg {Boolean} enableAutocomplete default false
24307  * @cfg {Boolean} enableReverseGeocode default true
24308  * @cfg {String} markerTitle
24309  * 
24310  * @constructor
24311  * Create a new LocationPicker
24312  * @param {Object} config The config object
24313  */
24314
24315
24316 Roo.bootstrap.LocationPicker = function(config){
24317     
24318     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24319     
24320     this.addEvents({
24321         /**
24322          * @event initial
24323          * Fires when the picker initialized.
24324          * @param {Roo.bootstrap.LocationPicker} this
24325          * @param {Google Location} location
24326          */
24327         initial : true,
24328         /**
24329          * @event positionchanged
24330          * Fires when the picker position changed.
24331          * @param {Roo.bootstrap.LocationPicker} this
24332          * @param {Google Location} location
24333          */
24334         positionchanged : true,
24335         /**
24336          * @event resize
24337          * Fires when the map resize.
24338          * @param {Roo.bootstrap.LocationPicker} this
24339          */
24340         resize : true,
24341         /**
24342          * @event show
24343          * Fires when the map show.
24344          * @param {Roo.bootstrap.LocationPicker} this
24345          */
24346         show : true,
24347         /**
24348          * @event hide
24349          * Fires when the map hide.
24350          * @param {Roo.bootstrap.LocationPicker} this
24351          */
24352         hide : true,
24353         /**
24354          * @event mapClick
24355          * Fires when click the map.
24356          * @param {Roo.bootstrap.LocationPicker} this
24357          * @param {Map event} e
24358          */
24359         mapClick : true,
24360         /**
24361          * @event mapRightClick
24362          * Fires when right click the map.
24363          * @param {Roo.bootstrap.LocationPicker} this
24364          * @param {Map event} e
24365          */
24366         mapRightClick : true,
24367         /**
24368          * @event markerClick
24369          * Fires when click the marker.
24370          * @param {Roo.bootstrap.LocationPicker} this
24371          * @param {Map event} e
24372          */
24373         markerClick : true,
24374         /**
24375          * @event markerRightClick
24376          * Fires when right click the marker.
24377          * @param {Roo.bootstrap.LocationPicker} this
24378          * @param {Map event} e
24379          */
24380         markerRightClick : true,
24381         /**
24382          * @event OverlayViewDraw
24383          * Fires when OverlayView Draw
24384          * @param {Roo.bootstrap.LocationPicker} this
24385          */
24386         OverlayViewDraw : true,
24387         /**
24388          * @event OverlayViewOnAdd
24389          * Fires when OverlayView Draw
24390          * @param {Roo.bootstrap.LocationPicker} this
24391          */
24392         OverlayViewOnAdd : true,
24393         /**
24394          * @event OverlayViewOnRemove
24395          * Fires when OverlayView Draw
24396          * @param {Roo.bootstrap.LocationPicker} this
24397          */
24398         OverlayViewOnRemove : true,
24399         /**
24400          * @event OverlayViewShow
24401          * Fires when OverlayView Draw
24402          * @param {Roo.bootstrap.LocationPicker} this
24403          * @param {Pixel} cpx
24404          */
24405         OverlayViewShow : true,
24406         /**
24407          * @event OverlayViewHide
24408          * Fires when OverlayView Draw
24409          * @param {Roo.bootstrap.LocationPicker} this
24410          */
24411         OverlayViewHide : true,
24412         /**
24413          * @event loadexception
24414          * Fires when load google lib failed.
24415          * @param {Roo.bootstrap.LocationPicker} this
24416          */
24417         loadexception : true
24418     });
24419         
24420 };
24421
24422 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24423     
24424     gMapContext: false,
24425     
24426     latitude: 0,
24427     longitude: 0,
24428     zoom: 15,
24429     mapTypeId: false,
24430     mapTypeControl: false,
24431     disableDoubleClickZoom: false,
24432     scrollwheel: true,
24433     streetViewControl: false,
24434     radius: 0,
24435     locationName: '',
24436     draggable: true,
24437     enableAutocomplete: false,
24438     enableReverseGeocode: true,
24439     markerTitle: '',
24440     
24441     getAutoCreate: function()
24442     {
24443
24444         var cfg = {
24445             tag: 'div',
24446             cls: 'roo-location-picker'
24447         };
24448         
24449         return cfg
24450     },
24451     
24452     initEvents: function(ct, position)
24453     {       
24454         if(!this.el.getWidth() || this.isApplied()){
24455             return;
24456         }
24457         
24458         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24459         
24460         this.initial();
24461     },
24462     
24463     initial: function()
24464     {
24465         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24466             this.fireEvent('loadexception', this);
24467             return;
24468         }
24469         
24470         if(!this.mapTypeId){
24471             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24472         }
24473         
24474         this.gMapContext = this.GMapContext();
24475         
24476         this.initOverlayView();
24477         
24478         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24479         
24480         var _this = this;
24481                 
24482         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24483             _this.setPosition(_this.gMapContext.marker.position);
24484         });
24485         
24486         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24487             _this.fireEvent('mapClick', this, event);
24488             
24489         });
24490
24491         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24492             _this.fireEvent('mapRightClick', this, event);
24493             
24494         });
24495         
24496         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24497             _this.fireEvent('markerClick', this, event);
24498             
24499         });
24500
24501         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24502             _this.fireEvent('markerRightClick', this, event);
24503             
24504         });
24505         
24506         this.setPosition(this.gMapContext.location);
24507         
24508         this.fireEvent('initial', this, this.gMapContext.location);
24509     },
24510     
24511     initOverlayView: function()
24512     {
24513         var _this = this;
24514         
24515         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24516             
24517             draw: function()
24518             {
24519                 _this.fireEvent('OverlayViewDraw', _this);
24520             },
24521             
24522             onAdd: function()
24523             {
24524                 _this.fireEvent('OverlayViewOnAdd', _this);
24525             },
24526             
24527             onRemove: function()
24528             {
24529                 _this.fireEvent('OverlayViewOnRemove', _this);
24530             },
24531             
24532             show: function(cpx)
24533             {
24534                 _this.fireEvent('OverlayViewShow', _this, cpx);
24535             },
24536             
24537             hide: function()
24538             {
24539                 _this.fireEvent('OverlayViewHide', _this);
24540             }
24541             
24542         });
24543     },
24544     
24545     fromLatLngToContainerPixel: function(event)
24546     {
24547         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24548     },
24549     
24550     isApplied: function() 
24551     {
24552         return this.getGmapContext() == false ? false : true;
24553     },
24554     
24555     getGmapContext: function() 
24556     {
24557         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24558     },
24559     
24560     GMapContext: function() 
24561     {
24562         var position = new google.maps.LatLng(this.latitude, this.longitude);
24563         
24564         var _map = new google.maps.Map(this.el.dom, {
24565             center: position,
24566             zoom: this.zoom,
24567             mapTypeId: this.mapTypeId,
24568             mapTypeControl: this.mapTypeControl,
24569             disableDoubleClickZoom: this.disableDoubleClickZoom,
24570             scrollwheel: this.scrollwheel,
24571             streetViewControl: this.streetViewControl,
24572             locationName: this.locationName,
24573             draggable: this.draggable,
24574             enableAutocomplete: this.enableAutocomplete,
24575             enableReverseGeocode: this.enableReverseGeocode
24576         });
24577         
24578         var _marker = new google.maps.Marker({
24579             position: position,
24580             map: _map,
24581             title: this.markerTitle,
24582             draggable: this.draggable
24583         });
24584         
24585         return {
24586             map: _map,
24587             marker: _marker,
24588             circle: null,
24589             location: position,
24590             radius: this.radius,
24591             locationName: this.locationName,
24592             addressComponents: {
24593                 formatted_address: null,
24594                 addressLine1: null,
24595                 addressLine2: null,
24596                 streetName: null,
24597                 streetNumber: null,
24598                 city: null,
24599                 district: null,
24600                 state: null,
24601                 stateOrProvince: null
24602             },
24603             settings: this,
24604             domContainer: this.el.dom,
24605             geodecoder: new google.maps.Geocoder()
24606         };
24607     },
24608     
24609     drawCircle: function(center, radius, options) 
24610     {
24611         if (this.gMapContext.circle != null) {
24612             this.gMapContext.circle.setMap(null);
24613         }
24614         if (radius > 0) {
24615             radius *= 1;
24616             options = Roo.apply({}, options, {
24617                 strokeColor: "#0000FF",
24618                 strokeOpacity: .35,
24619                 strokeWeight: 2,
24620                 fillColor: "#0000FF",
24621                 fillOpacity: .2
24622             });
24623             
24624             options.map = this.gMapContext.map;
24625             options.radius = radius;
24626             options.center = center;
24627             this.gMapContext.circle = new google.maps.Circle(options);
24628             return this.gMapContext.circle;
24629         }
24630         
24631         return null;
24632     },
24633     
24634     setPosition: function(location) 
24635     {
24636         this.gMapContext.location = location;
24637         this.gMapContext.marker.setPosition(location);
24638         this.gMapContext.map.panTo(location);
24639         this.drawCircle(location, this.gMapContext.radius, {});
24640         
24641         var _this = this;
24642         
24643         if (this.gMapContext.settings.enableReverseGeocode) {
24644             this.gMapContext.geodecoder.geocode({
24645                 latLng: this.gMapContext.location
24646             }, function(results, status) {
24647                 
24648                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24649                     _this.gMapContext.locationName = results[0].formatted_address;
24650                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24651                     
24652                     _this.fireEvent('positionchanged', this, location);
24653                 }
24654             });
24655             
24656             return;
24657         }
24658         
24659         this.fireEvent('positionchanged', this, location);
24660     },
24661     
24662     resize: function()
24663     {
24664         google.maps.event.trigger(this.gMapContext.map, "resize");
24665         
24666         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24667         
24668         this.fireEvent('resize', this);
24669     },
24670     
24671     setPositionByLatLng: function(latitude, longitude)
24672     {
24673         this.setPosition(new google.maps.LatLng(latitude, longitude));
24674     },
24675     
24676     getCurrentPosition: function() 
24677     {
24678         return {
24679             latitude: this.gMapContext.location.lat(),
24680             longitude: this.gMapContext.location.lng()
24681         };
24682     },
24683     
24684     getAddressName: function() 
24685     {
24686         return this.gMapContext.locationName;
24687     },
24688     
24689     getAddressComponents: function() 
24690     {
24691         return this.gMapContext.addressComponents;
24692     },
24693     
24694     address_component_from_google_geocode: function(address_components) 
24695     {
24696         var result = {};
24697         
24698         for (var i = 0; i < address_components.length; i++) {
24699             var component = address_components[i];
24700             if (component.types.indexOf("postal_code") >= 0) {
24701                 result.postalCode = component.short_name;
24702             } else if (component.types.indexOf("street_number") >= 0) {
24703                 result.streetNumber = component.short_name;
24704             } else if (component.types.indexOf("route") >= 0) {
24705                 result.streetName = component.short_name;
24706             } else if (component.types.indexOf("neighborhood") >= 0) {
24707                 result.city = component.short_name;
24708             } else if (component.types.indexOf("locality") >= 0) {
24709                 result.city = component.short_name;
24710             } else if (component.types.indexOf("sublocality") >= 0) {
24711                 result.district = component.short_name;
24712             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24713                 result.stateOrProvince = component.short_name;
24714             } else if (component.types.indexOf("country") >= 0) {
24715                 result.country = component.short_name;
24716             }
24717         }
24718         
24719         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24720         result.addressLine2 = "";
24721         return result;
24722     },
24723     
24724     setZoomLevel: function(zoom)
24725     {
24726         this.gMapContext.map.setZoom(zoom);
24727     },
24728     
24729     show: function()
24730     {
24731         if(!this.el){
24732             return;
24733         }
24734         
24735         this.el.show();
24736         
24737         this.resize();
24738         
24739         this.fireEvent('show', this);
24740     },
24741     
24742     hide: function()
24743     {
24744         if(!this.el){
24745             return;
24746         }
24747         
24748         this.el.hide();
24749         
24750         this.fireEvent('hide', this);
24751     }
24752     
24753 });
24754
24755 Roo.apply(Roo.bootstrap.LocationPicker, {
24756     
24757     OverlayView : function(map, options)
24758     {
24759         options = options || {};
24760         
24761         this.setMap(map);
24762     }
24763     
24764     
24765 });/*
24766  * - LGPL
24767  *
24768  * Alert
24769  * 
24770  */
24771
24772 /**
24773  * @class Roo.bootstrap.Alert
24774  * @extends Roo.bootstrap.Component
24775  * Bootstrap Alert class
24776  * @cfg {String} title The title of alert
24777  * @cfg {String} html The content of alert
24778  * @cfg {String} weight (  success | info | warning | danger )
24779  * @cfg {String} faicon font-awesomeicon
24780  * 
24781  * @constructor
24782  * Create a new alert
24783  * @param {Object} config The config object
24784  */
24785
24786
24787 Roo.bootstrap.Alert = function(config){
24788     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24789     
24790 };
24791
24792 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24793     
24794     title: '',
24795     html: '',
24796     weight: false,
24797     faicon: false,
24798     
24799     getAutoCreate : function()
24800     {
24801         
24802         var cfg = {
24803             tag : 'div',
24804             cls : 'alert',
24805             cn : [
24806                 {
24807                     tag : 'i',
24808                     cls : 'roo-alert-icon'
24809                     
24810                 },
24811                 {
24812                     tag : 'b',
24813                     cls : 'roo-alert-title',
24814                     html : this.title
24815                 },
24816                 {
24817                     tag : 'span',
24818                     cls : 'roo-alert-text',
24819                     html : this.html
24820                 }
24821             ]
24822         };
24823         
24824         if(this.faicon){
24825             cfg.cn[0].cls += ' fa ' + this.faicon;
24826         }
24827         
24828         if(this.weight){
24829             cfg.cls += ' alert-' + this.weight;
24830         }
24831         
24832         return cfg;
24833     },
24834     
24835     initEvents: function() 
24836     {
24837         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24838     },
24839     
24840     setTitle : function(str)
24841     {
24842         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24843     },
24844     
24845     setText : function(str)
24846     {
24847         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24848     },
24849     
24850     setWeight : function(weight)
24851     {
24852         if(this.weight){
24853             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24854         }
24855         
24856         this.weight = weight;
24857         
24858         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24859     },
24860     
24861     setIcon : function(icon)
24862     {
24863         if(this.faicon){
24864             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24865         }
24866         
24867         this.faicon = icon;
24868         
24869         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24870     },
24871     
24872     hide: function() 
24873     {
24874         this.el.hide();   
24875     },
24876     
24877     show: function() 
24878     {  
24879         this.el.show();   
24880     }
24881     
24882 });
24883
24884  
24885 /*
24886 * Licence: LGPL
24887 */
24888
24889 /**
24890  * @class Roo.bootstrap.UploadCropbox
24891  * @extends Roo.bootstrap.Component
24892  * Bootstrap UploadCropbox class
24893  * @cfg {String} emptyText show when image has been loaded
24894  * @cfg {String} rotateNotify show when image too small to rotate
24895  * @cfg {Number} errorTimeout default 3000
24896  * @cfg {Number} minWidth default 300
24897  * @cfg {Number} minHeight default 300
24898  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24899  * @cfg {Boolean} isDocument (true|false) default false
24900  * @cfg {String} url action url
24901  * @cfg {String} paramName default 'imageUpload'
24902  * @cfg {String} method default POST
24903  * @cfg {Boolean} loadMask (true|false) default true
24904  * @cfg {Boolean} loadingText default 'Loading...'
24905  * 
24906  * @constructor
24907  * Create a new UploadCropbox
24908  * @param {Object} config The config object
24909  */
24910
24911 Roo.bootstrap.UploadCropbox = function(config){
24912     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24913     
24914     this.addEvents({
24915         /**
24916          * @event beforeselectfile
24917          * Fire before select file
24918          * @param {Roo.bootstrap.UploadCropbox} this
24919          */
24920         "beforeselectfile" : true,
24921         /**
24922          * @event initial
24923          * Fire after initEvent
24924          * @param {Roo.bootstrap.UploadCropbox} this
24925          */
24926         "initial" : true,
24927         /**
24928          * @event crop
24929          * Fire after initEvent
24930          * @param {Roo.bootstrap.UploadCropbox} this
24931          * @param {String} data
24932          */
24933         "crop" : true,
24934         /**
24935          * @event prepare
24936          * Fire when preparing the file data
24937          * @param {Roo.bootstrap.UploadCropbox} this
24938          * @param {Object} file
24939          */
24940         "prepare" : true,
24941         /**
24942          * @event exception
24943          * Fire when get exception
24944          * @param {Roo.bootstrap.UploadCropbox} this
24945          * @param {XMLHttpRequest} xhr
24946          */
24947         "exception" : true,
24948         /**
24949          * @event beforeloadcanvas
24950          * Fire before load the canvas
24951          * @param {Roo.bootstrap.UploadCropbox} this
24952          * @param {String} src
24953          */
24954         "beforeloadcanvas" : true,
24955         /**
24956          * @event trash
24957          * Fire when trash image
24958          * @param {Roo.bootstrap.UploadCropbox} this
24959          */
24960         "trash" : true,
24961         /**
24962          * @event download
24963          * Fire when download the image
24964          * @param {Roo.bootstrap.UploadCropbox} this
24965          */
24966         "download" : true,
24967         /**
24968          * @event footerbuttonclick
24969          * Fire when footerbuttonclick
24970          * @param {Roo.bootstrap.UploadCropbox} this
24971          * @param {String} type
24972          */
24973         "footerbuttonclick" : true,
24974         /**
24975          * @event resize
24976          * Fire when resize
24977          * @param {Roo.bootstrap.UploadCropbox} this
24978          */
24979         "resize" : true,
24980         /**
24981          * @event rotate
24982          * Fire when rotate the image
24983          * @param {Roo.bootstrap.UploadCropbox} this
24984          * @param {String} pos
24985          */
24986         "rotate" : true,
24987         /**
24988          * @event inspect
24989          * Fire when inspect the file
24990          * @param {Roo.bootstrap.UploadCropbox} this
24991          * @param {Object} file
24992          */
24993         "inspect" : true,
24994         /**
24995          * @event upload
24996          * Fire when xhr upload the file
24997          * @param {Roo.bootstrap.UploadCropbox} this
24998          * @param {Object} data
24999          */
25000         "upload" : true,
25001         /**
25002          * @event arrange
25003          * Fire when arrange the file data
25004          * @param {Roo.bootstrap.UploadCropbox} this
25005          * @param {Object} formData
25006          */
25007         "arrange" : true
25008     });
25009     
25010     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25011 };
25012
25013 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25014     
25015     emptyText : 'Click to upload image',
25016     rotateNotify : 'Image is too small to rotate',
25017     errorTimeout : 3000,
25018     scale : 0,
25019     baseScale : 1,
25020     rotate : 0,
25021     dragable : false,
25022     pinching : false,
25023     mouseX : 0,
25024     mouseY : 0,
25025     cropData : false,
25026     minWidth : 300,
25027     minHeight : 300,
25028     file : false,
25029     exif : {},
25030     baseRotate : 1,
25031     cropType : 'image/jpeg',
25032     buttons : false,
25033     canvasLoaded : false,
25034     isDocument : false,
25035     method : 'POST',
25036     paramName : 'imageUpload',
25037     loadMask : true,
25038     loadingText : 'Loading...',
25039     maskEl : false,
25040     
25041     getAutoCreate : function()
25042     {
25043         var cfg = {
25044             tag : 'div',
25045             cls : 'roo-upload-cropbox',
25046             cn : [
25047                 {
25048                     tag : 'input',
25049                     cls : 'roo-upload-cropbox-selector',
25050                     type : 'file'
25051                 },
25052                 {
25053                     tag : 'div',
25054                     cls : 'roo-upload-cropbox-body',
25055                     style : 'cursor:pointer',
25056                     cn : [
25057                         {
25058                             tag : 'div',
25059                             cls : 'roo-upload-cropbox-preview'
25060                         },
25061                         {
25062                             tag : 'div',
25063                             cls : 'roo-upload-cropbox-thumb'
25064                         },
25065                         {
25066                             tag : 'div',
25067                             cls : 'roo-upload-cropbox-empty-notify',
25068                             html : this.emptyText
25069                         },
25070                         {
25071                             tag : 'div',
25072                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25073                             html : this.rotateNotify
25074                         }
25075                     ]
25076                 },
25077                 {
25078                     tag : 'div',
25079                     cls : 'roo-upload-cropbox-footer',
25080                     cn : {
25081                         tag : 'div',
25082                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25083                         cn : []
25084                     }
25085                 }
25086             ]
25087         };
25088         
25089         return cfg;
25090     },
25091     
25092     onRender : function(ct, position)
25093     {
25094         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25095         
25096         if (this.buttons.length) {
25097             
25098             Roo.each(this.buttons, function(bb) {
25099                 
25100                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25101                 
25102                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25103                 
25104             }, this);
25105         }
25106         
25107         if(this.loadMask){
25108             this.maskEl = this.el;
25109         }
25110     },
25111     
25112     initEvents : function()
25113     {
25114         this.urlAPI = (window.createObjectURL && window) || 
25115                                 (window.URL && URL.revokeObjectURL && URL) || 
25116                                 (window.webkitURL && webkitURL);
25117                         
25118         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25119         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25120         
25121         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25122         this.selectorEl.hide();
25123         
25124         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25125         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25126         
25127         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25128         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25129         this.thumbEl.hide();
25130         
25131         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25132         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25133         
25134         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25135         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25136         this.errorEl.hide();
25137         
25138         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25139         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25140         this.footerEl.hide();
25141         
25142         this.setThumbBoxSize();
25143         
25144         this.bind();
25145         
25146         this.resize();
25147         
25148         this.fireEvent('initial', this);
25149     },
25150
25151     bind : function()
25152     {
25153         var _this = this;
25154         
25155         window.addEventListener("resize", function() { _this.resize(); } );
25156         
25157         this.bodyEl.on('click', this.beforeSelectFile, this);
25158         
25159         if(Roo.isTouch){
25160             this.bodyEl.on('touchstart', this.onTouchStart, this);
25161             this.bodyEl.on('touchmove', this.onTouchMove, this);
25162             this.bodyEl.on('touchend', this.onTouchEnd, this);
25163         }
25164         
25165         if(!Roo.isTouch){
25166             this.bodyEl.on('mousedown', this.onMouseDown, this);
25167             this.bodyEl.on('mousemove', this.onMouseMove, this);
25168             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25169             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25170             Roo.get(document).on('mouseup', this.onMouseUp, this);
25171         }
25172         
25173         this.selectorEl.on('change', this.onFileSelected, this);
25174     },
25175     
25176     reset : function()
25177     {    
25178         this.scale = 0;
25179         this.baseScale = 1;
25180         this.rotate = 0;
25181         this.baseRotate = 1;
25182         this.dragable = false;
25183         this.pinching = false;
25184         this.mouseX = 0;
25185         this.mouseY = 0;
25186         this.cropData = false;
25187         this.notifyEl.dom.innerHTML = this.emptyText;
25188         
25189         this.selectorEl.dom.value = '';
25190         
25191     },
25192     
25193     resize : function()
25194     {
25195         if(this.fireEvent('resize', this) != false){
25196             this.setThumbBoxPosition();
25197             this.setCanvasPosition();
25198         }
25199     },
25200     
25201     onFooterButtonClick : function(e, el, o, type)
25202     {
25203         switch (type) {
25204             case 'rotate-left' :
25205                 this.onRotateLeft(e);
25206                 break;
25207             case 'rotate-right' :
25208                 this.onRotateRight(e);
25209                 break;
25210             case 'picture' :
25211                 this.beforeSelectFile(e);
25212                 break;
25213             case 'trash' :
25214                 this.trash(e);
25215                 break;
25216             case 'crop' :
25217                 this.crop(e);
25218                 break;
25219             case 'download' :
25220                 this.download(e);
25221                 break;
25222             default :
25223                 break;
25224         }
25225         
25226         this.fireEvent('footerbuttonclick', this, type);
25227     },
25228     
25229     beforeSelectFile : function(e)
25230     {
25231         e.preventDefault();
25232         
25233         if(this.fireEvent('beforeselectfile', this) != false){
25234             this.selectorEl.dom.click();
25235         }
25236     },
25237     
25238     onFileSelected : function(e)
25239     {
25240         e.preventDefault();
25241         
25242         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25243             return;
25244         }
25245         
25246         var file = this.selectorEl.dom.files[0];
25247         
25248         if(this.fireEvent('inspect', this, file) != false){
25249             this.prepare(file);
25250         }
25251         
25252     },
25253     
25254     trash : function(e)
25255     {
25256         this.fireEvent('trash', this);
25257     },
25258     
25259     download : function(e)
25260     {
25261         this.fireEvent('download', this);
25262     },
25263     
25264     loadCanvas : function(src)
25265     {   
25266         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25267             
25268             this.reset();
25269             
25270             this.imageEl = document.createElement('img');
25271             
25272             var _this = this;
25273             
25274             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25275             
25276             this.imageEl.src = src;
25277         }
25278     },
25279     
25280     onLoadCanvas : function()
25281     {   
25282         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25283         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25284         
25285         this.bodyEl.un('click', this.beforeSelectFile, this);
25286         
25287         this.notifyEl.hide();
25288         this.thumbEl.show();
25289         this.footerEl.show();
25290         
25291         this.baseRotateLevel();
25292         
25293         if(this.isDocument){
25294             this.setThumbBoxSize();
25295         }
25296         
25297         this.setThumbBoxPosition();
25298         
25299         this.baseScaleLevel();
25300         
25301         this.draw();
25302         
25303         this.resize();
25304         
25305         this.canvasLoaded = true;
25306         
25307         if(this.loadMask){
25308             this.maskEl.unmask();
25309         }
25310         
25311     },
25312     
25313     setCanvasPosition : function()
25314     {   
25315         if(!this.canvasEl){
25316             return;
25317         }
25318         
25319         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25320         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25321         
25322         this.previewEl.setLeft(pw);
25323         this.previewEl.setTop(ph);
25324         
25325     },
25326     
25327     onMouseDown : function(e)
25328     {   
25329         e.stopEvent();
25330         
25331         this.dragable = true;
25332         this.pinching = false;
25333         
25334         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25335             this.dragable = false;
25336             return;
25337         }
25338         
25339         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25340         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25341         
25342     },
25343     
25344     onMouseMove : function(e)
25345     {   
25346         e.stopEvent();
25347         
25348         if(!this.canvasLoaded){
25349             return;
25350         }
25351         
25352         if (!this.dragable){
25353             return;
25354         }
25355         
25356         var minX = Math.ceil(this.thumbEl.getLeft(true));
25357         var minY = Math.ceil(this.thumbEl.getTop(true));
25358         
25359         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25360         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25361         
25362         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25363         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25364         
25365         x = x - this.mouseX;
25366         y = y - this.mouseY;
25367         
25368         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25369         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25370         
25371         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25372         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25373         
25374         this.previewEl.setLeft(bgX);
25375         this.previewEl.setTop(bgY);
25376         
25377         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25378         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25379     },
25380     
25381     onMouseUp : function(e)
25382     {   
25383         e.stopEvent();
25384         
25385         this.dragable = false;
25386     },
25387     
25388     onMouseWheel : function(e)
25389     {   
25390         e.stopEvent();
25391         
25392         this.startScale = this.scale;
25393         
25394         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25395         
25396         if(!this.zoomable()){
25397             this.scale = this.startScale;
25398             return;
25399         }
25400         
25401         this.draw();
25402         
25403         return;
25404     },
25405     
25406     zoomable : function()
25407     {
25408         var minScale = this.thumbEl.getWidth() / this.minWidth;
25409         
25410         if(this.minWidth < this.minHeight){
25411             minScale = this.thumbEl.getHeight() / this.minHeight;
25412         }
25413         
25414         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25415         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25416         
25417         if(
25418                 this.isDocument &&
25419                 (this.rotate == 0 || this.rotate == 180) && 
25420                 (
25421                     width > this.imageEl.OriginWidth || 
25422                     height > this.imageEl.OriginHeight ||
25423                     (width < this.minWidth && height < this.minHeight)
25424                 )
25425         ){
25426             return false;
25427         }
25428         
25429         if(
25430                 this.isDocument &&
25431                 (this.rotate == 90 || this.rotate == 270) && 
25432                 (
25433                     width > this.imageEl.OriginWidth || 
25434                     height > this.imageEl.OriginHeight ||
25435                     (width < this.minHeight && height < this.minWidth)
25436                 )
25437         ){
25438             return false;
25439         }
25440         
25441         if(
25442                 !this.isDocument &&
25443                 (this.rotate == 0 || this.rotate == 180) && 
25444                 (
25445                     width < this.minWidth || 
25446                     width > this.imageEl.OriginWidth || 
25447                     height < this.minHeight || 
25448                     height > this.imageEl.OriginHeight
25449                 )
25450         ){
25451             return false;
25452         }
25453         
25454         if(
25455                 !this.isDocument &&
25456                 (this.rotate == 90 || this.rotate == 270) && 
25457                 (
25458                     width < this.minHeight || 
25459                     width > this.imageEl.OriginWidth || 
25460                     height < this.minWidth || 
25461                     height > this.imageEl.OriginHeight
25462                 )
25463         ){
25464             return false;
25465         }
25466         
25467         return true;
25468         
25469     },
25470     
25471     onRotateLeft : function(e)
25472     {   
25473         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25474             
25475             var minScale = this.thumbEl.getWidth() / this.minWidth;
25476             
25477             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25478             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25479             
25480             this.startScale = this.scale;
25481             
25482             while (this.getScaleLevel() < minScale){
25483             
25484                 this.scale = this.scale + 1;
25485                 
25486                 if(!this.zoomable()){
25487                     break;
25488                 }
25489                 
25490                 if(
25491                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25492                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25493                 ){
25494                     continue;
25495                 }
25496                 
25497                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25498
25499                 this.draw();
25500                 
25501                 return;
25502             }
25503             
25504             this.scale = this.startScale;
25505             
25506             this.onRotateFail();
25507             
25508             return false;
25509         }
25510         
25511         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25512
25513         if(this.isDocument){
25514             this.setThumbBoxSize();
25515             this.setThumbBoxPosition();
25516             this.setCanvasPosition();
25517         }
25518         
25519         this.draw();
25520         
25521         this.fireEvent('rotate', this, 'left');
25522         
25523     },
25524     
25525     onRotateRight : function(e)
25526     {
25527         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25528             
25529             var minScale = this.thumbEl.getWidth() / this.minWidth;
25530         
25531             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25532             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25533             
25534             this.startScale = this.scale;
25535             
25536             while (this.getScaleLevel() < minScale){
25537             
25538                 this.scale = this.scale + 1;
25539                 
25540                 if(!this.zoomable()){
25541                     break;
25542                 }
25543                 
25544                 if(
25545                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25546                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25547                 ){
25548                     continue;
25549                 }
25550                 
25551                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25552
25553                 this.draw();
25554                 
25555                 return;
25556             }
25557             
25558             this.scale = this.startScale;
25559             
25560             this.onRotateFail();
25561             
25562             return false;
25563         }
25564         
25565         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25566
25567         if(this.isDocument){
25568             this.setThumbBoxSize();
25569             this.setThumbBoxPosition();
25570             this.setCanvasPosition();
25571         }
25572         
25573         this.draw();
25574         
25575         this.fireEvent('rotate', this, 'right');
25576     },
25577     
25578     onRotateFail : function()
25579     {
25580         this.errorEl.show(true);
25581         
25582         var _this = this;
25583         
25584         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25585     },
25586     
25587     draw : function()
25588     {
25589         this.previewEl.dom.innerHTML = '';
25590         
25591         var canvasEl = document.createElement("canvas");
25592         
25593         var contextEl = canvasEl.getContext("2d");
25594         
25595         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25596         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25597         var center = this.imageEl.OriginWidth / 2;
25598         
25599         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25600             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25601             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25602             center = this.imageEl.OriginHeight / 2;
25603         }
25604         
25605         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25606         
25607         contextEl.translate(center, center);
25608         contextEl.rotate(this.rotate * Math.PI / 180);
25609
25610         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25611         
25612         this.canvasEl = document.createElement("canvas");
25613         
25614         this.contextEl = this.canvasEl.getContext("2d");
25615         
25616         switch (this.rotate) {
25617             case 0 :
25618                 
25619                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25620                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25621                 
25622                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25623                 
25624                 break;
25625             case 90 : 
25626                 
25627                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25628                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25629                 
25630                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25631                     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);
25632                     break;
25633                 }
25634                 
25635                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25636                 
25637                 break;
25638             case 180 :
25639                 
25640                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25641                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25642                 
25643                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25644                     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);
25645                     break;
25646                 }
25647                 
25648                 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);
25649                 
25650                 break;
25651             case 270 :
25652                 
25653                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25654                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25655         
25656                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25657                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25658                     break;
25659                 }
25660                 
25661                 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);
25662                 
25663                 break;
25664             default : 
25665                 break;
25666         }
25667         
25668         this.previewEl.appendChild(this.canvasEl);
25669         
25670         this.setCanvasPosition();
25671     },
25672     
25673     crop : function()
25674     {
25675         if(!this.canvasLoaded){
25676             return;
25677         }
25678         
25679         var imageCanvas = document.createElement("canvas");
25680         
25681         var imageContext = imageCanvas.getContext("2d");
25682         
25683         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25684         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25685         
25686         var center = imageCanvas.width / 2;
25687         
25688         imageContext.translate(center, center);
25689         
25690         imageContext.rotate(this.rotate * Math.PI / 180);
25691         
25692         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25693         
25694         var canvas = document.createElement("canvas");
25695         
25696         var context = canvas.getContext("2d");
25697                 
25698         canvas.width = this.minWidth;
25699         canvas.height = this.minHeight;
25700
25701         switch (this.rotate) {
25702             case 0 :
25703                 
25704                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25705                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25706                 
25707                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25708                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25709                 
25710                 var targetWidth = this.minWidth - 2 * x;
25711                 var targetHeight = this.minHeight - 2 * y;
25712                 
25713                 var scale = 1;
25714                 
25715                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25716                     scale = targetWidth / width;
25717                 }
25718                 
25719                 if(x > 0 && y == 0){
25720                     scale = targetHeight / height;
25721                 }
25722                 
25723                 if(x > 0 && y > 0){
25724                     scale = targetWidth / width;
25725                     
25726                     if(width < height){
25727                         scale = targetHeight / height;
25728                     }
25729                 }
25730                 
25731                 context.scale(scale, scale);
25732                 
25733                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25734                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25735
25736                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25737                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25738
25739                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25740                 
25741                 break;
25742             case 90 : 
25743                 
25744                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25745                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25746                 
25747                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25748                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25749                 
25750                 var targetWidth = this.minWidth - 2 * x;
25751                 var targetHeight = this.minHeight - 2 * y;
25752                 
25753                 var scale = 1;
25754                 
25755                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25756                     scale = targetWidth / width;
25757                 }
25758                 
25759                 if(x > 0 && y == 0){
25760                     scale = targetHeight / height;
25761                 }
25762                 
25763                 if(x > 0 && y > 0){
25764                     scale = targetWidth / width;
25765                     
25766                     if(width < height){
25767                         scale = targetHeight / height;
25768                     }
25769                 }
25770                 
25771                 context.scale(scale, scale);
25772                 
25773                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25774                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25775
25776                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25777                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25778                 
25779                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25780                 
25781                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25782                 
25783                 break;
25784             case 180 :
25785                 
25786                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25787                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25788                 
25789                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25790                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25791                 
25792                 var targetWidth = this.minWidth - 2 * x;
25793                 var targetHeight = this.minHeight - 2 * y;
25794                 
25795                 var scale = 1;
25796                 
25797                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25798                     scale = targetWidth / width;
25799                 }
25800                 
25801                 if(x > 0 && y == 0){
25802                     scale = targetHeight / height;
25803                 }
25804                 
25805                 if(x > 0 && y > 0){
25806                     scale = targetWidth / width;
25807                     
25808                     if(width < height){
25809                         scale = targetHeight / height;
25810                     }
25811                 }
25812                 
25813                 context.scale(scale, scale);
25814                 
25815                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25816                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25817
25818                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25819                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25820
25821                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25822                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25823                 
25824                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25825                 
25826                 break;
25827             case 270 :
25828                 
25829                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25830                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25831                 
25832                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25833                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25834                 
25835                 var targetWidth = this.minWidth - 2 * x;
25836                 var targetHeight = this.minHeight - 2 * y;
25837                 
25838                 var scale = 1;
25839                 
25840                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25841                     scale = targetWidth / width;
25842                 }
25843                 
25844                 if(x > 0 && y == 0){
25845                     scale = targetHeight / height;
25846                 }
25847                 
25848                 if(x > 0 && y > 0){
25849                     scale = targetWidth / width;
25850                     
25851                     if(width < height){
25852                         scale = targetHeight / height;
25853                     }
25854                 }
25855                 
25856                 context.scale(scale, scale);
25857                 
25858                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25859                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25860
25861                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25862                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25863                 
25864                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25865                 
25866                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25867                 
25868                 break;
25869             default : 
25870                 break;
25871         }
25872         
25873         this.cropData = canvas.toDataURL(this.cropType);
25874         
25875         if(this.fireEvent('crop', this, this.cropData) !== false){
25876             this.process(this.file, this.cropData);
25877         }
25878         
25879         return;
25880         
25881     },
25882     
25883     setThumbBoxSize : function()
25884     {
25885         var width, height;
25886         
25887         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25888             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25889             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25890             
25891             this.minWidth = width;
25892             this.minHeight = height;
25893             
25894             if(this.rotate == 90 || this.rotate == 270){
25895                 this.minWidth = height;
25896                 this.minHeight = width;
25897             }
25898         }
25899         
25900         height = 300;
25901         width = Math.ceil(this.minWidth * height / this.minHeight);
25902         
25903         if(this.minWidth > this.minHeight){
25904             width = 300;
25905             height = Math.ceil(this.minHeight * width / this.minWidth);
25906         }
25907         
25908         this.thumbEl.setStyle({
25909             width : width + 'px',
25910             height : height + 'px'
25911         });
25912
25913         return;
25914             
25915     },
25916     
25917     setThumbBoxPosition : function()
25918     {
25919         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25920         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25921         
25922         this.thumbEl.setLeft(x);
25923         this.thumbEl.setTop(y);
25924         
25925     },
25926     
25927     baseRotateLevel : function()
25928     {
25929         this.baseRotate = 1;
25930         
25931         if(
25932                 typeof(this.exif) != 'undefined' &&
25933                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25934                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25935         ){
25936             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25937         }
25938         
25939         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25940         
25941     },
25942     
25943     baseScaleLevel : function()
25944     {
25945         var width, height;
25946         
25947         if(this.isDocument){
25948             
25949             if(this.baseRotate == 6 || this.baseRotate == 8){
25950             
25951                 height = this.thumbEl.getHeight();
25952                 this.baseScale = height / this.imageEl.OriginWidth;
25953
25954                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25955                     width = this.thumbEl.getWidth();
25956                     this.baseScale = width / this.imageEl.OriginHeight;
25957                 }
25958
25959                 return;
25960             }
25961
25962             height = this.thumbEl.getHeight();
25963             this.baseScale = height / this.imageEl.OriginHeight;
25964
25965             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25966                 width = this.thumbEl.getWidth();
25967                 this.baseScale = width / this.imageEl.OriginWidth;
25968             }
25969
25970             return;
25971         }
25972         
25973         if(this.baseRotate == 6 || this.baseRotate == 8){
25974             
25975             width = this.thumbEl.getHeight();
25976             this.baseScale = width / this.imageEl.OriginHeight;
25977             
25978             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25979                 height = this.thumbEl.getWidth();
25980                 this.baseScale = height / this.imageEl.OriginHeight;
25981             }
25982             
25983             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25984                 height = this.thumbEl.getWidth();
25985                 this.baseScale = height / this.imageEl.OriginHeight;
25986                 
25987                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25988                     width = this.thumbEl.getHeight();
25989                     this.baseScale = width / this.imageEl.OriginWidth;
25990                 }
25991             }
25992             
25993             return;
25994         }
25995         
25996         width = this.thumbEl.getWidth();
25997         this.baseScale = width / this.imageEl.OriginWidth;
25998         
25999         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26000             height = this.thumbEl.getHeight();
26001             this.baseScale = height / this.imageEl.OriginHeight;
26002         }
26003         
26004         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26005             
26006             height = this.thumbEl.getHeight();
26007             this.baseScale = height / this.imageEl.OriginHeight;
26008             
26009             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26010                 width = this.thumbEl.getWidth();
26011                 this.baseScale = width / this.imageEl.OriginWidth;
26012             }
26013             
26014         }
26015         
26016         return;
26017     },
26018     
26019     getScaleLevel : function()
26020     {
26021         return this.baseScale * Math.pow(1.1, this.scale);
26022     },
26023     
26024     onTouchStart : function(e)
26025     {
26026         if(!this.canvasLoaded){
26027             this.beforeSelectFile(e);
26028             return;
26029         }
26030         
26031         var touches = e.browserEvent.touches;
26032         
26033         if(!touches){
26034             return;
26035         }
26036         
26037         if(touches.length == 1){
26038             this.onMouseDown(e);
26039             return;
26040         }
26041         
26042         if(touches.length != 2){
26043             return;
26044         }
26045         
26046         var coords = [];
26047         
26048         for(var i = 0, finger; finger = touches[i]; i++){
26049             coords.push(finger.pageX, finger.pageY);
26050         }
26051         
26052         var x = Math.pow(coords[0] - coords[2], 2);
26053         var y = Math.pow(coords[1] - coords[3], 2);
26054         
26055         this.startDistance = Math.sqrt(x + y);
26056         
26057         this.startScale = this.scale;
26058         
26059         this.pinching = true;
26060         this.dragable = false;
26061         
26062     },
26063     
26064     onTouchMove : function(e)
26065     {
26066         if(!this.pinching && !this.dragable){
26067             return;
26068         }
26069         
26070         var touches = e.browserEvent.touches;
26071         
26072         if(!touches){
26073             return;
26074         }
26075         
26076         if(this.dragable){
26077             this.onMouseMove(e);
26078             return;
26079         }
26080         
26081         var coords = [];
26082         
26083         for(var i = 0, finger; finger = touches[i]; i++){
26084             coords.push(finger.pageX, finger.pageY);
26085         }
26086         
26087         var x = Math.pow(coords[0] - coords[2], 2);
26088         var y = Math.pow(coords[1] - coords[3], 2);
26089         
26090         this.endDistance = Math.sqrt(x + y);
26091         
26092         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26093         
26094         if(!this.zoomable()){
26095             this.scale = this.startScale;
26096             return;
26097         }
26098         
26099         this.draw();
26100         
26101     },
26102     
26103     onTouchEnd : function(e)
26104     {
26105         this.pinching = false;
26106         this.dragable = false;
26107         
26108     },
26109     
26110     process : function(file, crop)
26111     {
26112         if(this.loadMask){
26113             this.maskEl.mask(this.loadingText);
26114         }
26115         
26116         this.xhr = new XMLHttpRequest();
26117         
26118         file.xhr = this.xhr;
26119
26120         this.xhr.open(this.method, this.url, true);
26121         
26122         var headers = {
26123             "Accept": "application/json",
26124             "Cache-Control": "no-cache",
26125             "X-Requested-With": "XMLHttpRequest"
26126         };
26127         
26128         for (var headerName in headers) {
26129             var headerValue = headers[headerName];
26130             if (headerValue) {
26131                 this.xhr.setRequestHeader(headerName, headerValue);
26132             }
26133         }
26134         
26135         var _this = this;
26136         
26137         this.xhr.onload = function()
26138         {
26139             _this.xhrOnLoad(_this.xhr);
26140         }
26141         
26142         this.xhr.onerror = function()
26143         {
26144             _this.xhrOnError(_this.xhr);
26145         }
26146         
26147         var formData = new FormData();
26148
26149         formData.append('returnHTML', 'NO');
26150         
26151         if(crop){
26152             formData.append('crop', crop);
26153         }
26154         
26155         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26156             formData.append(this.paramName, file, file.name);
26157         }
26158         
26159         if(typeof(file.filename) != 'undefined'){
26160             formData.append('filename', file.filename);
26161         }
26162         
26163         if(typeof(file.mimetype) != 'undefined'){
26164             formData.append('mimetype', file.mimetype);
26165         }
26166         
26167         if(this.fireEvent('arrange', this, formData) != false){
26168             this.xhr.send(formData);
26169         };
26170     },
26171     
26172     xhrOnLoad : function(xhr)
26173     {
26174         if(this.loadMask){
26175             this.maskEl.unmask();
26176         }
26177         
26178         if (xhr.readyState !== 4) {
26179             this.fireEvent('exception', this, xhr);
26180             return;
26181         }
26182
26183         var response = Roo.decode(xhr.responseText);
26184         
26185         if(!response.success){
26186             this.fireEvent('exception', this, xhr);
26187             return;
26188         }
26189         
26190         var response = Roo.decode(xhr.responseText);
26191         
26192         this.fireEvent('upload', this, response);
26193         
26194     },
26195     
26196     xhrOnError : function()
26197     {
26198         if(this.loadMask){
26199             this.maskEl.unmask();
26200         }
26201         
26202         Roo.log('xhr on error');
26203         
26204         var response = Roo.decode(xhr.responseText);
26205           
26206         Roo.log(response);
26207         
26208     },
26209     
26210     prepare : function(file)
26211     {   
26212         if(this.loadMask){
26213             this.maskEl.mask(this.loadingText);
26214         }
26215         
26216         this.file = false;
26217         this.exif = {};
26218         
26219         if(typeof(file) === 'string'){
26220             this.loadCanvas(file);
26221             return;
26222         }
26223         
26224         if(!file || !this.urlAPI){
26225             return;
26226         }
26227         
26228         this.file = file;
26229         this.cropType = file.type;
26230         
26231         var _this = this;
26232         
26233         if(this.fireEvent('prepare', this, this.file) != false){
26234             
26235             var reader = new FileReader();
26236             
26237             reader.onload = function (e) {
26238                 if (e.target.error) {
26239                     Roo.log(e.target.error);
26240                     return;
26241                 }
26242                 
26243                 var buffer = e.target.result,
26244                     dataView = new DataView(buffer),
26245                     offset = 2,
26246                     maxOffset = dataView.byteLength - 4,
26247                     markerBytes,
26248                     markerLength;
26249                 
26250                 if (dataView.getUint16(0) === 0xffd8) {
26251                     while (offset < maxOffset) {
26252                         markerBytes = dataView.getUint16(offset);
26253                         
26254                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26255                             markerLength = dataView.getUint16(offset + 2) + 2;
26256                             if (offset + markerLength > dataView.byteLength) {
26257                                 Roo.log('Invalid meta data: Invalid segment size.');
26258                                 break;
26259                             }
26260                             
26261                             if(markerBytes == 0xffe1){
26262                                 _this.parseExifData(
26263                                     dataView,
26264                                     offset,
26265                                     markerLength
26266                                 );
26267                             }
26268                             
26269                             offset += markerLength;
26270                             
26271                             continue;
26272                         }
26273                         
26274                         break;
26275                     }
26276                     
26277                 }
26278                 
26279                 var url = _this.urlAPI.createObjectURL(_this.file);
26280                 
26281                 _this.loadCanvas(url);
26282                 
26283                 return;
26284             }
26285             
26286             reader.readAsArrayBuffer(this.file);
26287             
26288         }
26289         
26290     },
26291     
26292     parseExifData : function(dataView, offset, length)
26293     {
26294         var tiffOffset = offset + 10,
26295             littleEndian,
26296             dirOffset;
26297     
26298         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26299             // No Exif data, might be XMP data instead
26300             return;
26301         }
26302         
26303         // Check for the ASCII code for "Exif" (0x45786966):
26304         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26305             // No Exif data, might be XMP data instead
26306             return;
26307         }
26308         if (tiffOffset + 8 > dataView.byteLength) {
26309             Roo.log('Invalid Exif data: Invalid segment size.');
26310             return;
26311         }
26312         // Check for the two null bytes:
26313         if (dataView.getUint16(offset + 8) !== 0x0000) {
26314             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26315             return;
26316         }
26317         // Check the byte alignment:
26318         switch (dataView.getUint16(tiffOffset)) {
26319         case 0x4949:
26320             littleEndian = true;
26321             break;
26322         case 0x4D4D:
26323             littleEndian = false;
26324             break;
26325         default:
26326             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26327             return;
26328         }
26329         // Check for the TIFF tag marker (0x002A):
26330         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26331             Roo.log('Invalid Exif data: Missing TIFF marker.');
26332             return;
26333         }
26334         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26335         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26336         
26337         this.parseExifTags(
26338             dataView,
26339             tiffOffset,
26340             tiffOffset + dirOffset,
26341             littleEndian
26342         );
26343     },
26344     
26345     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26346     {
26347         var tagsNumber,
26348             dirEndOffset,
26349             i;
26350         if (dirOffset + 6 > dataView.byteLength) {
26351             Roo.log('Invalid Exif data: Invalid directory offset.');
26352             return;
26353         }
26354         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26355         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26356         if (dirEndOffset + 4 > dataView.byteLength) {
26357             Roo.log('Invalid Exif data: Invalid directory size.');
26358             return;
26359         }
26360         for (i = 0; i < tagsNumber; i += 1) {
26361             this.parseExifTag(
26362                 dataView,
26363                 tiffOffset,
26364                 dirOffset + 2 + 12 * i, // tag offset
26365                 littleEndian
26366             );
26367         }
26368         // Return the offset to the next directory:
26369         return dataView.getUint32(dirEndOffset, littleEndian);
26370     },
26371     
26372     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26373     {
26374         var tag = dataView.getUint16(offset, littleEndian);
26375         
26376         this.exif[tag] = this.getExifValue(
26377             dataView,
26378             tiffOffset,
26379             offset,
26380             dataView.getUint16(offset + 2, littleEndian), // tag type
26381             dataView.getUint32(offset + 4, littleEndian), // tag length
26382             littleEndian
26383         );
26384     },
26385     
26386     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26387     {
26388         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26389             tagSize,
26390             dataOffset,
26391             values,
26392             i,
26393             str,
26394             c;
26395     
26396         if (!tagType) {
26397             Roo.log('Invalid Exif data: Invalid tag type.');
26398             return;
26399         }
26400         
26401         tagSize = tagType.size * length;
26402         // Determine if the value is contained in the dataOffset bytes,
26403         // or if the value at the dataOffset is a pointer to the actual data:
26404         dataOffset = tagSize > 4 ?
26405                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26406         if (dataOffset + tagSize > dataView.byteLength) {
26407             Roo.log('Invalid Exif data: Invalid data offset.');
26408             return;
26409         }
26410         if (length === 1) {
26411             return tagType.getValue(dataView, dataOffset, littleEndian);
26412         }
26413         values = [];
26414         for (i = 0; i < length; i += 1) {
26415             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26416         }
26417         
26418         if (tagType.ascii) {
26419             str = '';
26420             // Concatenate the chars:
26421             for (i = 0; i < values.length; i += 1) {
26422                 c = values[i];
26423                 // Ignore the terminating NULL byte(s):
26424                 if (c === '\u0000') {
26425                     break;
26426                 }
26427                 str += c;
26428             }
26429             return str;
26430         }
26431         return values;
26432     }
26433     
26434 });
26435
26436 Roo.apply(Roo.bootstrap.UploadCropbox, {
26437     tags : {
26438         'Orientation': 0x0112
26439     },
26440     
26441     Orientation: {
26442             1: 0, //'top-left',
26443 //            2: 'top-right',
26444             3: 180, //'bottom-right',
26445 //            4: 'bottom-left',
26446 //            5: 'left-top',
26447             6: 90, //'right-top',
26448 //            7: 'right-bottom',
26449             8: 270 //'left-bottom'
26450     },
26451     
26452     exifTagTypes : {
26453         // byte, 8-bit unsigned int:
26454         1: {
26455             getValue: function (dataView, dataOffset) {
26456                 return dataView.getUint8(dataOffset);
26457             },
26458             size: 1
26459         },
26460         // ascii, 8-bit byte:
26461         2: {
26462             getValue: function (dataView, dataOffset) {
26463                 return String.fromCharCode(dataView.getUint8(dataOffset));
26464             },
26465             size: 1,
26466             ascii: true
26467         },
26468         // short, 16 bit int:
26469         3: {
26470             getValue: function (dataView, dataOffset, littleEndian) {
26471                 return dataView.getUint16(dataOffset, littleEndian);
26472             },
26473             size: 2
26474         },
26475         // long, 32 bit int:
26476         4: {
26477             getValue: function (dataView, dataOffset, littleEndian) {
26478                 return dataView.getUint32(dataOffset, littleEndian);
26479             },
26480             size: 4
26481         },
26482         // rational = two long values, first is numerator, second is denominator:
26483         5: {
26484             getValue: function (dataView, dataOffset, littleEndian) {
26485                 return dataView.getUint32(dataOffset, littleEndian) /
26486                     dataView.getUint32(dataOffset + 4, littleEndian);
26487             },
26488             size: 8
26489         },
26490         // slong, 32 bit signed int:
26491         9: {
26492             getValue: function (dataView, dataOffset, littleEndian) {
26493                 return dataView.getInt32(dataOffset, littleEndian);
26494             },
26495             size: 4
26496         },
26497         // srational, two slongs, first is numerator, second is denominator:
26498         10: {
26499             getValue: function (dataView, dataOffset, littleEndian) {
26500                 return dataView.getInt32(dataOffset, littleEndian) /
26501                     dataView.getInt32(dataOffset + 4, littleEndian);
26502             },
26503             size: 8
26504         }
26505     },
26506     
26507     footer : {
26508         STANDARD : [
26509             {
26510                 tag : 'div',
26511                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26512                 action : 'rotate-left',
26513                 cn : [
26514                     {
26515                         tag : 'button',
26516                         cls : 'btn btn-default',
26517                         html : '<i class="fa fa-undo"></i>'
26518                     }
26519                 ]
26520             },
26521             {
26522                 tag : 'div',
26523                 cls : 'btn-group roo-upload-cropbox-picture',
26524                 action : 'picture',
26525                 cn : [
26526                     {
26527                         tag : 'button',
26528                         cls : 'btn btn-default',
26529                         html : '<i class="fa fa-picture-o"></i>'
26530                     }
26531                 ]
26532             },
26533             {
26534                 tag : 'div',
26535                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26536                 action : 'rotate-right',
26537                 cn : [
26538                     {
26539                         tag : 'button',
26540                         cls : 'btn btn-default',
26541                         html : '<i class="fa fa-repeat"></i>'
26542                     }
26543                 ]
26544             }
26545         ],
26546         DOCUMENT : [
26547             {
26548                 tag : 'div',
26549                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26550                 action : 'rotate-left',
26551                 cn : [
26552                     {
26553                         tag : 'button',
26554                         cls : 'btn btn-default',
26555                         html : '<i class="fa fa-undo"></i>'
26556                     }
26557                 ]
26558             },
26559             {
26560                 tag : 'div',
26561                 cls : 'btn-group roo-upload-cropbox-download',
26562                 action : 'download',
26563                 cn : [
26564                     {
26565                         tag : 'button',
26566                         cls : 'btn btn-default',
26567                         html : '<i class="fa fa-download"></i>'
26568                     }
26569                 ]
26570             },
26571             {
26572                 tag : 'div',
26573                 cls : 'btn-group roo-upload-cropbox-crop',
26574                 action : 'crop',
26575                 cn : [
26576                     {
26577                         tag : 'button',
26578                         cls : 'btn btn-default',
26579                         html : '<i class="fa fa-crop"></i>'
26580                     }
26581                 ]
26582             },
26583             {
26584                 tag : 'div',
26585                 cls : 'btn-group roo-upload-cropbox-trash',
26586                 action : 'trash',
26587                 cn : [
26588                     {
26589                         tag : 'button',
26590                         cls : 'btn btn-default',
26591                         html : '<i class="fa fa-trash"></i>'
26592                     }
26593                 ]
26594             },
26595             {
26596                 tag : 'div',
26597                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26598                 action : 'rotate-right',
26599                 cn : [
26600                     {
26601                         tag : 'button',
26602                         cls : 'btn btn-default',
26603                         html : '<i class="fa fa-repeat"></i>'
26604                     }
26605                 ]
26606             }
26607         ],
26608         ROTATOR : [
26609             {
26610                 tag : 'div',
26611                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26612                 action : 'rotate-left',
26613                 cn : [
26614                     {
26615                         tag : 'button',
26616                         cls : 'btn btn-default',
26617                         html : '<i class="fa fa-undo"></i>'
26618                     }
26619                 ]
26620             },
26621             {
26622                 tag : 'div',
26623                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26624                 action : 'rotate-right',
26625                 cn : [
26626                     {
26627                         tag : 'button',
26628                         cls : 'btn btn-default',
26629                         html : '<i class="fa fa-repeat"></i>'
26630                     }
26631                 ]
26632             }
26633         ]
26634     }
26635 });
26636
26637 /*
26638 * Licence: LGPL
26639 */
26640
26641 /**
26642  * @class Roo.bootstrap.DocumentManager
26643  * @extends Roo.bootstrap.Component
26644  * Bootstrap DocumentManager class
26645  * @cfg {String} paramName default 'imageUpload'
26646  * @cfg {String} method default POST
26647  * @cfg {String} url action url
26648  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26649  * @cfg {Boolean} multiple multiple upload default true
26650  * @cfg {Number} thumbSize default 300
26651  * @cfg {String} fieldLabel
26652  * @cfg {Number} labelWidth default 4
26653  * @cfg {String} labelAlign (left|top) default left
26654  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26655  * 
26656  * @constructor
26657  * Create a new DocumentManager
26658  * @param {Object} config The config object
26659  */
26660
26661 Roo.bootstrap.DocumentManager = function(config){
26662     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26663     
26664     this.addEvents({
26665         /**
26666          * @event initial
26667          * Fire when initial the DocumentManager
26668          * @param {Roo.bootstrap.DocumentManager} this
26669          */
26670         "initial" : true,
26671         /**
26672          * @event inspect
26673          * inspect selected file
26674          * @param {Roo.bootstrap.DocumentManager} this
26675          * @param {File} file
26676          */
26677         "inspect" : true,
26678         /**
26679          * @event exception
26680          * Fire when xhr load exception
26681          * @param {Roo.bootstrap.DocumentManager} this
26682          * @param {XMLHttpRequest} xhr
26683          */
26684         "exception" : true,
26685         /**
26686          * @event prepare
26687          * prepare the form data
26688          * @param {Roo.bootstrap.DocumentManager} this
26689          * @param {Object} formData
26690          */
26691         "prepare" : true,
26692         /**
26693          * @event remove
26694          * Fire when remove the file
26695          * @param {Roo.bootstrap.DocumentManager} this
26696          * @param {Object} file
26697          */
26698         "remove" : true,
26699         /**
26700          * @event refresh
26701          * Fire after refresh the file
26702          * @param {Roo.bootstrap.DocumentManager} this
26703          */
26704         "refresh" : true,
26705         /**
26706          * @event click
26707          * Fire after click the image
26708          * @param {Roo.bootstrap.DocumentManager} this
26709          * @param {Object} file
26710          */
26711         "click" : true,
26712         /**
26713          * @event edit
26714          * Fire when upload a image and editable set to true
26715          * @param {Roo.bootstrap.DocumentManager} this
26716          * @param {Object} file
26717          */
26718         "edit" : true,
26719         /**
26720          * @event beforeselectfile
26721          * Fire before select file
26722          * @param {Roo.bootstrap.DocumentManager} this
26723          */
26724         "beforeselectfile" : true,
26725         /**
26726          * @event process
26727          * Fire before process file
26728          * @param {Roo.bootstrap.DocumentManager} this
26729          * @param {Object} file
26730          */
26731         "process" : true
26732         
26733     });
26734 };
26735
26736 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26737     
26738     boxes : 0,
26739     inputName : '',
26740     thumbSize : 300,
26741     multiple : true,
26742     files : [],
26743     method : 'POST',
26744     url : '',
26745     paramName : 'imageUpload',
26746     fieldLabel : '',
26747     labelWidth : 4,
26748     labelAlign : 'left',
26749     editable : true,
26750     delegates : [],
26751     
26752     
26753     xhr : false, 
26754     
26755     getAutoCreate : function()
26756     {   
26757         var managerWidget = {
26758             tag : 'div',
26759             cls : 'roo-document-manager',
26760             cn : [
26761                 {
26762                     tag : 'input',
26763                     cls : 'roo-document-manager-selector',
26764                     type : 'file'
26765                 },
26766                 {
26767                     tag : 'div',
26768                     cls : 'roo-document-manager-uploader',
26769                     cn : [
26770                         {
26771                             tag : 'div',
26772                             cls : 'roo-document-manager-upload-btn',
26773                             html : '<i class="fa fa-plus"></i>'
26774                         }
26775                     ]
26776                     
26777                 }
26778             ]
26779         };
26780         
26781         var content = [
26782             {
26783                 tag : 'div',
26784                 cls : 'column col-md-12',
26785                 cn : managerWidget
26786             }
26787         ];
26788         
26789         if(this.fieldLabel.length){
26790             
26791             content = [
26792                 {
26793                     tag : 'div',
26794                     cls : 'column col-md-12',
26795                     html : this.fieldLabel
26796                 },
26797                 {
26798                     tag : 'div',
26799                     cls : 'column col-md-12',
26800                     cn : managerWidget
26801                 }
26802             ];
26803
26804             if(this.labelAlign == 'left'){
26805                 content = [
26806                     {
26807                         tag : 'div',
26808                         cls : 'column col-md-' + this.labelWidth,
26809                         html : this.fieldLabel
26810                     },
26811                     {
26812                         tag : 'div',
26813                         cls : 'column col-md-' + (12 - this.labelWidth),
26814                         cn : managerWidget
26815                     }
26816                 ];
26817                 
26818             }
26819         }
26820         
26821         var cfg = {
26822             tag : 'div',
26823             cls : 'row clearfix',
26824             cn : content
26825         };
26826         
26827         return cfg;
26828         
26829     },
26830     
26831     initEvents : function()
26832     {
26833         this.managerEl = this.el.select('.roo-document-manager', true).first();
26834         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26835         
26836         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26837         this.selectorEl.hide();
26838         
26839         if(this.multiple){
26840             this.selectorEl.attr('multiple', 'multiple');
26841         }
26842         
26843         this.selectorEl.on('change', this.onFileSelected, this);
26844         
26845         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26846         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26847         
26848         this.uploader.on('click', this.onUploaderClick, this);
26849         
26850         this.renderProgressDialog();
26851         
26852         var _this = this;
26853         
26854         window.addEventListener("resize", function() { _this.refresh(); } );
26855         
26856         this.fireEvent('initial', this);
26857     },
26858     
26859     renderProgressDialog : function()
26860     {
26861         var _this = this;
26862         
26863         this.progressDialog = new Roo.bootstrap.Modal({
26864             cls : 'roo-document-manager-progress-dialog',
26865             allow_close : false,
26866             title : '',
26867             buttons : [
26868                 {
26869                     name  :'cancel',
26870                     weight : 'danger',
26871                     html : 'Cancel'
26872                 }
26873             ], 
26874             listeners : { 
26875                 btnclick : function() {
26876                     _this.uploadCancel();
26877                     this.hide();
26878                 }
26879             }
26880         });
26881          
26882         this.progressDialog.render(Roo.get(document.body));
26883          
26884         this.progress = new Roo.bootstrap.Progress({
26885             cls : 'roo-document-manager-progress',
26886             active : true,
26887             striped : true
26888         });
26889         
26890         this.progress.render(this.progressDialog.getChildContainer());
26891         
26892         this.progressBar = new Roo.bootstrap.ProgressBar({
26893             cls : 'roo-document-manager-progress-bar',
26894             aria_valuenow : 0,
26895             aria_valuemin : 0,
26896             aria_valuemax : 12,
26897             panel : 'success'
26898         });
26899         
26900         this.progressBar.render(this.progress.getChildContainer());
26901     },
26902     
26903     onUploaderClick : function(e)
26904     {
26905         e.preventDefault();
26906      
26907         if(this.fireEvent('beforeselectfile', this) != false){
26908             this.selectorEl.dom.click();
26909         }
26910         
26911     },
26912     
26913     onFileSelected : function(e)
26914     {
26915         e.preventDefault();
26916         
26917         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26918             return;
26919         }
26920         
26921         Roo.each(this.selectorEl.dom.files, function(file){
26922             if(this.fireEvent('inspect', this, file) != false){
26923                 this.files.push(file);
26924             }
26925         }, this);
26926         
26927         this.queue();
26928         
26929     },
26930     
26931     queue : function()
26932     {
26933         this.selectorEl.dom.value = '';
26934         
26935         if(!this.files.length){
26936             return;
26937         }
26938         
26939         if(this.boxes > 0 && this.files.length > this.boxes){
26940             this.files = this.files.slice(0, this.boxes);
26941         }
26942         
26943         this.uploader.show();
26944         
26945         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26946             this.uploader.hide();
26947         }
26948         
26949         var _this = this;
26950         
26951         var files = [];
26952         
26953         var docs = [];
26954         
26955         Roo.each(this.files, function(file){
26956             
26957             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26958                 var f = this.renderPreview(file);
26959                 files.push(f);
26960                 return;
26961             }
26962             
26963             if(file.type.indexOf('image') != -1){
26964                 this.delegates.push(
26965                     (function(){
26966                         _this.process(file);
26967                     }).createDelegate(this)
26968                 );
26969         
26970                 return;
26971             }
26972             
26973             docs.push(
26974                 (function(){
26975                     _this.process(file);
26976                 }).createDelegate(this)
26977             );
26978             
26979         }, this);
26980         
26981         this.files = files;
26982         
26983         this.delegates = this.delegates.concat(docs);
26984         
26985         if(!this.delegates.length){
26986             this.refresh();
26987             return;
26988         }
26989         
26990         this.progressBar.aria_valuemax = this.delegates.length;
26991         
26992         this.arrange();
26993         
26994         return;
26995     },
26996     
26997     arrange : function()
26998     {
26999         if(!this.delegates.length){
27000             this.progressDialog.hide();
27001             this.refresh();
27002             return;
27003         }
27004         
27005         var delegate = this.delegates.shift();
27006         
27007         this.progressDialog.show();
27008         
27009         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27010         
27011         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27012         
27013         delegate();
27014     },
27015     
27016     refresh : function()
27017     {
27018         this.uploader.show();
27019         
27020         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27021             this.uploader.hide();
27022         }
27023         
27024         Roo.isTouch ? this.closable(false) : this.closable(true);
27025         
27026         this.fireEvent('refresh', this);
27027     },
27028     
27029     onRemove : function(e, el, o)
27030     {
27031         e.preventDefault();
27032         
27033         this.fireEvent('remove', this, o);
27034         
27035     },
27036     
27037     remove : function(o)
27038     {
27039         var files = [];
27040         
27041         Roo.each(this.files, function(file){
27042             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27043                 files.push(file);
27044                 return;
27045             }
27046
27047             o.target.remove();
27048
27049         }, this);
27050         
27051         this.files = files;
27052         
27053         this.refresh();
27054     },
27055     
27056     clear : function()
27057     {
27058         Roo.each(this.files, function(file){
27059             if(!file.target){
27060                 return;
27061             }
27062             
27063             file.target.remove();
27064
27065         }, this);
27066         
27067         this.files = [];
27068         
27069         this.refresh();
27070     },
27071     
27072     onClick : function(e, el, o)
27073     {
27074         e.preventDefault();
27075         
27076         this.fireEvent('click', this, o);
27077         
27078     },
27079     
27080     closable : function(closable)
27081     {
27082         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27083             
27084             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27085             
27086             if(closable){
27087                 el.show();
27088                 return;
27089             }
27090             
27091             el.hide();
27092             
27093         }, this);
27094     },
27095     
27096     xhrOnLoad : function(xhr)
27097     {
27098         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27099             el.remove();
27100         }, this);
27101         
27102         if (xhr.readyState !== 4) {
27103             this.arrange();
27104             this.fireEvent('exception', this, xhr);
27105             return;
27106         }
27107
27108         var response = Roo.decode(xhr.responseText);
27109         
27110         if(!response.success){
27111             this.arrange();
27112             this.fireEvent('exception', this, xhr);
27113             return;
27114         }
27115         
27116         var file = this.renderPreview(response.data);
27117         
27118         this.files.push(file);
27119         
27120         this.arrange();
27121         
27122     },
27123     
27124     xhrOnError : function(xhr)
27125     {
27126         Roo.log('xhr on error');
27127         
27128         var response = Roo.decode(xhr.responseText);
27129           
27130         Roo.log(response);
27131         
27132         this.arrange();
27133     },
27134     
27135     process : function(file)
27136     {
27137         if(this.fireEvent('process', this, file) !== false){
27138             if(this.editable && file.type.indexOf('image') != -1){
27139                 this.fireEvent('edit', this, file);
27140                 return;
27141             }
27142
27143             this.uploadStart(file, false);
27144
27145             return;
27146         }
27147         
27148     },
27149     
27150     uploadStart : function(file, crop)
27151     {
27152         this.xhr = new XMLHttpRequest();
27153         
27154         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27155             this.arrange();
27156             return;
27157         }
27158         
27159         file.xhr = this.xhr;
27160             
27161         this.managerEl.createChild({
27162             tag : 'div',
27163             cls : 'roo-document-manager-loading',
27164             cn : [
27165                 {
27166                     tag : 'div',
27167                     tooltip : file.name,
27168                     cls : 'roo-document-manager-thumb',
27169                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27170                 }
27171             ]
27172
27173         });
27174
27175         this.xhr.open(this.method, this.url, true);
27176         
27177         var headers = {
27178             "Accept": "application/json",
27179             "Cache-Control": "no-cache",
27180             "X-Requested-With": "XMLHttpRequest"
27181         };
27182         
27183         for (var headerName in headers) {
27184             var headerValue = headers[headerName];
27185             if (headerValue) {
27186                 this.xhr.setRequestHeader(headerName, headerValue);
27187             }
27188         }
27189         
27190         var _this = this;
27191         
27192         this.xhr.onload = function()
27193         {
27194             _this.xhrOnLoad(_this.xhr);
27195         }
27196         
27197         this.xhr.onerror = function()
27198         {
27199             _this.xhrOnError(_this.xhr);
27200         }
27201         
27202         var formData = new FormData();
27203
27204         formData.append('returnHTML', 'NO');
27205         
27206         if(crop){
27207             formData.append('crop', crop);
27208         }
27209         
27210         formData.append(this.paramName, file, file.name);
27211         
27212         if(this.fireEvent('prepare', this, formData) != false){
27213             this.xhr.send(formData);
27214         };
27215     },
27216     
27217     uploadCancel : function()
27218     {
27219         if (this.xhr) {
27220             this.xhr.abort();
27221         }
27222         
27223         
27224         this.delegates = [];
27225         
27226         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27227             el.remove();
27228         }, this);
27229         
27230         this.arrange();
27231     },
27232     
27233     renderPreview : function(file)
27234     {
27235         if(typeof(file.target) != 'undefined' && file.target){
27236             return file;
27237         }
27238         
27239         var previewEl = this.managerEl.createChild({
27240             tag : 'div',
27241             cls : 'roo-document-manager-preview',
27242             cn : [
27243                 {
27244                     tag : 'div',
27245                     tooltip : file.filename,
27246                     cls : 'roo-document-manager-thumb',
27247                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27248                 },
27249                 {
27250                     tag : 'button',
27251                     cls : 'close',
27252                     html : '<i class="fa fa-times-circle"></i>'
27253                 }
27254             ]
27255         });
27256
27257         var close = previewEl.select('button.close', true).first();
27258
27259         close.on('click', this.onRemove, this, file);
27260
27261         file.target = previewEl;
27262
27263         var image = previewEl.select('img', true).first();
27264         
27265         var _this = this;
27266         
27267         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27268         
27269         image.on('click', this.onClick, this, file);
27270         
27271         return file;
27272         
27273     },
27274     
27275     onPreviewLoad : function(file, image)
27276     {
27277         if(typeof(file.target) == 'undefined' || !file.target){
27278             return;
27279         }
27280         
27281         var width = image.dom.naturalWidth || image.dom.width;
27282         var height = image.dom.naturalHeight || image.dom.height;
27283         
27284         if(width > height){
27285             file.target.addClass('wide');
27286             return;
27287         }
27288         
27289         file.target.addClass('tall');
27290         return;
27291         
27292     },
27293     
27294     uploadFromSource : function(file, crop)
27295     {
27296         this.xhr = new XMLHttpRequest();
27297         
27298         this.managerEl.createChild({
27299             tag : 'div',
27300             cls : 'roo-document-manager-loading',
27301             cn : [
27302                 {
27303                     tag : 'div',
27304                     tooltip : file.name,
27305                     cls : 'roo-document-manager-thumb',
27306                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27307                 }
27308             ]
27309
27310         });
27311
27312         this.xhr.open(this.method, this.url, true);
27313         
27314         var headers = {
27315             "Accept": "application/json",
27316             "Cache-Control": "no-cache",
27317             "X-Requested-With": "XMLHttpRequest"
27318         };
27319         
27320         for (var headerName in headers) {
27321             var headerValue = headers[headerName];
27322             if (headerValue) {
27323                 this.xhr.setRequestHeader(headerName, headerValue);
27324             }
27325         }
27326         
27327         var _this = this;
27328         
27329         this.xhr.onload = function()
27330         {
27331             _this.xhrOnLoad(_this.xhr);
27332         }
27333         
27334         this.xhr.onerror = function()
27335         {
27336             _this.xhrOnError(_this.xhr);
27337         }
27338         
27339         var formData = new FormData();
27340
27341         formData.append('returnHTML', 'NO');
27342         
27343         formData.append('crop', crop);
27344         
27345         if(typeof(file.filename) != 'undefined'){
27346             formData.append('filename', file.filename);
27347         }
27348         
27349         if(typeof(file.mimetype) != 'undefined'){
27350             formData.append('mimetype', file.mimetype);
27351         }
27352         
27353         if(this.fireEvent('prepare', this, formData) != false){
27354             this.xhr.send(formData);
27355         };
27356     }
27357 });
27358
27359 /*
27360 * Licence: LGPL
27361 */
27362
27363 /**
27364  * @class Roo.bootstrap.DocumentViewer
27365  * @extends Roo.bootstrap.Component
27366  * Bootstrap DocumentViewer class
27367  * 
27368  * @constructor
27369  * Create a new DocumentViewer
27370  * @param {Object} config The config object
27371  */
27372
27373 Roo.bootstrap.DocumentViewer = function(config){
27374     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27375     
27376     this.addEvents({
27377         /**
27378          * @event initial
27379          * Fire after initEvent
27380          * @param {Roo.bootstrap.DocumentViewer} this
27381          */
27382         "initial" : true,
27383         /**
27384          * @event click
27385          * Fire after click
27386          * @param {Roo.bootstrap.DocumentViewer} this
27387          */
27388         "click" : true,
27389         /**
27390          * @event trash
27391          * Fire after trash button
27392          * @param {Roo.bootstrap.DocumentViewer} this
27393          */
27394         "trash" : true
27395         
27396     });
27397 };
27398
27399 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27400     
27401     getAutoCreate : function()
27402     {
27403         var cfg = {
27404             tag : 'div',
27405             cls : 'roo-document-viewer',
27406             cn : [
27407                 {
27408                     tag : 'div',
27409                     cls : 'roo-document-viewer-body',
27410                     cn : [
27411                         {
27412                             tag : 'div',
27413                             cls : 'roo-document-viewer-thumb',
27414                             cn : [
27415                                 {
27416                                     tag : 'img',
27417                                     cls : 'roo-document-viewer-image'
27418                                 }
27419                             ]
27420                         }
27421                     ]
27422                 },
27423                 {
27424                     tag : 'div',
27425                     cls : 'roo-document-viewer-footer',
27426                     cn : {
27427                         tag : 'div',
27428                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27429                         cn : [
27430                             {
27431                                 tag : 'div',
27432                                 cls : 'btn-group',
27433                                 cn : [
27434                                     {
27435                                         tag : 'button',
27436                                         cls : 'btn btn-default roo-document-viewer-trash',
27437                                         html : '<i class="fa fa-trash"></i>'
27438                                     }
27439                                 ]
27440                             }
27441                         ]
27442                     }
27443                 }
27444             ]
27445         };
27446         
27447         return cfg;
27448     },
27449     
27450     initEvents : function()
27451     {
27452         
27453         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27454         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27455         
27456         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27457         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27458         
27459         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27460         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27461         
27462         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27463         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27464         
27465         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27466         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27467         
27468         this.bodyEl.on('click', this.onClick, this);
27469         
27470         this.trashBtn.on('click', this.onTrash, this);
27471         
27472     },
27473     
27474     initial : function()
27475     {
27476 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27477         
27478         
27479         this.fireEvent('initial', this);
27480         
27481     },
27482     
27483     onClick : function(e)
27484     {
27485         e.preventDefault();
27486         
27487         this.fireEvent('click', this);
27488     },
27489     
27490     onTrash : function(e)
27491     {
27492         e.preventDefault();
27493         
27494         this.fireEvent('trash', this);
27495     }
27496     
27497 });
27498 /*
27499  * - LGPL
27500  *
27501  * nav progress bar
27502  * 
27503  */
27504
27505 /**
27506  * @class Roo.bootstrap.NavProgressBar
27507  * @extends Roo.bootstrap.Component
27508  * Bootstrap NavProgressBar class
27509  * 
27510  * @constructor
27511  * Create a new nav progress bar
27512  * @param {Object} config The config object
27513  */
27514
27515 Roo.bootstrap.NavProgressBar = function(config){
27516     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27517
27518     this.bullets = this.bullets || [];
27519    
27520 //    Roo.bootstrap.NavProgressBar.register(this);
27521      this.addEvents({
27522         /**
27523              * @event changed
27524              * Fires when the active item changes
27525              * @param {Roo.bootstrap.NavProgressBar} this
27526              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27527              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27528          */
27529         'changed': true
27530      });
27531     
27532 };
27533
27534 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27535     
27536     bullets : [],
27537     barItems : [],
27538     
27539     getAutoCreate : function()
27540     {
27541         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27542         
27543         cfg = {
27544             tag : 'div',
27545             cls : 'roo-navigation-bar-group',
27546             cn : [
27547                 {
27548                     tag : 'div',
27549                     cls : 'roo-navigation-top-bar'
27550                 },
27551                 {
27552                     tag : 'div',
27553                     cls : 'roo-navigation-bullets-bar',
27554                     cn : [
27555                         {
27556                             tag : 'ul',
27557                             cls : 'roo-navigation-bar'
27558                         }
27559                     ]
27560                 },
27561                 
27562                 {
27563                     tag : 'div',
27564                     cls : 'roo-navigation-bottom-bar'
27565                 }
27566             ]
27567             
27568         };
27569         
27570         return cfg;
27571         
27572     },
27573     
27574     initEvents: function() 
27575     {
27576         
27577     },
27578     
27579     onRender : function(ct, position) 
27580     {
27581         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27582         
27583         if(this.bullets.length){
27584             Roo.each(this.bullets, function(b){
27585                this.addItem(b);
27586             }, this);
27587         }
27588         
27589         this.format();
27590         
27591     },
27592     
27593     addItem : function(cfg)
27594     {
27595         var item = new Roo.bootstrap.NavProgressItem(cfg);
27596         
27597         item.parentId = this.id;
27598         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27599         
27600         if(cfg.html){
27601             var top = new Roo.bootstrap.Element({
27602                 tag : 'div',
27603                 cls : 'roo-navigation-bar-text'
27604             });
27605             
27606             var bottom = new Roo.bootstrap.Element({
27607                 tag : 'div',
27608                 cls : 'roo-navigation-bar-text'
27609             });
27610             
27611             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27612             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27613             
27614             var topText = new Roo.bootstrap.Element({
27615                 tag : 'span',
27616                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27617             });
27618             
27619             var bottomText = new Roo.bootstrap.Element({
27620                 tag : 'span',
27621                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27622             });
27623             
27624             topText.onRender(top.el, null);
27625             bottomText.onRender(bottom.el, null);
27626             
27627             item.topEl = top;
27628             item.bottomEl = bottom;
27629         }
27630         
27631         this.barItems.push(item);
27632         
27633         return item;
27634     },
27635     
27636     getActive : function()
27637     {
27638         var active = false;
27639         
27640         Roo.each(this.barItems, function(v){
27641             
27642             if (!v.isActive()) {
27643                 return;
27644             }
27645             
27646             active = v;
27647             return false;
27648             
27649         });
27650         
27651         return active;
27652     },
27653     
27654     setActiveItem : function(item)
27655     {
27656         var prev = false;
27657         
27658         Roo.each(this.barItems, function(v){
27659             if (v.rid == item.rid) {
27660                 return ;
27661             }
27662             
27663             if (v.isActive()) {
27664                 v.setActive(false);
27665                 prev = v;
27666             }
27667         });
27668
27669         item.setActive(true);
27670         
27671         this.fireEvent('changed', this, item, prev);
27672     },
27673     
27674     getBarItem: function(rid)
27675     {
27676         var ret = false;
27677         
27678         Roo.each(this.barItems, function(e) {
27679             if (e.rid != rid) {
27680                 return;
27681             }
27682             
27683             ret =  e;
27684             return false;
27685         });
27686         
27687         return ret;
27688     },
27689     
27690     indexOfItem : function(item)
27691     {
27692         var index = false;
27693         
27694         Roo.each(this.barItems, function(v, i){
27695             
27696             if (v.rid != item.rid) {
27697                 return;
27698             }
27699             
27700             index = i;
27701             return false
27702         });
27703         
27704         return index;
27705     },
27706     
27707     setActiveNext : function()
27708     {
27709         var i = this.indexOfItem(this.getActive());
27710         
27711         if (i > this.barItems.length) {
27712             return;
27713         }
27714         
27715         this.setActiveItem(this.barItems[i+1]);
27716     },
27717     
27718     setActivePrev : function()
27719     {
27720         var i = this.indexOfItem(this.getActive());
27721         
27722         if (i  < 1) {
27723             return;
27724         }
27725         
27726         this.setActiveItem(this.barItems[i-1]);
27727     },
27728     
27729     format : function()
27730     {
27731         if(!this.barItems.length){
27732             return;
27733         }
27734      
27735         var width = 100 / this.barItems.length;
27736         
27737         Roo.each(this.barItems, function(i){
27738             i.el.setStyle('width', width + '%');
27739             i.topEl.el.setStyle('width', width + '%');
27740             i.bottomEl.el.setStyle('width', width + '%');
27741         }, this);
27742         
27743     }
27744     
27745 });
27746 /*
27747  * - LGPL
27748  *
27749  * Nav Progress Item
27750  * 
27751  */
27752
27753 /**
27754  * @class Roo.bootstrap.NavProgressItem
27755  * @extends Roo.bootstrap.Component
27756  * Bootstrap NavProgressItem class
27757  * @cfg {String} rid the reference id
27758  * @cfg {Boolean} active (true|false) Is item active default false
27759  * @cfg {Boolean} disabled (true|false) Is item active default false
27760  * @cfg {String} html
27761  * @cfg {String} position (top|bottom) text position default bottom
27762  * @cfg {String} icon show icon instead of number
27763  * 
27764  * @constructor
27765  * Create a new NavProgressItem
27766  * @param {Object} config The config object
27767  */
27768 Roo.bootstrap.NavProgressItem = function(config){
27769     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27770     this.addEvents({
27771         // raw events
27772         /**
27773          * @event click
27774          * The raw click event for the entire grid.
27775          * @param {Roo.bootstrap.NavProgressItem} this
27776          * @param {Roo.EventObject} e
27777          */
27778         "click" : true
27779     });
27780    
27781 };
27782
27783 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27784     
27785     rid : '',
27786     active : false,
27787     disabled : false,
27788     html : '',
27789     position : 'bottom',
27790     icon : false,
27791     
27792     getAutoCreate : function()
27793     {
27794         var iconCls = 'roo-navigation-bar-item-icon';
27795         
27796         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27797         
27798         var cfg = {
27799             tag: 'li',
27800             cls: 'roo-navigation-bar-item',
27801             cn : [
27802                 {
27803                     tag : 'i',
27804                     cls : iconCls
27805                 }
27806             ]
27807         };
27808         
27809         if(this.active){
27810             cfg.cls += ' active';
27811         }
27812         if(this.disabled){
27813             cfg.cls += ' disabled';
27814         }
27815         
27816         return cfg;
27817     },
27818     
27819     disable : function()
27820     {
27821         this.setDisabled(true);
27822     },
27823     
27824     enable : function()
27825     {
27826         this.setDisabled(false);
27827     },
27828     
27829     initEvents: function() 
27830     {
27831         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27832         
27833         this.iconEl.on('click', this.onClick, this);
27834     },
27835     
27836     onClick : function(e)
27837     {
27838         e.preventDefault();
27839         
27840         if(this.disabled){
27841             return;
27842         }
27843         
27844         if(this.fireEvent('click', this, e) === false){
27845             return;
27846         };
27847         
27848         this.parent().setActiveItem(this);
27849     },
27850     
27851     isActive: function () 
27852     {
27853         return this.active;
27854     },
27855     
27856     setActive : function(state)
27857     {
27858         if(this.active == state){
27859             return;
27860         }
27861         
27862         this.active = state;
27863         
27864         if (state) {
27865             this.el.addClass('active');
27866             return;
27867         }
27868         
27869         this.el.removeClass('active');
27870         
27871         return;
27872     },
27873     
27874     setDisabled : function(state)
27875     {
27876         if(this.disabled == state){
27877             return;
27878         }
27879         
27880         this.disabled = state;
27881         
27882         if (state) {
27883             this.el.addClass('disabled');
27884             return;
27885         }
27886         
27887         this.el.removeClass('disabled');
27888     },
27889     
27890     tooltipEl : function()
27891     {
27892         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27893     }
27894 });
27895  
27896
27897  /*
27898  * - LGPL
27899  *
27900  * FieldLabel
27901  * 
27902  */
27903
27904 /**
27905  * @class Roo.bootstrap.FieldLabel
27906  * @extends Roo.bootstrap.Component
27907  * Bootstrap FieldLabel class
27908  * @cfg {String} html contents of the element
27909  * @cfg {String} tag tag of the element default label
27910  * @cfg {String} cls class of the element
27911  * @cfg {String} target label target 
27912  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27913  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27914  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27915  * @cfg {String} iconTooltip default "This field is required"
27916  * 
27917  * @constructor
27918  * Create a new FieldLabel
27919  * @param {Object} config The config object
27920  */
27921
27922 Roo.bootstrap.FieldLabel = function(config){
27923     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27924     
27925     this.addEvents({
27926             /**
27927              * @event invalid
27928              * Fires after the field has been marked as invalid.
27929              * @param {Roo.form.FieldLabel} this
27930              * @param {String} msg The validation message
27931              */
27932             invalid : true,
27933             /**
27934              * @event valid
27935              * Fires after the field has been validated with no errors.
27936              * @param {Roo.form.FieldLabel} this
27937              */
27938             valid : true
27939         });
27940 };
27941
27942 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27943     
27944     tag: 'label',
27945     cls: '',
27946     html: '',
27947     target: '',
27948     allowBlank : true,
27949     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27950     validClass : 'text-success fa fa-lg fa-check',
27951     iconTooltip : 'This field is required',
27952     
27953     getAutoCreate : function(){
27954         
27955         var cfg = {
27956             tag : this.tag,
27957             cls : 'roo-bootstrap-field-label ' + this.cls,
27958             for : this.target,
27959             cn : [
27960                 {
27961                     tag : 'i',
27962                     cls : '',
27963                     tooltip : this.iconTooltip
27964                 },
27965                 {
27966                     tag : 'span',
27967                     html : this.html
27968                 }
27969             ] 
27970         };
27971         
27972         return cfg;
27973     },
27974     
27975     initEvents: function() 
27976     {
27977         Roo.bootstrap.Element.superclass.initEvents.call(this);
27978         
27979         this.iconEl = this.el.select('i', true).first();
27980         
27981         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27982         
27983         Roo.bootstrap.FieldLabel.register(this);
27984     },
27985     
27986     /**
27987      * Mark this field as valid
27988      */
27989     markValid : function()
27990     {
27991         this.iconEl.show();
27992         
27993         this.iconEl.removeClass(this.invalidClass);
27994         
27995         this.iconEl.addClass(this.validClass);
27996         
27997         this.fireEvent('valid', this);
27998     },
27999     
28000     /**
28001      * Mark this field as invalid
28002      * @param {String} msg The validation message
28003      */
28004     markInvalid : function(msg)
28005     {
28006         this.iconEl.show();
28007         
28008         this.iconEl.removeClass(this.validClass);
28009         
28010         this.iconEl.addClass(this.invalidClass);
28011         
28012         this.fireEvent('invalid', this, msg);
28013     }
28014     
28015    
28016 });
28017
28018 Roo.apply(Roo.bootstrap.FieldLabel, {
28019     
28020     groups: {},
28021     
28022      /**
28023     * register a FieldLabel Group
28024     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28025     */
28026     register : function(label)
28027     {
28028         if(this.groups.hasOwnProperty(label.target)){
28029             return;
28030         }
28031      
28032         this.groups[label.target] = label;
28033         
28034     },
28035     /**
28036     * fetch a FieldLabel Group based on the target
28037     * @param {string} target
28038     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28039     */
28040     get: function(target) {
28041         if (typeof(this.groups[target]) == 'undefined') {
28042             return false;
28043         }
28044         
28045         return this.groups[target] ;
28046     }
28047 });
28048
28049  
28050
28051  /*
28052  * - LGPL
28053  *
28054  * page DateSplitField.
28055  * 
28056  */
28057
28058
28059 /**
28060  * @class Roo.bootstrap.DateSplitField
28061  * @extends Roo.bootstrap.Component
28062  * Bootstrap DateSplitField class
28063  * @cfg {string} fieldLabel - the label associated
28064  * @cfg {Number} labelWidth set the width of label (0-12)
28065  * @cfg {String} labelAlign (top|left)
28066  * @cfg {Boolean} dayAllowBlank (true|false) default false
28067  * @cfg {Boolean} monthAllowBlank (true|false) default false
28068  * @cfg {Boolean} yearAllowBlank (true|false) default false
28069  * @cfg {string} dayPlaceholder 
28070  * @cfg {string} monthPlaceholder
28071  * @cfg {string} yearPlaceholder
28072  * @cfg {string} dayFormat default 'd'
28073  * @cfg {string} monthFormat default 'm'
28074  * @cfg {string} yearFormat default 'Y'
28075
28076  *     
28077  * @constructor
28078  * Create a new DateSplitField
28079  * @param {Object} config The config object
28080  */
28081
28082 Roo.bootstrap.DateSplitField = function(config){
28083     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28084     
28085     this.addEvents({
28086         // raw events
28087          /**
28088          * @event years
28089          * getting the data of years
28090          * @param {Roo.bootstrap.DateSplitField} this
28091          * @param {Object} years
28092          */
28093         "years" : true,
28094         /**
28095          * @event days
28096          * getting the data of days
28097          * @param {Roo.bootstrap.DateSplitField} this
28098          * @param {Object} days
28099          */
28100         "days" : true,
28101         /**
28102          * @event invalid
28103          * Fires after the field has been marked as invalid.
28104          * @param {Roo.form.Field} this
28105          * @param {String} msg The validation message
28106          */
28107         invalid : true,
28108        /**
28109          * @event valid
28110          * Fires after the field has been validated with no errors.
28111          * @param {Roo.form.Field} this
28112          */
28113         valid : true
28114     });
28115 };
28116
28117 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28118     
28119     fieldLabel : '',
28120     labelAlign : 'top',
28121     labelWidth : 3,
28122     dayAllowBlank : false,
28123     monthAllowBlank : false,
28124     yearAllowBlank : false,
28125     dayPlaceholder : '',
28126     monthPlaceholder : '',
28127     yearPlaceholder : '',
28128     dayFormat : 'd',
28129     monthFormat : 'm',
28130     yearFormat : 'Y',
28131     isFormField : true,
28132     
28133     getAutoCreate : function()
28134     {
28135         var cfg = {
28136             tag : 'div',
28137             cls : 'row roo-date-split-field-group',
28138             cn : [
28139                 {
28140                     tag : 'input',
28141                     type : 'hidden',
28142                     cls : 'form-hidden-field roo-date-split-field-group-value',
28143                     name : this.name
28144                 }
28145             ]
28146         };
28147         
28148         if(this.fieldLabel){
28149             cfg.cn.push({
28150                 tag : 'div',
28151                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28152                 cn : [
28153                     {
28154                         tag : 'label',
28155                         html : this.fieldLabel
28156                     }
28157                 ]
28158             });
28159         }
28160         
28161         Roo.each(['day', 'month', 'year'], function(t){
28162             cfg.cn.push({
28163                 tag : 'div',
28164                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28165             });
28166         }, this);
28167         
28168         return cfg;
28169     },
28170     
28171     inputEl: function ()
28172     {
28173         return this.el.select('.roo-date-split-field-group-value', true).first();
28174     },
28175     
28176     onRender : function(ct, position) 
28177     {
28178         var _this = this;
28179         
28180         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28181         
28182         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28183         
28184         this.dayField = new Roo.bootstrap.ComboBox({
28185             allowBlank : this.dayAllowBlank,
28186             alwaysQuery : true,
28187             displayField : 'value',
28188             editable : false,
28189             fieldLabel : '',
28190             forceSelection : true,
28191             mode : 'local',
28192             placeholder : this.dayPlaceholder,
28193             selectOnFocus : true,
28194             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28195             triggerAction : 'all',
28196             typeAhead : true,
28197             valueField : 'value',
28198             store : new Roo.data.SimpleStore({
28199                 data : (function() {    
28200                     var days = [];
28201                     _this.fireEvent('days', _this, days);
28202                     return days;
28203                 })(),
28204                 fields : [ 'value' ]
28205             }),
28206             listeners : {
28207                 select : function (_self, record, index)
28208                 {
28209                     _this.setValue(_this.getValue());
28210                 }
28211             }
28212         });
28213
28214         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28215         
28216         this.monthField = new Roo.bootstrap.MonthField({
28217             after : '<i class=\"fa fa-calendar\"></i>',
28218             allowBlank : this.monthAllowBlank,
28219             placeholder : this.monthPlaceholder,
28220             readOnly : true,
28221             listeners : {
28222                 render : function (_self)
28223                 {
28224                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28225                         e.preventDefault();
28226                         _self.focus();
28227                     });
28228                 },
28229                 select : function (_self, oldvalue, newvalue)
28230                 {
28231                     _this.setValue(_this.getValue());
28232                 }
28233             }
28234         });
28235         
28236         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28237         
28238         this.yearField = new Roo.bootstrap.ComboBox({
28239             allowBlank : this.yearAllowBlank,
28240             alwaysQuery : true,
28241             displayField : 'value',
28242             editable : false,
28243             fieldLabel : '',
28244             forceSelection : true,
28245             mode : 'local',
28246             placeholder : this.yearPlaceholder,
28247             selectOnFocus : true,
28248             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28249             triggerAction : 'all',
28250             typeAhead : true,
28251             valueField : 'value',
28252             store : new Roo.data.SimpleStore({
28253                 data : (function() {
28254                     var years = [];
28255                     _this.fireEvent('years', _this, years);
28256                     return years;
28257                 })(),
28258                 fields : [ 'value' ]
28259             }),
28260             listeners : {
28261                 select : function (_self, record, index)
28262                 {
28263                     _this.setValue(_this.getValue());
28264                 }
28265             }
28266         });
28267
28268         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28269     },
28270     
28271     setValue : function(v, format)
28272     {
28273         this.inputEl.dom.value = v;
28274         
28275         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28276         
28277         var d = Date.parseDate(v, f);
28278         
28279         if(!d){
28280             this.validate();
28281             return;
28282         }
28283         
28284         this.setDay(d.format(this.dayFormat));
28285         this.setMonth(d.format(this.monthFormat));
28286         this.setYear(d.format(this.yearFormat));
28287         
28288         this.validate();
28289         
28290         return;
28291     },
28292     
28293     setDay : function(v)
28294     {
28295         this.dayField.setValue(v);
28296         this.inputEl.dom.value = this.getValue();
28297         this.validate();
28298         return;
28299     },
28300     
28301     setMonth : function(v)
28302     {
28303         this.monthField.setValue(v, true);
28304         this.inputEl.dom.value = this.getValue();
28305         this.validate();
28306         return;
28307     },
28308     
28309     setYear : function(v)
28310     {
28311         this.yearField.setValue(v);
28312         this.inputEl.dom.value = this.getValue();
28313         this.validate();
28314         return;
28315     },
28316     
28317     getDay : function()
28318     {
28319         return this.dayField.getValue();
28320     },
28321     
28322     getMonth : function()
28323     {
28324         return this.monthField.getValue();
28325     },
28326     
28327     getYear : function()
28328     {
28329         return this.yearField.getValue();
28330     },
28331     
28332     getValue : function()
28333     {
28334         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28335         
28336         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28337         
28338         return date;
28339     },
28340     
28341     reset : function()
28342     {
28343         this.setDay('');
28344         this.setMonth('');
28345         this.setYear('');
28346         this.inputEl.dom.value = '';
28347         this.validate();
28348         return;
28349     },
28350     
28351     validate : function()
28352     {
28353         var d = this.dayField.validate();
28354         var m = this.monthField.validate();
28355         var y = this.yearField.validate();
28356         
28357         var valid = true;
28358         
28359         if(
28360                 (!this.dayAllowBlank && !d) ||
28361                 (!this.monthAllowBlank && !m) ||
28362                 (!this.yearAllowBlank && !y)
28363         ){
28364             valid = false;
28365         }
28366         
28367         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28368             return valid;
28369         }
28370         
28371         if(valid){
28372             this.markValid();
28373             return valid;
28374         }
28375         
28376         this.markInvalid();
28377         
28378         return valid;
28379     },
28380     
28381     markValid : function()
28382     {
28383         
28384         var label = this.el.select('label', true).first();
28385         var icon = this.el.select('i.fa-star', true).first();
28386
28387         if(label && icon){
28388             icon.remove();
28389         }
28390         
28391         this.fireEvent('valid', this);
28392     },
28393     
28394      /**
28395      * Mark this field as invalid
28396      * @param {String} msg The validation message
28397      */
28398     markInvalid : function(msg)
28399     {
28400         
28401         var label = this.el.select('label', true).first();
28402         var icon = this.el.select('i.fa-star', true).first();
28403
28404         if(label && !icon){
28405             this.el.select('.roo-date-split-field-label', true).createChild({
28406                 tag : 'i',
28407                 cls : 'text-danger fa fa-lg fa-star',
28408                 tooltip : 'This field is required',
28409                 style : 'margin-right:5px;'
28410             }, label, true);
28411         }
28412         
28413         this.fireEvent('invalid', this, msg);
28414     },
28415     
28416     clearInvalid : function()
28417     {
28418         var label = this.el.select('label', true).first();
28419         var icon = this.el.select('i.fa-star', true).first();
28420
28421         if(label && icon){
28422             icon.remove();
28423         }
28424         
28425         this.fireEvent('valid', this);
28426     },
28427     
28428     getName: function()
28429     {
28430         return this.name;
28431     }
28432     
28433 });
28434
28435  /**
28436  *
28437  * This is based on 
28438  * http://masonry.desandro.com
28439  *
28440  * The idea is to render all the bricks based on vertical width...
28441  *
28442  * The original code extends 'outlayer' - we might need to use that....
28443  * 
28444  */
28445
28446
28447 /**
28448  * @class Roo.bootstrap.LayoutMasonry
28449  * @extends Roo.bootstrap.Component
28450  * Bootstrap Layout Masonry class
28451  * 
28452  * @constructor
28453  * Create a new Element
28454  * @param {Object} config The config object
28455  */
28456
28457 Roo.bootstrap.LayoutMasonry = function(config){
28458     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28459     
28460     this.bricks = [];
28461     
28462 };
28463
28464 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28465     
28466     /**
28467      * @cfg {Boolean} isLayoutInstant = no animation?
28468      */   
28469     isLayoutInstant : false, // needed?
28470    
28471     /**
28472      * @cfg {Number} boxWidth  width of the columns
28473      */   
28474     boxWidth : 450,
28475     
28476       /**
28477      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28478      */   
28479     boxHeight : 0,
28480     
28481     /**
28482      * @cfg {Number} padWidth padding below box..
28483      */   
28484     padWidth : 10, 
28485     
28486     /**
28487      * @cfg {Number} gutter gutter width..
28488      */   
28489     gutter : 10,
28490     
28491      /**
28492      * @cfg {Number} maxCols maximum number of columns
28493      */   
28494     
28495     maxCols: 0,
28496     
28497     /**
28498      * @cfg {Boolean} isAutoInitial defalut true
28499      */   
28500     isAutoInitial : true, 
28501     
28502     containerWidth: 0,
28503     
28504     /**
28505      * @cfg {Boolean} isHorizontal defalut false
28506      */   
28507     isHorizontal : false, 
28508
28509     currentSize : null,
28510     
28511     tag: 'div',
28512     
28513     cls: '',
28514     
28515     bricks: null, //CompositeElement
28516     
28517     cols : 1,
28518     
28519     _isLayoutInited : false,
28520     
28521 //    isAlternative : false, // only use for vertical layout...
28522     
28523     /**
28524      * @cfg {Number} alternativePadWidth padding below box..
28525      */   
28526     alternativePadWidth : 50, 
28527     
28528     getAutoCreate : function(){
28529         
28530         var cfg = {
28531             tag: this.tag,
28532             cls: 'blog-masonary-wrapper ' + this.cls,
28533             cn : {
28534                 cls : 'mas-boxes masonary'
28535             }
28536         };
28537         
28538         return cfg;
28539     },
28540     
28541     getChildContainer: function( )
28542     {
28543         if (this.boxesEl) {
28544             return this.boxesEl;
28545         }
28546         
28547         this.boxesEl = this.el.select('.mas-boxes').first();
28548         
28549         return this.boxesEl;
28550     },
28551     
28552     
28553     initEvents : function()
28554     {
28555         var _this = this;
28556         
28557         if(this.isAutoInitial){
28558             Roo.log('hook children rendered');
28559             this.on('childrenrendered', function() {
28560                 Roo.log('children rendered');
28561                 _this.initial();
28562             } ,this);
28563         }
28564     },
28565     
28566     initial : function()
28567     {
28568         this.currentSize = this.el.getBox(true);
28569         
28570         Roo.EventManager.onWindowResize(this.resize, this); 
28571
28572         if(!this.isAutoInitial){
28573             this.layout();
28574             return;
28575         }
28576         
28577         this.layout();
28578         
28579         return;
28580         //this.layout.defer(500,this);
28581         
28582     },
28583     
28584     resize : function()
28585     {
28586         Roo.log('resize');
28587         
28588         var cs = this.el.getBox(true);
28589         
28590         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28591             Roo.log("no change in with or X");
28592             return;
28593         }
28594         
28595         this.currentSize = cs;
28596         
28597         this.layout();
28598         
28599     },
28600     
28601     layout : function()
28602     {   
28603         this._resetLayout();
28604         
28605         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28606         
28607         this.layoutItems( isInstant );
28608       
28609         this._isLayoutInited = true;
28610         
28611     },
28612     
28613     _resetLayout : function()
28614     {
28615         if(this.isHorizontal){
28616             this.horizontalMeasureColumns();
28617             return;
28618         }
28619         
28620         this.verticalMeasureColumns();
28621         
28622     },
28623     
28624     verticalMeasureColumns : function()
28625     {
28626         this.getContainerWidth();
28627         
28628 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28629 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28630 //            return;
28631 //        }
28632         
28633         var boxWidth = this.boxWidth + this.padWidth;
28634         
28635         if(this.containerWidth < this.boxWidth){
28636             boxWidth = this.containerWidth
28637         }
28638         
28639         var containerWidth = this.containerWidth;
28640         
28641         var cols = Math.floor(containerWidth / boxWidth);
28642         
28643         this.cols = Math.max( cols, 1 );
28644         
28645         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28646         
28647         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28648         
28649         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28650         
28651         this.colWidth = boxWidth + avail - this.padWidth;
28652         
28653         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28654         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28655     },
28656     
28657     horizontalMeasureColumns : function()
28658     {
28659         this.getContainerWidth();
28660         
28661         var boxWidth = this.boxWidth;
28662         
28663         if(this.containerWidth < boxWidth){
28664             boxWidth = this.containerWidth;
28665         }
28666         
28667         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28668         
28669         this.el.setHeight(boxWidth);
28670         
28671     },
28672     
28673     getContainerWidth : function()
28674     {
28675         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28676     },
28677     
28678     layoutItems : function( isInstant )
28679     {
28680         var items = Roo.apply([], this.bricks);
28681         
28682         if(this.isHorizontal){
28683             this._horizontalLayoutItems( items , isInstant );
28684             return;
28685         }
28686         
28687 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28688 //            this._verticalAlternativeLayoutItems( items , isInstant );
28689 //            return;
28690 //        }
28691         
28692         this._verticalLayoutItems( items , isInstant );
28693         
28694     },
28695     
28696     _verticalLayoutItems : function ( items , isInstant)
28697     {
28698         if ( !items || !items.length ) {
28699             return;
28700         }
28701         
28702         var standard = [
28703             ['xs', 'xs', 'xs', 'tall'],
28704             ['xs', 'xs', 'tall'],
28705             ['xs', 'xs', 'sm'],
28706             ['xs', 'xs', 'xs'],
28707             ['xs', 'tall'],
28708             ['xs', 'sm'],
28709             ['xs', 'xs'],
28710             ['xs'],
28711             
28712             ['sm', 'xs', 'xs'],
28713             ['sm', 'xs'],
28714             ['sm'],
28715             
28716             ['tall', 'xs', 'xs', 'xs'],
28717             ['tall', 'xs', 'xs'],
28718             ['tall', 'xs'],
28719             ['tall']
28720             
28721         ];
28722         
28723         var queue = [];
28724         
28725         var boxes = [];
28726         
28727         var box = [];
28728         
28729         Roo.each(items, function(item, k){
28730             
28731             switch (item.size) {
28732                 // these layouts take up a full box,
28733                 case 'md' :
28734                 case 'md-left' :
28735                 case 'md-right' :
28736                 case 'wide' :
28737                     
28738                     if(box.length){
28739                         boxes.push(box);
28740                         box = [];
28741                     }
28742                     
28743                     boxes.push([item]);
28744                     
28745                     break;
28746                     
28747                 case 'xs' :
28748                 case 'sm' :
28749                 case 'tall' :
28750                     
28751                     box.push(item);
28752                     
28753                     break;
28754                 default :
28755                     break;
28756                     
28757             }
28758             
28759         }, this);
28760         
28761         if(box.length){
28762             boxes.push(box);
28763             box = [];
28764         }
28765         
28766         var filterPattern = function(box, length)
28767         {
28768             if(!box.length){
28769                 return;
28770             }
28771             
28772             var match = false;
28773             
28774             var pattern = box.slice(0, length);
28775             
28776             var format = [];
28777             
28778             Roo.each(pattern, function(i){
28779                 format.push(i.size);
28780             }, this);
28781             
28782             Roo.each(standard, function(s){
28783                 
28784                 if(String(s) != String(format)){
28785                     return;
28786                 }
28787                 
28788                 match = true;
28789                 return false;
28790                 
28791             }, this);
28792             
28793             if(!match && length == 1){
28794                 return;
28795             }
28796             
28797             if(!match){
28798                 filterPattern(box, length - 1);
28799                 return;
28800             }
28801                 
28802             queue.push(pattern);
28803
28804             box = box.slice(length, box.length);
28805
28806             filterPattern(box, 4);
28807
28808             return;
28809             
28810         }
28811         
28812         Roo.each(boxes, function(box, k){
28813             
28814             if(!box.length){
28815                 return;
28816             }
28817             
28818             if(box.length == 1){
28819                 queue.push(box);
28820                 return;
28821             }
28822             
28823             filterPattern(box, 4);
28824             
28825         }, this);
28826         
28827         this._processVerticalLayoutQueue( queue, isInstant );
28828         
28829     },
28830     
28831 //    _verticalAlternativeLayoutItems : function( items , isInstant )
28832 //    {
28833 //        if ( !items || !items.length ) {
28834 //            return;
28835 //        }
28836 //
28837 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
28838 //        
28839 //    },
28840     
28841     _horizontalLayoutItems : function ( items , isInstant)
28842     {
28843         if ( !items || !items.length || items.length < 3) {
28844             return;
28845         }
28846         
28847         items.reverse();
28848         
28849         var eItems = items.slice(0, 3);
28850         
28851         items = items.slice(3, items.length);
28852         
28853         var standard = [
28854             ['xs', 'xs', 'xs', 'wide'],
28855             ['xs', 'xs', 'wide'],
28856             ['xs', 'xs', 'sm'],
28857             ['xs', 'xs', 'xs'],
28858             ['xs', 'wide'],
28859             ['xs', 'sm'],
28860             ['xs', 'xs'],
28861             ['xs'],
28862             
28863             ['sm', 'xs', 'xs'],
28864             ['sm', 'xs'],
28865             ['sm'],
28866             
28867             ['wide', 'xs', 'xs', 'xs'],
28868             ['wide', 'xs', 'xs'],
28869             ['wide', 'xs'],
28870             ['wide'],
28871             
28872             ['wide-thin']
28873         ];
28874         
28875         var queue = [];
28876         
28877         var boxes = [];
28878         
28879         var box = [];
28880         
28881         Roo.each(items, function(item, k){
28882             
28883             switch (item.size) {
28884                 case 'md' :
28885                 case 'md-left' :
28886                 case 'md-right' :
28887                 case 'tall' :
28888                     
28889                     if(box.length){
28890                         boxes.push(box);
28891                         box = [];
28892                     }
28893                     
28894                     boxes.push([item]);
28895                     
28896                     break;
28897                     
28898                 case 'xs' :
28899                 case 'sm' :
28900                 case 'wide' :
28901                 case 'wide-thin' :
28902                     
28903                     box.push(item);
28904                     
28905                     break;
28906                 default :
28907                     break;
28908                     
28909             }
28910             
28911         }, this);
28912         
28913         if(box.length){
28914             boxes.push(box);
28915             box = [];
28916         }
28917         
28918         var filterPattern = function(box, length)
28919         {
28920             if(!box.length){
28921                 return;
28922             }
28923             
28924             var match = false;
28925             
28926             var pattern = box.slice(0, length);
28927             
28928             var format = [];
28929             
28930             Roo.each(pattern, function(i){
28931                 format.push(i.size);
28932             }, this);
28933             
28934             Roo.each(standard, function(s){
28935                 
28936                 if(String(s) != String(format)){
28937                     return;
28938                 }
28939                 
28940                 match = true;
28941                 return false;
28942                 
28943             }, this);
28944             
28945             if(!match && length == 1){
28946                 return;
28947             }
28948             
28949             if(!match){
28950                 filterPattern(box, length - 1);
28951                 return;
28952             }
28953                 
28954             queue.push(pattern);
28955
28956             box = box.slice(length, box.length);
28957
28958             filterPattern(box, 4);
28959
28960             return;
28961             
28962         }
28963         
28964         Roo.each(boxes, function(box, k){
28965             
28966             if(!box.length){
28967                 return;
28968             }
28969             
28970             if(box.length == 1){
28971                 queue.push(box);
28972                 return;
28973             }
28974             
28975             filterPattern(box, 4);
28976             
28977         }, this);
28978         
28979         
28980         var prune = [];
28981         
28982         var pos = this.el.getBox(true);
28983         
28984         var minX = pos.x;
28985         
28986         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
28987         
28988         var hit_end = false;
28989         
28990         Roo.each(queue, function(box){
28991             
28992             if(hit_end){
28993                 
28994                 Roo.each(box, function(b){
28995                 
28996                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
28997                     b.el.hide();
28998
28999                 }, this);
29000
29001                 return;
29002             }
29003             
29004             var mx = 0;
29005             
29006             Roo.each(box, function(b){
29007                 
29008                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29009                 b.el.show();
29010
29011                 mx = Math.max(mx, b.x);
29012                 
29013             }, this);
29014             
29015             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29016             
29017             if(maxX < minX){
29018                 
29019                 Roo.each(box, function(b){
29020                 
29021                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29022                     b.el.hide();
29023                     
29024                 }, this);
29025                 
29026                 hit_end = true;
29027                 
29028                 return;
29029             }
29030             
29031             prune.push(box);
29032             
29033         }, this);
29034         
29035         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29036     },
29037     
29038     /** Sets position of item in DOM
29039     * @param {Element} item
29040     * @param {Number} x - horizontal position
29041     * @param {Number} y - vertical position
29042     * @param {Boolean} isInstant - disables transitions
29043     */
29044     _processVerticalLayoutQueue : function( queue, isInstant )
29045     {
29046         var pos = this.el.getBox(true);
29047         var x = pos.x;
29048         var y = pos.y;
29049         var maxY = [];
29050         
29051         for (var i = 0; i < this.cols; i++){
29052             maxY[i] = pos.y;
29053         }
29054         
29055         Roo.each(queue, function(box, k){
29056             
29057             var col = k % this.cols;
29058             
29059             Roo.each(box, function(b,kk){
29060                 
29061                 b.el.position('absolute');
29062                 
29063                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29064                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29065                 
29066                 if(b.size == 'md-left' || b.size == 'md-right'){
29067                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29068                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29069                 }
29070                 
29071                 b.el.setWidth(width);
29072                 b.el.setHeight(height);
29073                 // iframe?
29074                 b.el.select('iframe',true).setSize(width,height);
29075                 
29076             }, this);
29077             
29078             for (var i = 0; i < this.cols; i++){
29079                 
29080                 if(maxY[i] < maxY[col]){
29081                     col = i;
29082                     continue;
29083                 }
29084                 
29085                 col = Math.min(col, i);
29086                 
29087             }
29088             
29089             x = pos.x + col * (this.colWidth + this.padWidth);
29090             
29091             y = maxY[col];
29092             
29093             var positions = [];
29094             
29095             switch (box.length){
29096                 case 1 :
29097                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29098                     break;
29099                 case 2 :
29100                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29101                     break;
29102                 case 3 :
29103                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29104                     break;
29105                 case 4 :
29106                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29107                     break;
29108                 default :
29109                     break;
29110             }
29111             
29112             Roo.each(box, function(b,kk){
29113                 
29114                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29115                 
29116                 var sz = b.el.getSize();
29117                 
29118                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29119                 
29120             }, this);
29121             
29122         }, this);
29123         
29124         var mY = 0;
29125         
29126         for (var i = 0; i < this.cols; i++){
29127             mY = Math.max(mY, maxY[i]);
29128         }
29129         
29130         this.el.setHeight(mY - pos.y);
29131         
29132     },
29133     
29134 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29135 //    {
29136 //        var pos = this.el.getBox(true);
29137 //        var x = pos.x;
29138 //        var y = pos.y;
29139 //        var maxX = pos.right;
29140 //        
29141 //        var maxHeight = 0;
29142 //        
29143 //        Roo.each(items, function(item, k){
29144 //            
29145 //            var c = k % 2;
29146 //            
29147 //            item.el.position('absolute');
29148 //                
29149 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29150 //
29151 //            item.el.setWidth(width);
29152 //
29153 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29154 //
29155 //            item.el.setHeight(height);
29156 //            
29157 //            if(c == 0){
29158 //                item.el.setXY([x, y], isInstant ? false : true);
29159 //            } else {
29160 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29161 //            }
29162 //            
29163 //            y = y + height + this.alternativePadWidth;
29164 //            
29165 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29166 //            
29167 //        }, this);
29168 //        
29169 //        this.el.setHeight(maxHeight);
29170 //        
29171 //    },
29172     
29173     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29174     {
29175         var pos = this.el.getBox(true);
29176         
29177         var minX = pos.x;
29178         var minY = pos.y;
29179         
29180         var maxX = pos.right;
29181         
29182         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29183         
29184         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29185         
29186         Roo.each(queue, function(box, k){
29187             
29188             Roo.each(box, function(b, kk){
29189                 
29190                 b.el.position('absolute');
29191                 
29192                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29193                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29194                 
29195                 if(b.size == 'md-left' || b.size == 'md-right'){
29196                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29197                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29198                 }
29199                 
29200                 b.el.setWidth(width);
29201                 b.el.setHeight(height);
29202                 
29203             }, this);
29204             
29205             if(!box.length){
29206                 return;
29207             }
29208             
29209             var positions = [];
29210             
29211             switch (box.length){
29212                 case 1 :
29213                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29214                     break;
29215                 case 2 :
29216                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29217                     break;
29218                 case 3 :
29219                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29220                     break;
29221                 case 4 :
29222                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29223                     break;
29224                 default :
29225                     break;
29226             }
29227             
29228             Roo.each(box, function(b,kk){
29229                 
29230                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29231                 
29232                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29233                 
29234             }, this);
29235             
29236         }, this);
29237         
29238     },
29239     
29240     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29241     {
29242         Roo.each(eItems, function(b,k){
29243             
29244             b.size = (k == 0) ? 'sm' : 'xs';
29245             b.x = (k == 0) ? 2 : 1;
29246             b.y = (k == 0) ? 2 : 1;
29247             
29248             b.el.position('absolute');
29249             
29250             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29251                 
29252             b.el.setWidth(width);
29253             
29254             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29255             
29256             b.el.setHeight(height);
29257             
29258         }, this);
29259
29260         var positions = [];
29261         
29262         positions.push({
29263             x : maxX - this.unitWidth * 2 - this.gutter,
29264             y : minY
29265         });
29266         
29267         positions.push({
29268             x : maxX - this.unitWidth,
29269             y : minY + (this.unitWidth + this.gutter) * 2
29270         });
29271         
29272         positions.push({
29273             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29274             y : minY
29275         });
29276         
29277         Roo.each(eItems, function(b,k){
29278             
29279             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29280
29281         }, this);
29282         
29283     },
29284     
29285     getVerticalOneBoxColPositions : function(x, y, box)
29286     {
29287         var pos = [];
29288         
29289         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29290         
29291         if(box[0].size == 'md-left'){
29292             rand = 0;
29293         }
29294         
29295         if(box[0].size == 'md-right'){
29296             rand = 1;
29297         }
29298         
29299         pos.push({
29300             x : x + (this.unitWidth + this.gutter) * rand,
29301             y : y
29302         });
29303         
29304         return pos;
29305     },
29306     
29307     getVerticalTwoBoxColPositions : function(x, y, box)
29308     {
29309         var pos = [];
29310         
29311         if(box[0].size == 'xs'){
29312             
29313             pos.push({
29314                 x : x,
29315                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29316             });
29317
29318             pos.push({
29319                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29320                 y : y
29321             });
29322             
29323             return pos;
29324             
29325         }
29326         
29327         pos.push({
29328             x : x,
29329             y : y
29330         });
29331
29332         pos.push({
29333             x : x + (this.unitWidth + this.gutter) * 2,
29334             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29335         });
29336         
29337         return pos;
29338         
29339     },
29340     
29341     getVerticalThreeBoxColPositions : function(x, y, box)
29342     {
29343         var pos = [];
29344         
29345         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29346             
29347             pos.push({
29348                 x : x,
29349                 y : y
29350             });
29351
29352             pos.push({
29353                 x : x + (this.unitWidth + this.gutter) * 1,
29354                 y : y
29355             });
29356             
29357             pos.push({
29358                 x : x + (this.unitWidth + this.gutter) * 2,
29359                 y : y
29360             });
29361             
29362             return pos;
29363             
29364         }
29365         
29366         if(box[0].size == 'xs' && box[1].size == 'xs'){
29367             
29368             pos.push({
29369                 x : x,
29370                 y : y
29371             });
29372
29373             pos.push({
29374                 x : x,
29375                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29376             });
29377             
29378             pos.push({
29379                 x : x + (this.unitWidth + this.gutter) * 1,
29380                 y : y
29381             });
29382             
29383             return pos;
29384             
29385         }
29386         
29387         pos.push({
29388             x : x,
29389             y : y
29390         });
29391
29392         pos.push({
29393             x : x + (this.unitWidth + this.gutter) * 2,
29394             y : y
29395         });
29396
29397         pos.push({
29398             x : x + (this.unitWidth + this.gutter) * 2,
29399             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29400         });
29401             
29402         return pos;
29403         
29404     },
29405     
29406     getVerticalFourBoxColPositions : function(x, y, box)
29407     {
29408         var pos = [];
29409         
29410         if(box[0].size == 'xs'){
29411             
29412             pos.push({
29413                 x : x,
29414                 y : y
29415             });
29416
29417             pos.push({
29418                 x : x,
29419                 y : y + (this.unitHeight + this.gutter) * 1
29420             });
29421             
29422             pos.push({
29423                 x : x,
29424                 y : y + (this.unitHeight + this.gutter) * 2
29425             });
29426             
29427             pos.push({
29428                 x : x + (this.unitWidth + this.gutter) * 1,
29429                 y : y
29430             });
29431             
29432             return pos;
29433             
29434         }
29435         
29436         pos.push({
29437             x : x,
29438             y : y
29439         });
29440
29441         pos.push({
29442             x : x + (this.unitWidth + this.gutter) * 2,
29443             y : y
29444         });
29445
29446         pos.push({
29447             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29448             y : y + (this.unitHeight + this.gutter) * 1
29449         });
29450
29451         pos.push({
29452             x : x + (this.unitWidth + this.gutter) * 2,
29453             y : y + (this.unitWidth + this.gutter) * 2
29454         });
29455
29456         return pos;
29457         
29458     },
29459     
29460     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29461     {
29462         var pos = [];
29463         
29464         if(box[0].size == 'md-left'){
29465             pos.push({
29466                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29467                 y : minY
29468             });
29469             
29470             return pos;
29471         }
29472         
29473         if(box[0].size == 'md-right'){
29474             pos.push({
29475                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29476                 y : minY + (this.unitWidth + this.gutter) * 1
29477             });
29478             
29479             return pos;
29480         }
29481         
29482         var rand = Math.floor(Math.random() * (4 - box[0].y));
29483         
29484         pos.push({
29485             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29486             y : minY + (this.unitWidth + this.gutter) * rand
29487         });
29488         
29489         return pos;
29490         
29491     },
29492     
29493     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29494     {
29495         var pos = [];
29496         
29497         if(box[0].size == 'xs'){
29498             
29499             pos.push({
29500                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29501                 y : minY
29502             });
29503
29504             pos.push({
29505                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29506                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29507             });
29508             
29509             return pos;
29510             
29511         }
29512         
29513         pos.push({
29514             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29515             y : minY
29516         });
29517
29518         pos.push({
29519             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29520             y : minY + (this.unitWidth + this.gutter) * 2
29521         });
29522         
29523         return pos;
29524         
29525     },
29526     
29527     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29528     {
29529         var pos = [];
29530         
29531         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29532             
29533             pos.push({
29534                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29535                 y : minY
29536             });
29537
29538             pos.push({
29539                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29540                 y : minY + (this.unitWidth + this.gutter) * 1
29541             });
29542             
29543             pos.push({
29544                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29545                 y : minY + (this.unitWidth + this.gutter) * 2
29546             });
29547             
29548             return pos;
29549             
29550         }
29551         
29552         if(box[0].size == 'xs' && box[1].size == 'xs'){
29553             
29554             pos.push({
29555                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29556                 y : minY
29557             });
29558
29559             pos.push({
29560                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29561                 y : minY
29562             });
29563             
29564             pos.push({
29565                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29566                 y : minY + (this.unitWidth + this.gutter) * 1
29567             });
29568             
29569             return pos;
29570             
29571         }
29572         
29573         pos.push({
29574             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29575             y : minY
29576         });
29577
29578         pos.push({
29579             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29580             y : minY + (this.unitWidth + this.gutter) * 2
29581         });
29582
29583         pos.push({
29584             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29585             y : minY + (this.unitWidth + this.gutter) * 2
29586         });
29587             
29588         return pos;
29589         
29590     },
29591     
29592     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29593     {
29594         var pos = [];
29595         
29596         if(box[0].size == 'xs'){
29597             
29598             pos.push({
29599                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29600                 y : minY
29601             });
29602
29603             pos.push({
29604                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29605                 y : minY
29606             });
29607             
29608             pos.push({
29609                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29610                 y : minY
29611             });
29612             
29613             pos.push({
29614                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29615                 y : minY + (this.unitWidth + this.gutter) * 1
29616             });
29617             
29618             return pos;
29619             
29620         }
29621         
29622         pos.push({
29623             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29624             y : minY
29625         });
29626         
29627         pos.push({
29628             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29629             y : minY + (this.unitWidth + this.gutter) * 2
29630         });
29631         
29632         pos.push({
29633             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29634             y : minY + (this.unitWidth + this.gutter) * 2
29635         });
29636         
29637         pos.push({
29638             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29639             y : minY + (this.unitWidth + this.gutter) * 2
29640         });
29641
29642         return pos;
29643         
29644     }
29645     
29646 });
29647
29648  
29649
29650  /**
29651  *
29652  * This is based on 
29653  * http://masonry.desandro.com
29654  *
29655  * The idea is to render all the bricks based on vertical width...
29656  *
29657  * The original code extends 'outlayer' - we might need to use that....
29658  * 
29659  */
29660
29661
29662 /**
29663  * @class Roo.bootstrap.LayoutMasonryAuto
29664  * @extends Roo.bootstrap.Component
29665  * Bootstrap Layout Masonry class
29666  * 
29667  * @constructor
29668  * Create a new Element
29669  * @param {Object} config The config object
29670  */
29671
29672 Roo.bootstrap.LayoutMasonryAuto = function(config){
29673     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29674 };
29675
29676 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
29677     
29678       /**
29679      * @cfg {Boolean} isFitWidth  - resize the width..
29680      */   
29681     isFitWidth : false,  // options..
29682     /**
29683      * @cfg {Boolean} isOriginLeft = left align?
29684      */   
29685     isOriginLeft : true,
29686     /**
29687      * @cfg {Boolean} isOriginTop = top align?
29688      */   
29689     isOriginTop : false,
29690     /**
29691      * @cfg {Boolean} isLayoutInstant = no animation?
29692      */   
29693     isLayoutInstant : false, // needed?
29694     /**
29695      * @cfg {Boolean} isResizingContainer = not sure if this is used..
29696      */   
29697     isResizingContainer : true,
29698     /**
29699      * @cfg {Number} columnWidth  width of the columns 
29700      */   
29701     
29702     columnWidth : 0,
29703     
29704     /**
29705      * @cfg {Number} maxCols maximum number of columns
29706      */   
29707     
29708     maxCols: 0,
29709     /**
29710      * @cfg {Number} padHeight padding below box..
29711      */   
29712     
29713     padHeight : 10, 
29714     
29715     /**
29716      * @cfg {Boolean} isAutoInitial defalut true
29717      */   
29718     
29719     isAutoInitial : true, 
29720     
29721     // private?
29722     gutter : 0,
29723     
29724     containerWidth: 0,
29725     initialColumnWidth : 0,
29726     currentSize : null,
29727     
29728     colYs : null, // array.
29729     maxY : 0,
29730     padWidth: 10,
29731     
29732     
29733     tag: 'div',
29734     cls: '',
29735     bricks: null, //CompositeElement
29736     cols : 0, // array?
29737     // element : null, // wrapped now this.el
29738     _isLayoutInited : null, 
29739     
29740     
29741     getAutoCreate : function(){
29742         
29743         var cfg = {
29744             tag: this.tag,
29745             cls: 'blog-masonary-wrapper ' + this.cls,
29746             cn : {
29747                 cls : 'mas-boxes masonary'
29748             }
29749         };
29750         
29751         return cfg;
29752     },
29753     
29754     getChildContainer: function( )
29755     {
29756         if (this.boxesEl) {
29757             return this.boxesEl;
29758         }
29759         
29760         this.boxesEl = this.el.select('.mas-boxes').first();
29761         
29762         return this.boxesEl;
29763     },
29764     
29765     
29766     initEvents : function()
29767     {
29768         var _this = this;
29769         
29770         if(this.isAutoInitial){
29771             Roo.log('hook children rendered');
29772             this.on('childrenrendered', function() {
29773                 Roo.log('children rendered');
29774                 _this.initial();
29775             } ,this);
29776         }
29777         
29778     },
29779     
29780     initial : function()
29781     {
29782         this.reloadItems();
29783
29784         this.currentSize = this.el.getBox(true);
29785
29786         /// was window resize... - let's see if this works..
29787         Roo.EventManager.onWindowResize(this.resize, this); 
29788
29789         if(!this.isAutoInitial){
29790             this.layout();
29791             return;
29792         }
29793         
29794         this.layout.defer(500,this);
29795     },
29796     
29797     reloadItems: function()
29798     {
29799         this.bricks = this.el.select('.masonry-brick', true);
29800         
29801         this.bricks.each(function(b) {
29802             //Roo.log(b.getSize());
29803             if (!b.attr('originalwidth')) {
29804                 b.attr('originalwidth',  b.getSize().width);
29805             }
29806             
29807         });
29808         
29809         Roo.log(this.bricks.elements.length);
29810     },
29811     
29812     resize : function()
29813     {
29814         Roo.log('resize');
29815         var cs = this.el.getBox(true);
29816         
29817         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29818             Roo.log("no change in with or X");
29819             return;
29820         }
29821         this.currentSize = cs;
29822         this.layout();
29823     },
29824     
29825     layout : function()
29826     {
29827          Roo.log('layout');
29828         this._resetLayout();
29829         //this._manageStamps();
29830       
29831         // don't animate first layout
29832         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29833         this.layoutItems( isInstant );
29834       
29835         // flag for initalized
29836         this._isLayoutInited = true;
29837     },
29838     
29839     layoutItems : function( isInstant )
29840     {
29841         //var items = this._getItemsForLayout( this.items );
29842         // original code supports filtering layout items.. we just ignore it..
29843         
29844         this._layoutItems( this.bricks , isInstant );
29845       
29846         this._postLayout();
29847     },
29848     _layoutItems : function ( items , isInstant)
29849     {
29850        //this.fireEvent( 'layout', this, items );
29851     
29852
29853         if ( !items || !items.elements.length ) {
29854           // no items, emit event with empty array
29855             return;
29856         }
29857
29858         var queue = [];
29859         items.each(function(item) {
29860             Roo.log("layout item");
29861             Roo.log(item);
29862             // get x/y object from method
29863             var position = this._getItemLayoutPosition( item );
29864             // enqueue
29865             position.item = item;
29866             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29867             queue.push( position );
29868         }, this);
29869       
29870         this._processLayoutQueue( queue );
29871     },
29872     /** Sets position of item in DOM
29873     * @param {Element} item
29874     * @param {Number} x - horizontal position
29875     * @param {Number} y - vertical position
29876     * @param {Boolean} isInstant - disables transitions
29877     */
29878     _processLayoutQueue : function( queue )
29879     {
29880         for ( var i=0, len = queue.length; i < len; i++ ) {
29881             var obj = queue[i];
29882             obj.item.position('absolute');
29883             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29884         }
29885     },
29886       
29887     
29888     /**
29889     * Any logic you want to do after each layout,
29890     * i.e. size the container
29891     */
29892     _postLayout : function()
29893     {
29894         this.resizeContainer();
29895     },
29896     
29897     resizeContainer : function()
29898     {
29899         if ( !this.isResizingContainer ) {
29900             return;
29901         }
29902         var size = this._getContainerSize();
29903         if ( size ) {
29904             this.el.setSize(size.width,size.height);
29905             this.boxesEl.setSize(size.width,size.height);
29906         }
29907     },
29908     
29909     
29910     
29911     _resetLayout : function()
29912     {
29913         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
29914         this.colWidth = this.el.getWidth();
29915         //this.gutter = this.el.getWidth(); 
29916         
29917         this.measureColumns();
29918
29919         // reset column Y
29920         var i = this.cols;
29921         this.colYs = [];
29922         while (i--) {
29923             this.colYs.push( 0 );
29924         }
29925     
29926         this.maxY = 0;
29927     },
29928
29929     measureColumns : function()
29930     {
29931         this.getContainerWidth();
29932       // if columnWidth is 0, default to outerWidth of first item
29933         if ( !this.columnWidth ) {
29934             var firstItem = this.bricks.first();
29935             Roo.log(firstItem);
29936             this.columnWidth  = this.containerWidth;
29937             if (firstItem && firstItem.attr('originalwidth') ) {
29938                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
29939             }
29940             // columnWidth fall back to item of first element
29941             Roo.log("set column width?");
29942                         this.initialColumnWidth = this.columnWidth  ;
29943
29944             // if first elem has no width, default to size of container
29945             
29946         }
29947         
29948         
29949         if (this.initialColumnWidth) {
29950             this.columnWidth = this.initialColumnWidth;
29951         }
29952         
29953         
29954             
29955         // column width is fixed at the top - however if container width get's smaller we should
29956         // reduce it...
29957         
29958         // this bit calcs how man columns..
29959             
29960         var columnWidth = this.columnWidth += this.gutter;
29961       
29962         // calculate columns
29963         var containerWidth = this.containerWidth + this.gutter;
29964         
29965         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
29966         // fix rounding errors, typically with gutters
29967         var excess = columnWidth - containerWidth % columnWidth;
29968         
29969         
29970         // if overshoot is less than a pixel, round up, otherwise floor it
29971         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
29972         cols = Math[ mathMethod ]( cols );
29973         this.cols = Math.max( cols, 1 );
29974         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29975         
29976          // padding positioning..
29977         var totalColWidth = this.cols * this.columnWidth;
29978         var padavail = this.containerWidth - totalColWidth;
29979         // so for 2 columns - we need 3 'pads'
29980         
29981         var padNeeded = (1+this.cols) * this.padWidth;
29982         
29983         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
29984         
29985         this.columnWidth += padExtra
29986         //this.padWidth = Math.floor(padavail /  ( this.cols));
29987         
29988         // adjust colum width so that padding is fixed??
29989         
29990         // we have 3 columns ... total = width * 3
29991         // we have X left over... that should be used by 
29992         
29993         //if (this.expandC) {
29994             
29995         //}
29996         
29997         
29998         
29999     },
30000     
30001     getContainerWidth : function()
30002     {
30003        /* // container is parent if fit width
30004         var container = this.isFitWidth ? this.element.parentNode : this.element;
30005         // check that this.size and size are there
30006         // IE8 triggers resize on body size change, so they might not be
30007         
30008         var size = getSize( container );  //FIXME
30009         this.containerWidth = size && size.innerWidth; //FIXME
30010         */
30011          
30012         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30013         
30014     },
30015     
30016     _getItemLayoutPosition : function( item )  // what is item?
30017     {
30018         // we resize the item to our columnWidth..
30019       
30020         item.setWidth(this.columnWidth);
30021         item.autoBoxAdjust  = false;
30022         
30023         var sz = item.getSize();
30024  
30025         // how many columns does this brick span
30026         var remainder = this.containerWidth % this.columnWidth;
30027         
30028         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30029         // round if off by 1 pixel, otherwise use ceil
30030         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30031         colSpan = Math.min( colSpan, this.cols );
30032         
30033         // normally this should be '1' as we dont' currently allow multi width columns..
30034         
30035         var colGroup = this._getColGroup( colSpan );
30036         // get the minimum Y value from the columns
30037         var minimumY = Math.min.apply( Math, colGroup );
30038         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30039         
30040         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30041          
30042         // position the brick
30043         var position = {
30044             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30045             y: this.currentSize.y + minimumY + this.padHeight
30046         };
30047         
30048         Roo.log(position);
30049         // apply setHeight to necessary columns
30050         var setHeight = minimumY + sz.height + this.padHeight;
30051         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30052         
30053         var setSpan = this.cols + 1 - colGroup.length;
30054         for ( var i = 0; i < setSpan; i++ ) {
30055           this.colYs[ shortColIndex + i ] = setHeight ;
30056         }
30057       
30058         return position;
30059     },
30060     
30061     /**
30062      * @param {Number} colSpan - number of columns the element spans
30063      * @returns {Array} colGroup
30064      */
30065     _getColGroup : function( colSpan )
30066     {
30067         if ( colSpan < 2 ) {
30068           // if brick spans only one column, use all the column Ys
30069           return this.colYs;
30070         }
30071       
30072         var colGroup = [];
30073         // how many different places could this brick fit horizontally
30074         var groupCount = this.cols + 1 - colSpan;
30075         // for each group potential horizontal position
30076         for ( var i = 0; i < groupCount; i++ ) {
30077           // make an array of colY values for that one group
30078           var groupColYs = this.colYs.slice( i, i + colSpan );
30079           // and get the max value of the array
30080           colGroup[i] = Math.max.apply( Math, groupColYs );
30081         }
30082         return colGroup;
30083     },
30084     /*
30085     _manageStamp : function( stamp )
30086     {
30087         var stampSize =  stamp.getSize();
30088         var offset = stamp.getBox();
30089         // get the columns that this stamp affects
30090         var firstX = this.isOriginLeft ? offset.x : offset.right;
30091         var lastX = firstX + stampSize.width;
30092         var firstCol = Math.floor( firstX / this.columnWidth );
30093         firstCol = Math.max( 0, firstCol );
30094         
30095         var lastCol = Math.floor( lastX / this.columnWidth );
30096         // lastCol should not go over if multiple of columnWidth #425
30097         lastCol -= lastX % this.columnWidth ? 0 : 1;
30098         lastCol = Math.min( this.cols - 1, lastCol );
30099         
30100         // set colYs to bottom of the stamp
30101         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30102             stampSize.height;
30103             
30104         for ( var i = firstCol; i <= lastCol; i++ ) {
30105           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30106         }
30107     },
30108     */
30109     
30110     _getContainerSize : function()
30111     {
30112         this.maxY = Math.max.apply( Math, this.colYs );
30113         var size = {
30114             height: this.maxY
30115         };
30116       
30117         if ( this.isFitWidth ) {
30118             size.width = this._getContainerFitWidth();
30119         }
30120       
30121         return size;
30122     },
30123     
30124     _getContainerFitWidth : function()
30125     {
30126         var unusedCols = 0;
30127         // count unused columns
30128         var i = this.cols;
30129         while ( --i ) {
30130           if ( this.colYs[i] !== 0 ) {
30131             break;
30132           }
30133           unusedCols++;
30134         }
30135         // fit container to columns that have been used
30136         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30137     },
30138     
30139     needsResizeLayout : function()
30140     {
30141         var previousWidth = this.containerWidth;
30142         this.getContainerWidth();
30143         return previousWidth !== this.containerWidth;
30144     }
30145  
30146 });
30147
30148  
30149
30150  /*
30151  * - LGPL
30152  *
30153  * element
30154  * 
30155  */
30156
30157 /**
30158  * @class Roo.bootstrap.MasonryBrick
30159  * @extends Roo.bootstrap.Component
30160  * Bootstrap MasonryBrick class
30161  * 
30162  * @constructor
30163  * Create a new MasonryBrick
30164  * @param {Object} config The config object
30165  */
30166
30167 Roo.bootstrap.MasonryBrick = function(config){
30168     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30169     
30170     this.addEvents({
30171         // raw events
30172         /**
30173          * @event click
30174          * When a MasonryBrick is clcik
30175          * @param {Roo.bootstrap.MasonryBrick} this
30176          * @param {Roo.EventObject} e
30177          */
30178         "click" : true
30179     });
30180 };
30181
30182 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30183     
30184     /**
30185      * @cfg {String} title
30186      */   
30187     title : '',
30188     /**
30189      * @cfg {String} html
30190      */   
30191     html : '',
30192     /**
30193      * @cfg {String} bgimage
30194      */   
30195     bgimage : '',
30196     /**
30197      * @cfg {String} videourl
30198      */   
30199     videourl : '',
30200     /**
30201      * @cfg {String} cls
30202      */   
30203     cls : '',
30204     /**
30205      * @cfg {String} href
30206      */   
30207     href : '',
30208     /**
30209      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30210      */   
30211     size : 'xs',
30212     
30213     /**
30214      * @cfg {String} (center|bottom) placetitle
30215      */   
30216     placetitle : '',
30217     
30218     getAutoCreate : function()
30219     {
30220         var cls = 'masonry-brick';
30221         
30222         if(this.href.length){
30223             cls += ' masonry-brick-link';
30224         }
30225         
30226         if(this.bgimage.length){
30227             cls += ' masonry-brick-image';
30228         }
30229         
30230         if(this.size){
30231             cls += ' masonry-' + this.size + '-brick';
30232         }
30233         
30234         if(this.placetitle.length){
30235             
30236             switch (this.placetitle) {
30237                 case 'center' :
30238                     cls += ' masonry-center-title';
30239                     break;
30240                 case 'bottom' :
30241                     cls += ' masonry-bottom-title';
30242                     break;
30243                 default:
30244                     break;
30245             }
30246             
30247         } else {
30248             if(!this.html.length && !this.bgimage.length){
30249                 cls += ' masonry-center-title';
30250             }
30251
30252             if(!this.html.length && this.bgimage.length){
30253                 cls += ' masonry-bottom-title';
30254             }
30255         }
30256         
30257         if(this.cls){
30258             cls += ' ' + this.cls;
30259         }
30260         
30261         var cfg = {
30262             tag: (this.href.length) ? 'a' : 'div',
30263             cls: cls,
30264             cn: [
30265                 {
30266                     tag: 'div',
30267                     cls: 'masonry-brick-paragraph',
30268                     cn: []
30269                 }
30270             ]
30271         };
30272         
30273         if(this.href.length){
30274             cfg.href = this.href;
30275         }
30276         
30277         var cn = cfg.cn[0].cn;
30278         
30279         if(this.title.length){
30280             cn.push({
30281                 tag: 'h4',
30282                 cls: 'masonry-brick-title',
30283                 html: this.title
30284             });
30285         }
30286         
30287         if(this.html.length){
30288             cn.push({
30289                 tag: 'p',
30290                 cls: 'masonry-brick-text',
30291                 html: this.html
30292             });
30293         }  
30294         if (!this.title.length && !this.html.length) {
30295             cfg.cn[0].cls += ' hide';
30296         }
30297         
30298         if(this.bgimage.length){
30299             cfg.cn.push({
30300                 tag: 'img',
30301                 cls: 'masonry-brick-image-view',
30302                 src: this.bgimage
30303             });
30304         }
30305         if(this.videourl.length){
30306             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30307             // youtube support only?
30308             cfg.cn.push({
30309                 tag: 'iframe',
30310                 cls: 'masonry-brick-image-view',
30311                 src: vurl,
30312                 frameborder : 0,
30313                 allowfullscreen : true
30314             });
30315             
30316             
30317         }
30318         return cfg;
30319         
30320     },
30321     
30322     initEvents: function() 
30323     {
30324         switch (this.size) {
30325             case 'xs' :
30326 //                this.intSize = 1;
30327                 this.x = 1;
30328                 this.y = 1;
30329                 break;
30330             case 'sm' :
30331 //                this.intSize = 2;
30332                 this.x = 2;
30333                 this.y = 2;
30334                 break;
30335             case 'md' :
30336             case 'md-left' :
30337             case 'md-right' :
30338 //                this.intSize = 3;
30339                 this.x = 3;
30340                 this.y = 3;
30341                 break;
30342             case 'tall' :
30343 //                this.intSize = 3;
30344                 this.x = 2;
30345                 this.y = 3;
30346                 break;
30347             case 'wide' :
30348 //                this.intSize = 3;
30349                 this.x = 3;
30350                 this.y = 2;
30351                 break;
30352             case 'wide-thin' :
30353 //                this.intSize = 3;
30354                 this.x = 3;
30355                 this.y = 1;
30356                 break;
30357                         
30358             default :
30359                 break;
30360         }
30361         
30362         
30363         
30364         if(Roo.isTouch){
30365             this.el.on('touchstart', this.onTouchStart, this);
30366             this.el.on('touchmove', this.onTouchMove, this);
30367             this.el.on('touchend', this.onTouchEnd, this);
30368             this.el.on('contextmenu', this.onContextMenu, this);
30369         } else {
30370             this.el.on('mouseenter'  ,this.enter, this);
30371             this.el.on('mouseleave', this.leave, this);
30372         }
30373         
30374         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30375             this.parent().bricks.push(this);   
30376         }
30377         
30378     },
30379     
30380     onClick: function(e, el)
30381     {
30382        // alert('click');
30383         
30384         if(!Roo.isTouch){
30385             return;
30386         }
30387         
30388         var time = this.endTimer - this.startTimer;
30389         
30390         //alert(time);
30391         
30392         if(time < 1000){
30393             return;
30394         }
30395         
30396         e.preventDefault();
30397     },
30398     
30399     enter: function(e, el)
30400     {
30401         e.preventDefault();
30402         
30403         if(this.bgimage.length && this.html.length){
30404             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30405         }
30406     },
30407     
30408     leave: function(e, el)
30409     {
30410         e.preventDefault();
30411         
30412         if(this.bgimage.length && this.html.length){
30413             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30414         }
30415     },
30416     
30417     onTouchStart: function(e, el)
30418     {
30419 //        e.preventDefault();
30420         
30421         if(!this.bgimage.length || !this.html.length){
30422             return;
30423         }
30424         
30425         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30426         
30427         this.timer = new Date().getTime();
30428         
30429         this.touchmoved = false;
30430     },
30431     
30432     onTouchMove: function(e, el)
30433     {
30434         this.touchmoved = true;
30435     },
30436     onContextMenu : function(e,el)
30437     {
30438             e.preventDefault();
30439             e.stopPropagation();
30440             return false;
30441     },
30442     
30443     
30444     onTouchEnd: function(e, el)
30445     {
30446 //        e.preventDefault();
30447         
30448         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30449             
30450             this.leave(e,el);
30451             
30452             return;
30453         }
30454         
30455         if(!this.bgimage.length || !this.html.length){
30456             
30457             if(this.href.length){
30458                 window.location.href = this.href;
30459             }
30460             
30461             return;
30462         }
30463         
30464         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30465         
30466         window.location.href = this.href;
30467     }
30468     
30469 });
30470
30471  
30472
30473  /*
30474  * - LGPL
30475  *
30476  * element
30477  * 
30478  */
30479
30480 /**
30481  * @class Roo.bootstrap.Brick
30482  * @extends Roo.bootstrap.Component
30483  * Bootstrap Brick class
30484  * 
30485  * @constructor
30486  * Create a new Brick
30487  * @param {Object} config The config object
30488  */
30489
30490 Roo.bootstrap.Brick = function(config){
30491     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30492     
30493     this.addEvents({
30494         // raw events
30495         /**
30496          * @event click
30497          * When a Brick is click
30498          * @param {Roo.bootstrap.Brick} this
30499          * @param {Roo.EventObject} e
30500          */
30501         "click" : true
30502     });
30503 };
30504
30505 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30506     
30507     /**
30508      * @cfg {String} title
30509      */   
30510     title : '',
30511     /**
30512      * @cfg {String} html
30513      */   
30514     html : '',
30515     /**
30516      * @cfg {String} bgimage
30517      */   
30518     bgimage : '',
30519     /**
30520      * @cfg {String} cls
30521      */   
30522     cls : '',
30523     /**
30524      * @cfg {String} href
30525      */   
30526     href : '',
30527     /**
30528      * @cfg {String} video
30529      */   
30530     video : '',
30531     /**
30532      * @cfg {Boolean} square
30533      */   
30534     square : true,
30535     
30536     getAutoCreate : function()
30537     {
30538         var cls = 'roo-brick';
30539         
30540         if(this.href.length){
30541             cls += ' roo-brick-link';
30542         }
30543         
30544         if(this.bgimage.length){
30545             cls += ' roo-brick-image';
30546         }
30547         
30548         if(!this.html.length && !this.bgimage.length){
30549             cls += ' roo-brick-center-title';
30550         }
30551         
30552         if(!this.html.length && this.bgimage.length){
30553             cls += ' roo-brick-bottom-title';
30554         }
30555         
30556         if(this.cls){
30557             cls += ' ' + this.cls;
30558         }
30559         
30560         var cfg = {
30561             tag: (this.href.length) ? 'a' : 'div',
30562             cls: cls,
30563             cn: [
30564                 {
30565                     tag: 'div',
30566                     cls: 'roo-brick-paragraph',
30567                     cn: []
30568                 }
30569             ]
30570         };
30571         
30572         if(this.href.length){
30573             cfg.href = this.href;
30574         }
30575         
30576         var cn = cfg.cn[0].cn;
30577         
30578         if(this.title.length){
30579             cn.push({
30580                 tag: 'h4',
30581                 cls: 'roo-brick-title',
30582                 html: this.title
30583             });
30584         }
30585         
30586         if(this.html.length){
30587             cn.push({
30588                 tag: 'p',
30589                 cls: 'roo-brick-text',
30590                 html: this.html
30591             });
30592         } else {
30593             cn.cls += ' hide';
30594         }
30595         
30596         if(this.bgimage.length){
30597             cfg.cn.push({
30598                 tag: 'img',
30599                 cls: 'roo-brick-image-view',
30600                 src: this.bgimage
30601             });
30602         }
30603         
30604         return cfg;
30605     },
30606     
30607     initEvents: function() 
30608     {
30609         if(this.title.length || this.html.length){
30610             this.el.on('mouseenter'  ,this.enter, this);
30611             this.el.on('mouseleave', this.leave, this);
30612         }
30613         
30614         
30615         Roo.EventManager.onWindowResize(this.resize, this); 
30616         
30617         this.resize();
30618     },
30619     
30620     resize : function()
30621     {
30622         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30623         
30624         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30625 //        paragraph.setHeight(paragraph.getWidth());
30626         
30627         if(this.bgimage.length){
30628             var image = this.el.select('.roo-brick-image-view', true).first();
30629             image.setWidth(paragraph.getWidth());
30630             image.setHeight(paragraph.getWidth());
30631         }
30632         
30633     },
30634     
30635     enter: function(e, el)
30636     {
30637         e.preventDefault();
30638         
30639         if(this.bgimage.length){
30640             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30641             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30642         }
30643     },
30644     
30645     leave: function(e, el)
30646     {
30647         e.preventDefault();
30648         
30649         if(this.bgimage.length){
30650             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30651             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30652         }
30653     }
30654     
30655 });
30656
30657  
30658
30659  /*
30660  * Based on:
30661  * Ext JS Library 1.1.1
30662  * Copyright(c) 2006-2007, Ext JS, LLC.
30663  *
30664  * Originally Released Under LGPL - original licence link has changed is not relivant.
30665  *
30666  * Fork - LGPL
30667  * <script type="text/javascript">
30668  */
30669
30670
30671 /**
30672  * @class Roo.bootstrap.SplitBar
30673  * @extends Roo.util.Observable
30674  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30675  * <br><br>
30676  * Usage:
30677  * <pre><code>
30678 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30679                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30680 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30681 split.minSize = 100;
30682 split.maxSize = 600;
30683 split.animate = true;
30684 split.on('moved', splitterMoved);
30685 </code></pre>
30686  * @constructor
30687  * Create a new SplitBar
30688  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
30689  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
30690  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30691  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
30692                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30693                         position of the SplitBar).
30694  */
30695 Roo.bootstrap.SplitBar = function(cfg){
30696     
30697     /** @private */
30698     
30699     //{
30700     //  dragElement : elm
30701     //  resizingElement: el,
30702         // optional..
30703     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30704     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
30705         // existingProxy ???
30706     //}
30707     
30708     this.el = Roo.get(cfg.dragElement, true);
30709     this.el.dom.unselectable = "on";
30710     /** @private */
30711     this.resizingEl = Roo.get(cfg.resizingElement, true);
30712
30713     /**
30714      * @private
30715      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30716      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30717      * @type Number
30718      */
30719     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30720     
30721     /**
30722      * The minimum size of the resizing element. (Defaults to 0)
30723      * @type Number
30724      */
30725     this.minSize = 0;
30726     
30727     /**
30728      * The maximum size of the resizing element. (Defaults to 2000)
30729      * @type Number
30730      */
30731     this.maxSize = 2000;
30732     
30733     /**
30734      * Whether to animate the transition to the new size
30735      * @type Boolean
30736      */
30737     this.animate = false;
30738     
30739     /**
30740      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30741      * @type Boolean
30742      */
30743     this.useShim = false;
30744     
30745     /** @private */
30746     this.shim = null;
30747     
30748     if(!cfg.existingProxy){
30749         /** @private */
30750         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30751     }else{
30752         this.proxy = Roo.get(cfg.existingProxy).dom;
30753     }
30754     /** @private */
30755     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30756     
30757     /** @private */
30758     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30759     
30760     /** @private */
30761     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30762     
30763     /** @private */
30764     this.dragSpecs = {};
30765     
30766     /**
30767      * @private The adapter to use to positon and resize elements
30768      */
30769     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30770     this.adapter.init(this);
30771     
30772     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30773         /** @private */
30774         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30775         this.el.addClass("roo-splitbar-h");
30776     }else{
30777         /** @private */
30778         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30779         this.el.addClass("roo-splitbar-v");
30780     }
30781     
30782     this.addEvents({
30783         /**
30784          * @event resize
30785          * Fires when the splitter is moved (alias for {@link #event-moved})
30786          * @param {Roo.bootstrap.SplitBar} this
30787          * @param {Number} newSize the new width or height
30788          */
30789         "resize" : true,
30790         /**
30791          * @event moved
30792          * Fires when the splitter is moved
30793          * @param {Roo.bootstrap.SplitBar} this
30794          * @param {Number} newSize the new width or height
30795          */
30796         "moved" : true,
30797         /**
30798          * @event beforeresize
30799          * Fires before the splitter is dragged
30800          * @param {Roo.bootstrap.SplitBar} this
30801          */
30802         "beforeresize" : true,
30803
30804         "beforeapply" : true
30805     });
30806
30807     Roo.util.Observable.call(this);
30808 };
30809
30810 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30811     onStartProxyDrag : function(x, y){
30812         this.fireEvent("beforeresize", this);
30813         if(!this.overlay){
30814             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
30815             o.unselectable();
30816             o.enableDisplayMode("block");
30817             // all splitbars share the same overlay
30818             Roo.bootstrap.SplitBar.prototype.overlay = o;
30819         }
30820         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30821         this.overlay.show();
30822         Roo.get(this.proxy).setDisplayed("block");
30823         var size = this.adapter.getElementSize(this);
30824         this.activeMinSize = this.getMinimumSize();;
30825         this.activeMaxSize = this.getMaximumSize();;
30826         var c1 = size - this.activeMinSize;
30827         var c2 = Math.max(this.activeMaxSize - size, 0);
30828         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30829             this.dd.resetConstraints();
30830             this.dd.setXConstraint(
30831                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
30832                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30833             );
30834             this.dd.setYConstraint(0, 0);
30835         }else{
30836             this.dd.resetConstraints();
30837             this.dd.setXConstraint(0, 0);
30838             this.dd.setYConstraint(
30839                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
30840                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30841             );
30842          }
30843         this.dragSpecs.startSize = size;
30844         this.dragSpecs.startPoint = [x, y];
30845         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30846     },
30847     
30848     /** 
30849      * @private Called after the drag operation by the DDProxy
30850      */
30851     onEndProxyDrag : function(e){
30852         Roo.get(this.proxy).setDisplayed(false);
30853         var endPoint = Roo.lib.Event.getXY(e);
30854         if(this.overlay){
30855             this.overlay.hide();
30856         }
30857         var newSize;
30858         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30859             newSize = this.dragSpecs.startSize + 
30860                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30861                     endPoint[0] - this.dragSpecs.startPoint[0] :
30862                     this.dragSpecs.startPoint[0] - endPoint[0]
30863                 );
30864         }else{
30865             newSize = this.dragSpecs.startSize + 
30866                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30867                     endPoint[1] - this.dragSpecs.startPoint[1] :
30868                     this.dragSpecs.startPoint[1] - endPoint[1]
30869                 );
30870         }
30871         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30872         if(newSize != this.dragSpecs.startSize){
30873             if(this.fireEvent('beforeapply', this, newSize) !== false){
30874                 this.adapter.setElementSize(this, newSize);
30875                 this.fireEvent("moved", this, newSize);
30876                 this.fireEvent("resize", this, newSize);
30877             }
30878         }
30879     },
30880     
30881     /**
30882      * Get the adapter this SplitBar uses
30883      * @return The adapter object
30884      */
30885     getAdapter : function(){
30886         return this.adapter;
30887     },
30888     
30889     /**
30890      * Set the adapter this SplitBar uses
30891      * @param {Object} adapter A SplitBar adapter object
30892      */
30893     setAdapter : function(adapter){
30894         this.adapter = adapter;
30895         this.adapter.init(this);
30896     },
30897     
30898     /**
30899      * Gets the minimum size for the resizing element
30900      * @return {Number} The minimum size
30901      */
30902     getMinimumSize : function(){
30903         return this.minSize;
30904     },
30905     
30906     /**
30907      * Sets the minimum size for the resizing element
30908      * @param {Number} minSize The minimum size
30909      */
30910     setMinimumSize : function(minSize){
30911         this.minSize = minSize;
30912     },
30913     
30914     /**
30915      * Gets the maximum size for the resizing element
30916      * @return {Number} The maximum size
30917      */
30918     getMaximumSize : function(){
30919         return this.maxSize;
30920     },
30921     
30922     /**
30923      * Sets the maximum size for the resizing element
30924      * @param {Number} maxSize The maximum size
30925      */
30926     setMaximumSize : function(maxSize){
30927         this.maxSize = maxSize;
30928     },
30929     
30930     /**
30931      * Sets the initialize size for the resizing element
30932      * @param {Number} size The initial size
30933      */
30934     setCurrentSize : function(size){
30935         var oldAnimate = this.animate;
30936         this.animate = false;
30937         this.adapter.setElementSize(this, size);
30938         this.animate = oldAnimate;
30939     },
30940     
30941     /**
30942      * Destroy this splitbar. 
30943      * @param {Boolean} removeEl True to remove the element
30944      */
30945     destroy : function(removeEl){
30946         if(this.shim){
30947             this.shim.remove();
30948         }
30949         this.dd.unreg();
30950         this.proxy.parentNode.removeChild(this.proxy);
30951         if(removeEl){
30952             this.el.remove();
30953         }
30954     }
30955 });
30956
30957 /**
30958  * @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.
30959  */
30960 Roo.bootstrap.SplitBar.createProxy = function(dir){
30961     var proxy = new Roo.Element(document.createElement("div"));
30962     proxy.unselectable();
30963     var cls = 'roo-splitbar-proxy';
30964     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
30965     document.body.appendChild(proxy.dom);
30966     return proxy.dom;
30967 };
30968
30969 /** 
30970  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
30971  * Default Adapter. It assumes the splitter and resizing element are not positioned
30972  * elements and only gets/sets the width of the element. Generally used for table based layouts.
30973  */
30974 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
30975 };
30976
30977 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
30978     // do nothing for now
30979     init : function(s){
30980     
30981     },
30982     /**
30983      * Called before drag operations to get the current size of the resizing element. 
30984      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30985      */
30986      getElementSize : function(s){
30987         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30988             return s.resizingEl.getWidth();
30989         }else{
30990             return s.resizingEl.getHeight();
30991         }
30992     },
30993     
30994     /**
30995      * Called after drag operations to set the size of the resizing element.
30996      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30997      * @param {Number} newSize The new size to set
30998      * @param {Function} onComplete A function to be invoked when resizing is complete
30999      */
31000     setElementSize : function(s, newSize, onComplete){
31001         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31002             if(!s.animate){
31003                 s.resizingEl.setWidth(newSize);
31004                 if(onComplete){
31005                     onComplete(s, newSize);
31006                 }
31007             }else{
31008                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31009             }
31010         }else{
31011             
31012             if(!s.animate){
31013                 s.resizingEl.setHeight(newSize);
31014                 if(onComplete){
31015                     onComplete(s, newSize);
31016                 }
31017             }else{
31018                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31019             }
31020         }
31021     }
31022 };
31023
31024 /** 
31025  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31026  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31027  * Adapter that  moves the splitter element to align with the resized sizing element. 
31028  * Used with an absolute positioned SplitBar.
31029  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31030  * document.body, make sure you assign an id to the body element.
31031  */
31032 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31033     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31034     this.container = Roo.get(container);
31035 };
31036
31037 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31038     init : function(s){
31039         this.basic.init(s);
31040     },
31041     
31042     getElementSize : function(s){
31043         return this.basic.getElementSize(s);
31044     },
31045     
31046     setElementSize : function(s, newSize, onComplete){
31047         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31048     },
31049     
31050     moveSplitter : function(s){
31051         var yes = Roo.bootstrap.SplitBar;
31052         switch(s.placement){
31053             case yes.LEFT:
31054                 s.el.setX(s.resizingEl.getRight());
31055                 break;
31056             case yes.RIGHT:
31057                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31058                 break;
31059             case yes.TOP:
31060                 s.el.setY(s.resizingEl.getBottom());
31061                 break;
31062             case yes.BOTTOM:
31063                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31064                 break;
31065         }
31066     }
31067 };
31068
31069 /**
31070  * Orientation constant - Create a vertical SplitBar
31071  * @static
31072  * @type Number
31073  */
31074 Roo.bootstrap.SplitBar.VERTICAL = 1;
31075
31076 /**
31077  * Orientation constant - Create a horizontal SplitBar
31078  * @static
31079  * @type Number
31080  */
31081 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31082
31083 /**
31084  * Placement constant - The resizing element is to the left of the splitter element
31085  * @static
31086  * @type Number
31087  */
31088 Roo.bootstrap.SplitBar.LEFT = 1;
31089
31090 /**
31091  * Placement constant - The resizing element is to the right of the splitter element
31092  * @static
31093  * @type Number
31094  */
31095 Roo.bootstrap.SplitBar.RIGHT = 2;
31096
31097 /**
31098  * Placement constant - The resizing element is positioned above the splitter element
31099  * @static
31100  * @type Number
31101  */
31102 Roo.bootstrap.SplitBar.TOP = 3;
31103
31104 /**
31105  * Placement constant - The resizing element is positioned under splitter element
31106  * @static
31107  * @type Number
31108  */
31109 Roo.bootstrap.SplitBar.BOTTOM = 4;
31110 Roo.namespace("Roo.bootstrap.layout");/*
31111  * Based on:
31112  * Ext JS Library 1.1.1
31113  * Copyright(c) 2006-2007, Ext JS, LLC.
31114  *
31115  * Originally Released Under LGPL - original licence link has changed is not relivant.
31116  *
31117  * Fork - LGPL
31118  * <script type="text/javascript">
31119  */
31120  
31121 /**
31122  * @class Roo.bootstrap.layout.Manager
31123  * @extends Roo.bootstrap.Component
31124  * Base class for layout managers.
31125  */
31126 Roo.bootstrap.layout.Manager = function(config)
31127 {
31128     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31129     
31130     
31131      
31132     
31133     
31134     /** false to disable window resize monitoring @type Boolean */
31135     this.monitorWindowResize = true;
31136     this.regions = {};
31137     this.addEvents({
31138         /**
31139          * @event layout
31140          * Fires when a layout is performed. 
31141          * @param {Roo.LayoutManager} this
31142          */
31143         "layout" : true,
31144         /**
31145          * @event regionresized
31146          * Fires when the user resizes a region. 
31147          * @param {Roo.LayoutRegion} region The resized region
31148          * @param {Number} newSize The new size (width for east/west, height for north/south)
31149          */
31150         "regionresized" : true,
31151         /**
31152          * @event regioncollapsed
31153          * Fires when a region is collapsed. 
31154          * @param {Roo.LayoutRegion} region The collapsed region
31155          */
31156         "regioncollapsed" : true,
31157         /**
31158          * @event regionexpanded
31159          * Fires when a region is expanded.  
31160          * @param {Roo.LayoutRegion} region The expanded region
31161          */
31162         "regionexpanded" : true
31163     });
31164     this.updating = false;
31165     
31166     if (config.el) {
31167         this.el = Roo.get(config.el);
31168         this.initEvents();
31169     }
31170     
31171 };
31172
31173 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31174     
31175     
31176     regions : null,
31177     
31178     monitorWindowResize : true,
31179     
31180     
31181     updating : false,
31182     
31183     
31184     onRender : function(ct, position)
31185     {
31186         if(!this.el){
31187             this.el = Roo.get(ct);
31188             this.initEvents();
31189         }
31190     },
31191     
31192     
31193     initEvents: function()
31194     {
31195         
31196         
31197         // ie scrollbar fix
31198         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31199             document.body.scroll = "no";
31200         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31201             this.el.position('relative');
31202         }
31203         this.id = this.el.id;
31204         this.el.addClass("roo-layout-container");
31205         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31206         if(this.el.dom != document.body ) {
31207             this.el.on('resize', this.layout,this);
31208             this.el.on('show', this.layout,this);
31209         }
31210
31211     },
31212     
31213     /**
31214      * Returns true if this layout is currently being updated
31215      * @return {Boolean}
31216      */
31217     isUpdating : function(){
31218         return this.updating; 
31219     },
31220     
31221     /**
31222      * Suspend the LayoutManager from doing auto-layouts while
31223      * making multiple add or remove calls
31224      */
31225     beginUpdate : function(){
31226         this.updating = true;    
31227     },
31228     
31229     /**
31230      * Restore auto-layouts and optionally disable the manager from performing a layout
31231      * @param {Boolean} noLayout true to disable a layout update 
31232      */
31233     endUpdate : function(noLayout){
31234         this.updating = false;
31235         if(!noLayout){
31236             this.layout();
31237         }    
31238     },
31239     
31240     layout: function(){
31241         // abstract...
31242     },
31243     
31244     onRegionResized : function(region, newSize){
31245         this.fireEvent("regionresized", region, newSize);
31246         this.layout();
31247     },
31248     
31249     onRegionCollapsed : function(region){
31250         this.fireEvent("regioncollapsed", region);
31251     },
31252     
31253     onRegionExpanded : function(region){
31254         this.fireEvent("regionexpanded", region);
31255     },
31256         
31257     /**
31258      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31259      * performs box-model adjustments.
31260      * @return {Object} The size as an object {width: (the width), height: (the height)}
31261      */
31262     getViewSize : function()
31263     {
31264         var size;
31265         if(this.el.dom != document.body){
31266             size = this.el.getSize();
31267         }else{
31268             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31269         }
31270         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31271         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31272         return size;
31273     },
31274     
31275     /**
31276      * Returns the Element this layout is bound to.
31277      * @return {Roo.Element}
31278      */
31279     getEl : function(){
31280         return this.el;
31281     },
31282     
31283     /**
31284      * Returns the specified region.
31285      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31286      * @return {Roo.LayoutRegion}
31287      */
31288     getRegion : function(target){
31289         return this.regions[target.toLowerCase()];
31290     },
31291     
31292     onWindowResize : function(){
31293         if(this.monitorWindowResize){
31294             this.layout();
31295         }
31296     }
31297 });/*
31298  * Based on:
31299  * Ext JS Library 1.1.1
31300  * Copyright(c) 2006-2007, Ext JS, LLC.
31301  *
31302  * Originally Released Under LGPL - original licence link has changed is not relivant.
31303  *
31304  * Fork - LGPL
31305  * <script type="text/javascript">
31306  */
31307 /**
31308  * @class Roo.bootstrap.layout.Border
31309  * @extends Roo.bootstrap.layout.Manager
31310  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31311  * please see: examples/bootstrap/nested.html<br><br>
31312  
31313 <b>The container the layout is rendered into can be either the body element or any other element.
31314 If it is not the body element, the container needs to either be an absolute positioned element,
31315 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31316 the container size if it is not the body element.</b>
31317
31318 * @constructor
31319 * Create a new Border
31320 * @param {Object} config Configuration options
31321  */
31322 Roo.bootstrap.layout.Border = function(config){
31323     config = config || {};
31324     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31325     
31326     
31327     
31328     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31329         if(config[region]){
31330             config[region].region = region;
31331             this.addRegion(config[region]);
31332         }
31333     },this);
31334     
31335 };
31336
31337 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31338
31339 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31340     /**
31341      * Creates and adds a new region if it doesn't already exist.
31342      * @param {String} target The target region key (north, south, east, west or center).
31343      * @param {Object} config The regions config object
31344      * @return {BorderLayoutRegion} The new region
31345      */
31346     addRegion : function(config)
31347     {
31348         if(!this.regions[config.region]){
31349             var r = this.factory(config);
31350             this.bindRegion(r);
31351         }
31352         return this.regions[config.region];
31353     },
31354
31355     // private (kinda)
31356     bindRegion : function(r){
31357         this.regions[r.config.region] = r;
31358         
31359         r.on("visibilitychange",    this.layout, this);
31360         r.on("paneladded",          this.layout, this);
31361         r.on("panelremoved",        this.layout, this);
31362         r.on("invalidated",         this.layout, this);
31363         r.on("resized",             this.onRegionResized, this);
31364         r.on("collapsed",           this.onRegionCollapsed, this);
31365         r.on("expanded",            this.onRegionExpanded, this);
31366     },
31367
31368     /**
31369      * Performs a layout update.
31370      */
31371     layout : function()
31372     {
31373         if(this.updating) {
31374             return;
31375         }
31376         var size = this.getViewSize();
31377         var w = size.width;
31378         var h = size.height;
31379         var centerW = w;
31380         var centerH = h;
31381         var centerY = 0;
31382         var centerX = 0;
31383         //var x = 0, y = 0;
31384
31385         var rs = this.regions;
31386         var north = rs["north"];
31387         var south = rs["south"]; 
31388         var west = rs["west"];
31389         var east = rs["east"];
31390         var center = rs["center"];
31391         //if(this.hideOnLayout){ // not supported anymore
31392             //c.el.setStyle("display", "none");
31393         //}
31394         if(north && north.isVisible()){
31395             var b = north.getBox();
31396             var m = north.getMargins();
31397             b.width = w - (m.left+m.right);
31398             b.x = m.left;
31399             b.y = m.top;
31400             centerY = b.height + b.y + m.bottom;
31401             centerH -= centerY;
31402             north.updateBox(this.safeBox(b));
31403         }
31404         if(south && south.isVisible()){
31405             var b = south.getBox();
31406             var m = south.getMargins();
31407             b.width = w - (m.left+m.right);
31408             b.x = m.left;
31409             var totalHeight = (b.height + m.top + m.bottom);
31410             b.y = h - totalHeight + m.top;
31411             centerH -= totalHeight;
31412             south.updateBox(this.safeBox(b));
31413         }
31414         if(west && west.isVisible()){
31415             var b = west.getBox();
31416             var m = west.getMargins();
31417             b.height = centerH - (m.top+m.bottom);
31418             b.x = m.left;
31419             b.y = centerY + m.top;
31420             var totalWidth = (b.width + m.left + m.right);
31421             centerX += totalWidth;
31422             centerW -= totalWidth;
31423             west.updateBox(this.safeBox(b));
31424         }
31425         if(east && east.isVisible()){
31426             var b = east.getBox();
31427             var m = east.getMargins();
31428             b.height = centerH - (m.top+m.bottom);
31429             var totalWidth = (b.width + m.left + m.right);
31430             b.x = w - totalWidth + m.left;
31431             b.y = centerY + m.top;
31432             centerW -= totalWidth;
31433             east.updateBox(this.safeBox(b));
31434         }
31435         if(center){
31436             var m = center.getMargins();
31437             var centerBox = {
31438                 x: centerX + m.left,
31439                 y: centerY + m.top,
31440                 width: centerW - (m.left+m.right),
31441                 height: centerH - (m.top+m.bottom)
31442             };
31443             //if(this.hideOnLayout){
31444                 //center.el.setStyle("display", "block");
31445             //}
31446             center.updateBox(this.safeBox(centerBox));
31447         }
31448         this.el.repaint();
31449         this.fireEvent("layout", this);
31450     },
31451
31452     // private
31453     safeBox : function(box){
31454         box.width = Math.max(0, box.width);
31455         box.height = Math.max(0, box.height);
31456         return box;
31457     },
31458
31459     /**
31460      * Adds a ContentPanel (or subclass) to this layout.
31461      * @param {String} target The target region key (north, south, east, west or center).
31462      * @param {Roo.ContentPanel} panel The panel to add
31463      * @return {Roo.ContentPanel} The added panel
31464      */
31465     add : function(target, panel){
31466          
31467         target = target.toLowerCase();
31468         return this.regions[target].add(panel);
31469     },
31470
31471     /**
31472      * Remove a ContentPanel (or subclass) to this layout.
31473      * @param {String} target The target region key (north, south, east, west or center).
31474      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31475      * @return {Roo.ContentPanel} The removed panel
31476      */
31477     remove : function(target, panel){
31478         target = target.toLowerCase();
31479         return this.regions[target].remove(panel);
31480     },
31481
31482     /**
31483      * Searches all regions for a panel with the specified id
31484      * @param {String} panelId
31485      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31486      */
31487     findPanel : function(panelId){
31488         var rs = this.regions;
31489         for(var target in rs){
31490             if(typeof rs[target] != "function"){
31491                 var p = rs[target].getPanel(panelId);
31492                 if(p){
31493                     return p;
31494                 }
31495             }
31496         }
31497         return null;
31498     },
31499
31500     /**
31501      * Searches all regions for a panel with the specified id and activates (shows) it.
31502      * @param {String/ContentPanel} panelId The panels id or the panel itself
31503      * @return {Roo.ContentPanel} The shown panel or null
31504      */
31505     showPanel : function(panelId) {
31506       var rs = this.regions;
31507       for(var target in rs){
31508          var r = rs[target];
31509          if(typeof r != "function"){
31510             if(r.hasPanel(panelId)){
31511                return r.showPanel(panelId);
31512             }
31513          }
31514       }
31515       return null;
31516    },
31517
31518    /**
31519      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31520      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31521      */
31522    /*
31523     restoreState : function(provider){
31524         if(!provider){
31525             provider = Roo.state.Manager;
31526         }
31527         var sm = new Roo.LayoutStateManager();
31528         sm.init(this, provider);
31529     },
31530 */
31531  
31532  
31533     /**
31534      * Adds a xtype elements to the layout.
31535      * <pre><code>
31536
31537 layout.addxtype({
31538        xtype : 'ContentPanel',
31539        region: 'west',
31540        items: [ .... ]
31541    }
31542 );
31543
31544 layout.addxtype({
31545         xtype : 'NestedLayoutPanel',
31546         region: 'west',
31547         layout: {
31548            center: { },
31549            west: { }   
31550         },
31551         items : [ ... list of content panels or nested layout panels.. ]
31552    }
31553 );
31554 </code></pre>
31555      * @param {Object} cfg Xtype definition of item to add.
31556      */
31557     addxtype : function(cfg)
31558     {
31559         // basically accepts a pannel...
31560         // can accept a layout region..!?!?
31561         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31562         
31563         
31564         // theory?  children can only be panels??
31565         
31566         //if (!cfg.xtype.match(/Panel$/)) {
31567         //    return false;
31568         //}
31569         var ret = false;
31570         
31571         if (typeof(cfg.region) == 'undefined') {
31572             Roo.log("Failed to add Panel, region was not set");
31573             Roo.log(cfg);
31574             return false;
31575         }
31576         var region = cfg.region;
31577         delete cfg.region;
31578         
31579           
31580         var xitems = [];
31581         if (cfg.items) {
31582             xitems = cfg.items;
31583             delete cfg.items;
31584         }
31585         var nb = false;
31586         
31587         switch(cfg.xtype) 
31588         {
31589             case 'Content':  // ContentPanel (el, cfg)
31590             case 'Scroll':  // ContentPanel (el, cfg)
31591             case 'View': 
31592                 cfg.autoCreate = true;
31593                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31594                 //} else {
31595                 //    var el = this.el.createChild();
31596                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31597                 //}
31598                 
31599                 this.add(region, ret);
31600                 break;
31601             
31602             /*
31603             case 'TreePanel': // our new panel!
31604                 cfg.el = this.el.createChild();
31605                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31606                 this.add(region, ret);
31607                 break;
31608             */
31609             
31610             case 'Nest': 
31611                 // create a new Layout (which is  a Border Layout...
31612                 
31613                 var clayout = cfg.layout;
31614                 clayout.el  = this.el.createChild();
31615                 clayout.items   = clayout.items  || [];
31616                 
31617                 delete cfg.layout;
31618                 
31619                 // replace this exitems with the clayout ones..
31620                 xitems = clayout.items;
31621                  
31622                 // force background off if it's in center...
31623                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31624                     cfg.background = false;
31625                 }
31626                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31627                 
31628                 
31629                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31630                 //console.log('adding nested layout panel '  + cfg.toSource());
31631                 this.add(region, ret);
31632                 nb = {}; /// find first...
31633                 break;
31634             
31635             case 'Grid':
31636                 
31637                 // needs grid and region
31638                 
31639                 //var el = this.getRegion(region).el.createChild();
31640                 /*
31641                  *var el = this.el.createChild();
31642                 // create the grid first...
31643                 cfg.grid.container = el;
31644                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31645                 */
31646                 
31647                 if (region == 'center' && this.active ) {
31648                     cfg.background = false;
31649                 }
31650                 
31651                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31652                 
31653                 this.add(region, ret);
31654                 /*
31655                 if (cfg.background) {
31656                     // render grid on panel activation (if panel background)
31657                     ret.on('activate', function(gp) {
31658                         if (!gp.grid.rendered) {
31659                     //        gp.grid.render(el);
31660                         }
31661                     });
31662                 } else {
31663                   //  cfg.grid.render(el);
31664                 }
31665                 */
31666                 break;
31667            
31668            
31669             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31670                 // it was the old xcomponent building that caused this before.
31671                 // espeically if border is the top element in the tree.
31672                 ret = this;
31673                 break; 
31674                 
31675                     
31676                 
31677                 
31678                 
31679             default:
31680                 /*
31681                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31682                     
31683                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31684                     this.add(region, ret);
31685                 } else {
31686                 */
31687                     Roo.log(cfg);
31688                     throw "Can not add '" + cfg.xtype + "' to Border";
31689                     return null;
31690              
31691                                 
31692              
31693         }
31694         this.beginUpdate();
31695         // add children..
31696         var region = '';
31697         var abn = {};
31698         Roo.each(xitems, function(i)  {
31699             region = nb && i.region ? i.region : false;
31700             
31701             var add = ret.addxtype(i);
31702            
31703             if (region) {
31704                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31705                 if (!i.background) {
31706                     abn[region] = nb[region] ;
31707                 }
31708             }
31709             
31710         });
31711         this.endUpdate();
31712
31713         // make the last non-background panel active..
31714         //if (nb) { Roo.log(abn); }
31715         if (nb) {
31716             
31717             for(var r in abn) {
31718                 region = this.getRegion(r);
31719                 if (region) {
31720                     // tried using nb[r], but it does not work..
31721                      
31722                     region.showPanel(abn[r]);
31723                    
31724                 }
31725             }
31726         }
31727         return ret;
31728         
31729     },
31730     
31731     
31732 // private
31733     factory : function(cfg)
31734     {
31735         
31736         var validRegions = Roo.bootstrap.layout.Border.regions;
31737
31738         var target = cfg.region;
31739         cfg.mgr = this;
31740         
31741         var r = Roo.bootstrap.layout;
31742         Roo.log(target);
31743         switch(target){
31744             case "north":
31745                 return new r.North(cfg);
31746             case "south":
31747                 return new r.South(cfg);
31748             case "east":
31749                 return new r.East(cfg);
31750             case "west":
31751                 return new r.West(cfg);
31752             case "center":
31753                 return new r.Center(cfg);
31754         }
31755         throw 'Layout region "'+target+'" not supported.';
31756     }
31757     
31758     
31759 });
31760  /*
31761  * Based on:
31762  * Ext JS Library 1.1.1
31763  * Copyright(c) 2006-2007, Ext JS, LLC.
31764  *
31765  * Originally Released Under LGPL - original licence link has changed is not relivant.
31766  *
31767  * Fork - LGPL
31768  * <script type="text/javascript">
31769  */
31770  
31771 /**
31772  * @class Roo.bootstrap.layout.Basic
31773  * @extends Roo.util.Observable
31774  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31775  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31776  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31777  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31778  * @cfg {string}   region  the region that it inhabits..
31779  * @cfg {bool}   skipConfig skip config?
31780  * 
31781
31782  */
31783 Roo.bootstrap.layout.Basic = function(config){
31784     
31785     this.mgr = config.mgr;
31786     
31787     this.position = config.region;
31788     
31789     var skipConfig = config.skipConfig;
31790     
31791     this.events = {
31792         /**
31793          * @scope Roo.BasicLayoutRegion
31794          */
31795         
31796         /**
31797          * @event beforeremove
31798          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31799          * @param {Roo.LayoutRegion} this
31800          * @param {Roo.ContentPanel} panel The panel
31801          * @param {Object} e The cancel event object
31802          */
31803         "beforeremove" : true,
31804         /**
31805          * @event invalidated
31806          * Fires when the layout for this region is changed.
31807          * @param {Roo.LayoutRegion} this
31808          */
31809         "invalidated" : true,
31810         /**
31811          * @event visibilitychange
31812          * Fires when this region is shown or hidden 
31813          * @param {Roo.LayoutRegion} this
31814          * @param {Boolean} visibility true or false
31815          */
31816         "visibilitychange" : true,
31817         /**
31818          * @event paneladded
31819          * Fires when a panel is added. 
31820          * @param {Roo.LayoutRegion} this
31821          * @param {Roo.ContentPanel} panel The panel
31822          */
31823         "paneladded" : true,
31824         /**
31825          * @event panelremoved
31826          * Fires when a panel is removed. 
31827          * @param {Roo.LayoutRegion} this
31828          * @param {Roo.ContentPanel} panel The panel
31829          */
31830         "panelremoved" : true,
31831         /**
31832          * @event beforecollapse
31833          * Fires when this region before collapse.
31834          * @param {Roo.LayoutRegion} this
31835          */
31836         "beforecollapse" : true,
31837         /**
31838          * @event collapsed
31839          * Fires when this region is collapsed.
31840          * @param {Roo.LayoutRegion} this
31841          */
31842         "collapsed" : true,
31843         /**
31844          * @event expanded
31845          * Fires when this region is expanded.
31846          * @param {Roo.LayoutRegion} this
31847          */
31848         "expanded" : true,
31849         /**
31850          * @event slideshow
31851          * Fires when this region is slid into view.
31852          * @param {Roo.LayoutRegion} this
31853          */
31854         "slideshow" : true,
31855         /**
31856          * @event slidehide
31857          * Fires when this region slides out of view. 
31858          * @param {Roo.LayoutRegion} this
31859          */
31860         "slidehide" : true,
31861         /**
31862          * @event panelactivated
31863          * Fires when a panel is activated. 
31864          * @param {Roo.LayoutRegion} this
31865          * @param {Roo.ContentPanel} panel The activated panel
31866          */
31867         "panelactivated" : true,
31868         /**
31869          * @event resized
31870          * Fires when the user resizes this region. 
31871          * @param {Roo.LayoutRegion} this
31872          * @param {Number} newSize The new size (width for east/west, height for north/south)
31873          */
31874         "resized" : true
31875     };
31876     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31877     this.panels = new Roo.util.MixedCollection();
31878     this.panels.getKey = this.getPanelId.createDelegate(this);
31879     this.box = null;
31880     this.activePanel = null;
31881     // ensure listeners are added...
31882     
31883     if (config.listeners || config.events) {
31884         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31885             listeners : config.listeners || {},
31886             events : config.events || {}
31887         });
31888     }
31889     
31890     if(skipConfig !== true){
31891         this.applyConfig(config);
31892     }
31893 };
31894
31895 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31896 {
31897     getPanelId : function(p){
31898         return p.getId();
31899     },
31900     
31901     applyConfig : function(config){
31902         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31903         this.config = config;
31904         
31905     },
31906     
31907     /**
31908      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31909      * the width, for horizontal (north, south) the height.
31910      * @param {Number} newSize The new width or height
31911      */
31912     resizeTo : function(newSize){
31913         var el = this.el ? this.el :
31914                  (this.activePanel ? this.activePanel.getEl() : null);
31915         if(el){
31916             switch(this.position){
31917                 case "east":
31918                 case "west":
31919                     el.setWidth(newSize);
31920                     this.fireEvent("resized", this, newSize);
31921                 break;
31922                 case "north":
31923                 case "south":
31924                     el.setHeight(newSize);
31925                     this.fireEvent("resized", this, newSize);
31926                 break;                
31927             }
31928         }
31929     },
31930     
31931     getBox : function(){
31932         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31933     },
31934     
31935     getMargins : function(){
31936         return this.margins;
31937     },
31938     
31939     updateBox : function(box){
31940         this.box = box;
31941         var el = this.activePanel.getEl();
31942         el.dom.style.left = box.x + "px";
31943         el.dom.style.top = box.y + "px";
31944         this.activePanel.setSize(box.width, box.height);
31945     },
31946     
31947     /**
31948      * Returns the container element for this region.
31949      * @return {Roo.Element}
31950      */
31951     getEl : function(){
31952         return this.activePanel;
31953     },
31954     
31955     /**
31956      * Returns true if this region is currently visible.
31957      * @return {Boolean}
31958      */
31959     isVisible : function(){
31960         return this.activePanel ? true : false;
31961     },
31962     
31963     setActivePanel : function(panel){
31964         panel = this.getPanel(panel);
31965         if(this.activePanel && this.activePanel != panel){
31966             this.activePanel.setActiveState(false);
31967             this.activePanel.getEl().setLeftTop(-10000,-10000);
31968         }
31969         this.activePanel = panel;
31970         panel.setActiveState(true);
31971         if(this.box){
31972             panel.setSize(this.box.width, this.box.height);
31973         }
31974         this.fireEvent("panelactivated", this, panel);
31975         this.fireEvent("invalidated");
31976     },
31977     
31978     /**
31979      * Show the specified panel.
31980      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31981      * @return {Roo.ContentPanel} The shown panel or null
31982      */
31983     showPanel : function(panel){
31984         panel = this.getPanel(panel);
31985         if(panel){
31986             this.setActivePanel(panel);
31987         }
31988         return panel;
31989     },
31990     
31991     /**
31992      * Get the active panel for this region.
31993      * @return {Roo.ContentPanel} The active panel or null
31994      */
31995     getActivePanel : function(){
31996         return this.activePanel;
31997     },
31998     
31999     /**
32000      * Add the passed ContentPanel(s)
32001      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32002      * @return {Roo.ContentPanel} The panel added (if only one was added)
32003      */
32004     add : function(panel){
32005         if(arguments.length > 1){
32006             for(var i = 0, len = arguments.length; i < len; i++) {
32007                 this.add(arguments[i]);
32008             }
32009             return null;
32010         }
32011         if(this.hasPanel(panel)){
32012             this.showPanel(panel);
32013             return panel;
32014         }
32015         var el = panel.getEl();
32016         if(el.dom.parentNode != this.mgr.el.dom){
32017             this.mgr.el.dom.appendChild(el.dom);
32018         }
32019         if(panel.setRegion){
32020             panel.setRegion(this);
32021         }
32022         this.panels.add(panel);
32023         el.setStyle("position", "absolute");
32024         if(!panel.background){
32025             this.setActivePanel(panel);
32026             if(this.config.initialSize && this.panels.getCount()==1){
32027                 this.resizeTo(this.config.initialSize);
32028             }
32029         }
32030         this.fireEvent("paneladded", this, panel);
32031         return panel;
32032     },
32033     
32034     /**
32035      * Returns true if the panel is in this region.
32036      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32037      * @return {Boolean}
32038      */
32039     hasPanel : function(panel){
32040         if(typeof panel == "object"){ // must be panel obj
32041             panel = panel.getId();
32042         }
32043         return this.getPanel(panel) ? true : false;
32044     },
32045     
32046     /**
32047      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32048      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32049      * @param {Boolean} preservePanel Overrides the config preservePanel option
32050      * @return {Roo.ContentPanel} The panel that was removed
32051      */
32052     remove : function(panel, preservePanel){
32053         panel = this.getPanel(panel);
32054         if(!panel){
32055             return null;
32056         }
32057         var e = {};
32058         this.fireEvent("beforeremove", this, panel, e);
32059         if(e.cancel === true){
32060             return null;
32061         }
32062         var panelId = panel.getId();
32063         this.panels.removeKey(panelId);
32064         return panel;
32065     },
32066     
32067     /**
32068      * Returns the panel specified or null if it's not in this region.
32069      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32070      * @return {Roo.ContentPanel}
32071      */
32072     getPanel : function(id){
32073         if(typeof id == "object"){ // must be panel obj
32074             return id;
32075         }
32076         return this.panels.get(id);
32077     },
32078     
32079     /**
32080      * Returns this regions position (north/south/east/west/center).
32081      * @return {String} 
32082      */
32083     getPosition: function(){
32084         return this.position;    
32085     }
32086 });/*
32087  * Based on:
32088  * Ext JS Library 1.1.1
32089  * Copyright(c) 2006-2007, Ext JS, LLC.
32090  *
32091  * Originally Released Under LGPL - original licence link has changed is not relivant.
32092  *
32093  * Fork - LGPL
32094  * <script type="text/javascript">
32095  */
32096  
32097 /**
32098  * @class Roo.bootstrap.layout.Region
32099  * @extends Roo.bootstrap.layout.Basic
32100  * This class represents a region in a layout manager.
32101  
32102  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32103  * @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})
32104  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32105  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32106  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32107  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32108  * @cfg {String}    title           The title for the region (overrides panel titles)
32109  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32110  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32111  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32112  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32113  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32114  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32115  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32116  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32117  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32118  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32119
32120  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32121  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32122  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32123  * @cfg {Number}    width           For East/West panels
32124  * @cfg {Number}    height          For North/South panels
32125  * @cfg {Boolean}   split           To show the splitter
32126  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32127  * 
32128  * @cfg {string}   cls             Extra CSS classes to add to region
32129  * 
32130  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32131  * @cfg {string}   region  the region that it inhabits..
32132  *
32133
32134  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32135  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32136
32137  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32138  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32139  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32140  */
32141 Roo.bootstrap.layout.Region = function(config)
32142 {
32143     this.applyConfig(config);
32144
32145     var mgr = config.mgr;
32146     var pos = config.region;
32147     config.skipConfig = true;
32148     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32149     
32150     if (mgr.el) {
32151         this.onRender(mgr.el);   
32152     }
32153      
32154     this.visible = true;
32155     this.collapsed = false;
32156 };
32157
32158 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32159
32160     position: '', // set by wrapper (eg. north/south etc..)
32161
32162     createBody : function(){
32163         /** This region's body element 
32164         * @type Roo.Element */
32165         this.bodyEl = this.el.createChild({
32166                 tag: "div",
32167                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32168         });
32169     },
32170
32171     onRender: function(ctr, pos)
32172     {
32173         var dh = Roo.DomHelper;
32174         /** This region's container element 
32175         * @type Roo.Element */
32176         this.el = dh.append(ctr.dom, {
32177                 tag: "div",
32178                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32179             }, true);
32180         /** This region's title element 
32181         * @type Roo.Element */
32182     
32183         this.titleEl = dh.append(this.el.dom,
32184             {
32185                     tag: "div",
32186                     unselectable: "on",
32187                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32188                     children:[
32189                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32190                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32191                     ]}, true);
32192         
32193         this.titleEl.enableDisplayMode();
32194         /** This region's title text element 
32195         * @type HTMLElement */
32196         this.titleTextEl = this.titleEl.dom.firstChild;
32197         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32198         /*
32199         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32200         this.closeBtn.enableDisplayMode();
32201         this.closeBtn.on("click", this.closeClicked, this);
32202         this.closeBtn.hide();
32203     */
32204         this.createBody(this.config);
32205         if(this.config.hideWhenEmpty){
32206             this.hide();
32207             this.on("paneladded", this.validateVisibility, this);
32208             this.on("panelremoved", this.validateVisibility, this);
32209         }
32210         if(this.autoScroll){
32211             this.bodyEl.setStyle("overflow", "auto");
32212         }else{
32213             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32214         }
32215         //if(c.titlebar !== false){
32216             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32217                 this.titleEl.hide();
32218             }else{
32219                 this.titleEl.show();
32220                 if(this.config.title){
32221                     this.titleTextEl.innerHTML = this.config.title;
32222                 }
32223             }
32224         //}
32225         if(this.config.collapsed){
32226             this.collapse(true);
32227         }
32228         if(this.config.hidden){
32229             this.hide();
32230         }
32231     },
32232     
32233     applyConfig : function(c)
32234     {
32235         /*
32236          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32237             var dh = Roo.DomHelper;
32238             if(c.titlebar !== false){
32239                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32240                 this.collapseBtn.on("click", this.collapse, this);
32241                 this.collapseBtn.enableDisplayMode();
32242                 /*
32243                 if(c.showPin === true || this.showPin){
32244                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32245                     this.stickBtn.enableDisplayMode();
32246                     this.stickBtn.on("click", this.expand, this);
32247                     this.stickBtn.hide();
32248                 }
32249                 
32250             }
32251             */
32252             /** This region's collapsed element
32253             * @type Roo.Element */
32254             /*
32255              *
32256             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32257                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32258             ]}, true);
32259             
32260             if(c.floatable !== false){
32261                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32262                this.collapsedEl.on("click", this.collapseClick, this);
32263             }
32264
32265             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32266                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32267                    id: "message", unselectable: "on", style:{"float":"left"}});
32268                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32269              }
32270             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32271             this.expandBtn.on("click", this.expand, this);
32272             
32273         }
32274         
32275         if(this.collapseBtn){
32276             this.collapseBtn.setVisible(c.collapsible == true);
32277         }
32278         
32279         this.cmargins = c.cmargins || this.cmargins ||
32280                          (this.position == "west" || this.position == "east" ?
32281                              {top: 0, left: 2, right:2, bottom: 0} :
32282                              {top: 2, left: 0, right:0, bottom: 2});
32283         */
32284         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32285         
32286         
32287         this.bottomTabs = c.tabPosition != "top";
32288         
32289         this.autoScroll = c.autoScroll || false;
32290         
32291         
32292        
32293         
32294         this.duration = c.duration || .30;
32295         this.slideDuration = c.slideDuration || .45;
32296         this.config = c;
32297        
32298     },
32299     /**
32300      * Returns true if this region is currently visible.
32301      * @return {Boolean}
32302      */
32303     isVisible : function(){
32304         return this.visible;
32305     },
32306
32307     /**
32308      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32309      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32310      */
32311     //setCollapsedTitle : function(title){
32312     //    title = title || "&#160;";
32313      //   if(this.collapsedTitleTextEl){
32314       //      this.collapsedTitleTextEl.innerHTML = title;
32315        // }
32316     //},
32317
32318     getBox : function(){
32319         var b;
32320       //  if(!this.collapsed){
32321             b = this.el.getBox(false, true);
32322        // }else{
32323           //  b = this.collapsedEl.getBox(false, true);
32324         //}
32325         return b;
32326     },
32327
32328     getMargins : function(){
32329         return this.margins;
32330         //return this.collapsed ? this.cmargins : this.margins;
32331     },
32332 /*
32333     highlight : function(){
32334         this.el.addClass("x-layout-panel-dragover");
32335     },
32336
32337     unhighlight : function(){
32338         this.el.removeClass("x-layout-panel-dragover");
32339     },
32340 */
32341     updateBox : function(box)
32342     {
32343         this.box = box;
32344         if(!this.collapsed){
32345             this.el.dom.style.left = box.x + "px";
32346             this.el.dom.style.top = box.y + "px";
32347             this.updateBody(box.width, box.height);
32348         }else{
32349             this.collapsedEl.dom.style.left = box.x + "px";
32350             this.collapsedEl.dom.style.top = box.y + "px";
32351             this.collapsedEl.setSize(box.width, box.height);
32352         }
32353         if(this.tabs){
32354             this.tabs.autoSizeTabs();
32355         }
32356     },
32357
32358     updateBody : function(w, h)
32359     {
32360         if(w !== null){
32361             this.el.setWidth(w);
32362             w -= this.el.getBorderWidth("rl");
32363             if(this.config.adjustments){
32364                 w += this.config.adjustments[0];
32365             }
32366         }
32367         if(h !== null){
32368             this.el.setHeight(h);
32369             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32370             h -= this.el.getBorderWidth("tb");
32371             if(this.config.adjustments){
32372                 h += this.config.adjustments[1];
32373             }
32374             this.bodyEl.setHeight(h);
32375             if(this.tabs){
32376                 h = this.tabs.syncHeight(h);
32377             }
32378         }
32379         if(this.panelSize){
32380             w = w !== null ? w : this.panelSize.width;
32381             h = h !== null ? h : this.panelSize.height;
32382         }
32383         if(this.activePanel){
32384             var el = this.activePanel.getEl();
32385             w = w !== null ? w : el.getWidth();
32386             h = h !== null ? h : el.getHeight();
32387             this.panelSize = {width: w, height: h};
32388             this.activePanel.setSize(w, h);
32389         }
32390         if(Roo.isIE && this.tabs){
32391             this.tabs.el.repaint();
32392         }
32393     },
32394
32395     /**
32396      * Returns the container element for this region.
32397      * @return {Roo.Element}
32398      */
32399     getEl : function(){
32400         return this.el;
32401     },
32402
32403     /**
32404      * Hides this region.
32405      */
32406     hide : function(){
32407         //if(!this.collapsed){
32408             this.el.dom.style.left = "-2000px";
32409             this.el.hide();
32410         //}else{
32411          //   this.collapsedEl.dom.style.left = "-2000px";
32412          //   this.collapsedEl.hide();
32413        // }
32414         this.visible = false;
32415         this.fireEvent("visibilitychange", this, false);
32416     },
32417
32418     /**
32419      * Shows this region if it was previously hidden.
32420      */
32421     show : function(){
32422         //if(!this.collapsed){
32423             this.el.show();
32424         //}else{
32425         //    this.collapsedEl.show();
32426        // }
32427         this.visible = true;
32428         this.fireEvent("visibilitychange", this, true);
32429     },
32430 /*
32431     closeClicked : function(){
32432         if(this.activePanel){
32433             this.remove(this.activePanel);
32434         }
32435     },
32436
32437     collapseClick : function(e){
32438         if(this.isSlid){
32439            e.stopPropagation();
32440            this.slideIn();
32441         }else{
32442            e.stopPropagation();
32443            this.slideOut();
32444         }
32445     },
32446 */
32447     /**
32448      * Collapses this region.
32449      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32450      */
32451     /*
32452     collapse : function(skipAnim, skipCheck = false){
32453         if(this.collapsed) {
32454             return;
32455         }
32456         
32457         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32458             
32459             this.collapsed = true;
32460             if(this.split){
32461                 this.split.el.hide();
32462             }
32463             if(this.config.animate && skipAnim !== true){
32464                 this.fireEvent("invalidated", this);
32465                 this.animateCollapse();
32466             }else{
32467                 this.el.setLocation(-20000,-20000);
32468                 this.el.hide();
32469                 this.collapsedEl.show();
32470                 this.fireEvent("collapsed", this);
32471                 this.fireEvent("invalidated", this);
32472             }
32473         }
32474         
32475     },
32476 */
32477     animateCollapse : function(){
32478         // overridden
32479     },
32480
32481     /**
32482      * Expands this region if it was previously collapsed.
32483      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32484      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32485      */
32486     /*
32487     expand : function(e, skipAnim){
32488         if(e) {
32489             e.stopPropagation();
32490         }
32491         if(!this.collapsed || this.el.hasActiveFx()) {
32492             return;
32493         }
32494         if(this.isSlid){
32495             this.afterSlideIn();
32496             skipAnim = true;
32497         }
32498         this.collapsed = false;
32499         if(this.config.animate && skipAnim !== true){
32500             this.animateExpand();
32501         }else{
32502             this.el.show();
32503             if(this.split){
32504                 this.split.el.show();
32505             }
32506             this.collapsedEl.setLocation(-2000,-2000);
32507             this.collapsedEl.hide();
32508             this.fireEvent("invalidated", this);
32509             this.fireEvent("expanded", this);
32510         }
32511     },
32512 */
32513     animateExpand : function(){
32514         // overridden
32515     },
32516
32517     initTabs : function()
32518     {
32519         this.bodyEl.setStyle("overflow", "hidden");
32520         var ts = new Roo.bootstrap.panel.Tabs({
32521                 el: this.bodyEl.dom,
32522                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32523                 disableTooltips: this.config.disableTabTips,
32524                 toolbar : this.config.toolbar
32525             });
32526         
32527         if(this.config.hideTabs){
32528             ts.stripWrap.setDisplayed(false);
32529         }
32530         this.tabs = ts;
32531         ts.resizeTabs = this.config.resizeTabs === true;
32532         ts.minTabWidth = this.config.minTabWidth || 40;
32533         ts.maxTabWidth = this.config.maxTabWidth || 250;
32534         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32535         ts.monitorResize = false;
32536         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32537         ts.bodyEl.addClass('roo-layout-tabs-body');
32538         this.panels.each(this.initPanelAsTab, this);
32539     },
32540
32541     initPanelAsTab : function(panel){
32542         var ti = this.tabs.addTab(
32543                     panel.getEl().id,
32544                     panel.getTitle(), null,
32545                     this.config.closeOnTab && panel.isClosable()
32546             );
32547         if(panel.tabTip !== undefined){
32548             ti.setTooltip(panel.tabTip);
32549         }
32550         ti.on("activate", function(){
32551               this.setActivePanel(panel);
32552         }, this);
32553         
32554         if(this.config.closeOnTab){
32555             ti.on("beforeclose", function(t, e){
32556                 e.cancel = true;
32557                 this.remove(panel);
32558             }, this);
32559         }
32560         return ti;
32561     },
32562
32563     updatePanelTitle : function(panel, title)
32564     {
32565         if(this.activePanel == panel){
32566             this.updateTitle(title);
32567         }
32568         if(this.tabs){
32569             var ti = this.tabs.getTab(panel.getEl().id);
32570             ti.setText(title);
32571             if(panel.tabTip !== undefined){
32572                 ti.setTooltip(panel.tabTip);
32573             }
32574         }
32575     },
32576
32577     updateTitle : function(title){
32578         if(this.titleTextEl && !this.config.title){
32579             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32580         }
32581     },
32582
32583     setActivePanel : function(panel)
32584     {
32585         panel = this.getPanel(panel);
32586         if(this.activePanel && this.activePanel != panel){
32587             this.activePanel.setActiveState(false);
32588         }
32589         this.activePanel = panel;
32590         panel.setActiveState(true);
32591         if(this.panelSize){
32592             panel.setSize(this.panelSize.width, this.panelSize.height);
32593         }
32594         if(this.closeBtn){
32595             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32596         }
32597         this.updateTitle(panel.getTitle());
32598         if(this.tabs){
32599             this.fireEvent("invalidated", this);
32600         }
32601         this.fireEvent("panelactivated", this, panel);
32602     },
32603
32604     /**
32605      * Shows the specified panel.
32606      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32607      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32608      */
32609     showPanel : function(panel)
32610     {
32611         panel = this.getPanel(panel);
32612         if(panel){
32613             if(this.tabs){
32614                 var tab = this.tabs.getTab(panel.getEl().id);
32615                 if(tab.isHidden()){
32616                     this.tabs.unhideTab(tab.id);
32617                 }
32618                 tab.activate();
32619             }else{
32620                 this.setActivePanel(panel);
32621             }
32622         }
32623         return panel;
32624     },
32625
32626     /**
32627      * Get the active panel for this region.
32628      * @return {Roo.ContentPanel} The active panel or null
32629      */
32630     getActivePanel : function(){
32631         return this.activePanel;
32632     },
32633
32634     validateVisibility : function(){
32635         if(this.panels.getCount() < 1){
32636             this.updateTitle("&#160;");
32637             this.closeBtn.hide();
32638             this.hide();
32639         }else{
32640             if(!this.isVisible()){
32641                 this.show();
32642             }
32643         }
32644     },
32645
32646     /**
32647      * Adds the passed ContentPanel(s) to this region.
32648      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32649      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32650      */
32651     add : function(panel){
32652         if(arguments.length > 1){
32653             for(var i = 0, len = arguments.length; i < len; i++) {
32654                 this.add(arguments[i]);
32655             }
32656             return null;
32657         }
32658         if(this.hasPanel(panel)){
32659             this.showPanel(panel);
32660             return panel;
32661         }
32662         panel.setRegion(this);
32663         this.panels.add(panel);
32664         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32665             this.bodyEl.dom.appendChild(panel.getEl().dom);
32666             if(panel.background !== true){
32667                 this.setActivePanel(panel);
32668             }
32669             this.fireEvent("paneladded", this, panel);
32670             return panel;
32671         }
32672         if(!this.tabs){
32673             this.initTabs();
32674         }else{
32675             this.initPanelAsTab(panel);
32676         }
32677         
32678         
32679         if(panel.background !== true){
32680             this.tabs.activate(panel.getEl().id);
32681         }
32682         this.fireEvent("paneladded", this, panel);
32683         return panel;
32684     },
32685
32686     /**
32687      * Hides the tab for the specified panel.
32688      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32689      */
32690     hidePanel : function(panel){
32691         if(this.tabs && (panel = this.getPanel(panel))){
32692             this.tabs.hideTab(panel.getEl().id);
32693         }
32694     },
32695
32696     /**
32697      * Unhides the tab for a previously hidden panel.
32698      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32699      */
32700     unhidePanel : function(panel){
32701         if(this.tabs && (panel = this.getPanel(panel))){
32702             this.tabs.unhideTab(panel.getEl().id);
32703         }
32704     },
32705
32706     clearPanels : function(){
32707         while(this.panels.getCount() > 0){
32708              this.remove(this.panels.first());
32709         }
32710     },
32711
32712     /**
32713      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32714      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32715      * @param {Boolean} preservePanel Overrides the config preservePanel option
32716      * @return {Roo.ContentPanel} The panel that was removed
32717      */
32718     remove : function(panel, preservePanel)
32719     {
32720         panel = this.getPanel(panel);
32721         if(!panel){
32722             return null;
32723         }
32724         var e = {};
32725         this.fireEvent("beforeremove", this, panel, e);
32726         if(e.cancel === true){
32727             return null;
32728         }
32729         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32730         var panelId = panel.getId();
32731         this.panels.removeKey(panelId);
32732         if(preservePanel){
32733             document.body.appendChild(panel.getEl().dom);
32734         }
32735         if(this.tabs){
32736             this.tabs.removeTab(panel.getEl().id);
32737         }else if (!preservePanel){
32738             this.bodyEl.dom.removeChild(panel.getEl().dom);
32739         }
32740         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32741             var p = this.panels.first();
32742             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32743             tempEl.appendChild(p.getEl().dom);
32744             this.bodyEl.update("");
32745             this.bodyEl.dom.appendChild(p.getEl().dom);
32746             tempEl = null;
32747             this.updateTitle(p.getTitle());
32748             this.tabs = null;
32749             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32750             this.setActivePanel(p);
32751         }
32752         panel.setRegion(null);
32753         if(this.activePanel == panel){
32754             this.activePanel = null;
32755         }
32756         if(this.config.autoDestroy !== false && preservePanel !== true){
32757             try{panel.destroy();}catch(e){}
32758         }
32759         this.fireEvent("panelremoved", this, panel);
32760         return panel;
32761     },
32762
32763     /**
32764      * Returns the TabPanel component used by this region
32765      * @return {Roo.TabPanel}
32766      */
32767     getTabs : function(){
32768         return this.tabs;
32769     },
32770
32771     createTool : function(parentEl, className){
32772         var btn = Roo.DomHelper.append(parentEl, {
32773             tag: "div",
32774             cls: "x-layout-tools-button",
32775             children: [ {
32776                 tag: "div",
32777                 cls: "roo-layout-tools-button-inner " + className,
32778                 html: "&#160;"
32779             }]
32780         }, true);
32781         btn.addClassOnOver("roo-layout-tools-button-over");
32782         return btn;
32783     }
32784 });/*
32785  * Based on:
32786  * Ext JS Library 1.1.1
32787  * Copyright(c) 2006-2007, Ext JS, LLC.
32788  *
32789  * Originally Released Under LGPL - original licence link has changed is not relivant.
32790  *
32791  * Fork - LGPL
32792  * <script type="text/javascript">
32793  */
32794  
32795
32796
32797 /**
32798  * @class Roo.SplitLayoutRegion
32799  * @extends Roo.LayoutRegion
32800  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32801  */
32802 Roo.bootstrap.layout.Split = function(config){
32803     this.cursor = config.cursor;
32804     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32805 };
32806
32807 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32808 {
32809     splitTip : "Drag to resize.",
32810     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32811     useSplitTips : false,
32812
32813     applyConfig : function(config){
32814         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32815     },
32816     
32817     onRender : function(ctr,pos) {
32818         
32819         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
32820         if(!this.config.split){
32821             return;
32822         }
32823         if(!this.split){
32824             
32825             var splitEl = Roo.DomHelper.append(ctr.dom,  {
32826                             tag: "div",
32827                             id: this.el.id + "-split",
32828                             cls: "roo-layout-split roo-layout-split-"+this.position,
32829                             html: "&#160;"
32830             });
32831             /** The SplitBar for this region 
32832             * @type Roo.SplitBar */
32833             // does not exist yet...
32834             Roo.log([this.position, this.orientation]);
32835             
32836             this.split = new Roo.bootstrap.SplitBar({
32837                 dragElement : splitEl,
32838                 resizingElement: this.el,
32839                 orientation : this.orientation
32840             });
32841             
32842             this.split.on("moved", this.onSplitMove, this);
32843             this.split.useShim = this.config.useShim === true;
32844             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32845             if(this.useSplitTips){
32846                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32847             }
32848             //if(config.collapsible){
32849             //    this.split.el.on("dblclick", this.collapse,  this);
32850             //}
32851         }
32852         if(typeof this.config.minSize != "undefined"){
32853             this.split.minSize = this.config.minSize;
32854         }
32855         if(typeof this.config.maxSize != "undefined"){
32856             this.split.maxSize = this.config.maxSize;
32857         }
32858         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
32859             this.hideSplitter();
32860         }
32861         
32862     },
32863
32864     getHMaxSize : function(){
32865          var cmax = this.config.maxSize || 10000;
32866          var center = this.mgr.getRegion("center");
32867          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32868     },
32869
32870     getVMaxSize : function(){
32871          var cmax = this.config.maxSize || 10000;
32872          var center = this.mgr.getRegion("center");
32873          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32874     },
32875
32876     onSplitMove : function(split, newSize){
32877         this.fireEvent("resized", this, newSize);
32878     },
32879     
32880     /** 
32881      * Returns the {@link Roo.SplitBar} for this region.
32882      * @return {Roo.SplitBar}
32883      */
32884     getSplitBar : function(){
32885         return this.split;
32886     },
32887     
32888     hide : function(){
32889         this.hideSplitter();
32890         Roo.bootstrap.layout.Split.superclass.hide.call(this);
32891     },
32892
32893     hideSplitter : function(){
32894         if(this.split){
32895             this.split.el.setLocation(-2000,-2000);
32896             this.split.el.hide();
32897         }
32898     },
32899
32900     show : function(){
32901         if(this.split){
32902             this.split.el.show();
32903         }
32904         Roo.bootstrap.layout.Split.superclass.show.call(this);
32905     },
32906     
32907     beforeSlide: function(){
32908         if(Roo.isGecko){// firefox overflow auto bug workaround
32909             this.bodyEl.clip();
32910             if(this.tabs) {
32911                 this.tabs.bodyEl.clip();
32912             }
32913             if(this.activePanel){
32914                 this.activePanel.getEl().clip();
32915                 
32916                 if(this.activePanel.beforeSlide){
32917                     this.activePanel.beforeSlide();
32918                 }
32919             }
32920         }
32921     },
32922     
32923     afterSlide : function(){
32924         if(Roo.isGecko){// firefox overflow auto bug workaround
32925             this.bodyEl.unclip();
32926             if(this.tabs) {
32927                 this.tabs.bodyEl.unclip();
32928             }
32929             if(this.activePanel){
32930                 this.activePanel.getEl().unclip();
32931                 if(this.activePanel.afterSlide){
32932                     this.activePanel.afterSlide();
32933                 }
32934             }
32935         }
32936     },
32937
32938     initAutoHide : function(){
32939         if(this.autoHide !== false){
32940             if(!this.autoHideHd){
32941                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32942                 this.autoHideHd = {
32943                     "mouseout": function(e){
32944                         if(!e.within(this.el, true)){
32945                             st.delay(500);
32946                         }
32947                     },
32948                     "mouseover" : function(e){
32949                         st.cancel();
32950                     },
32951                     scope : this
32952                 };
32953             }
32954             this.el.on(this.autoHideHd);
32955         }
32956     },
32957
32958     clearAutoHide : function(){
32959         if(this.autoHide !== false){
32960             this.el.un("mouseout", this.autoHideHd.mouseout);
32961             this.el.un("mouseover", this.autoHideHd.mouseover);
32962         }
32963     },
32964
32965     clearMonitor : function(){
32966         Roo.get(document).un("click", this.slideInIf, this);
32967     },
32968
32969     // these names are backwards but not changed for compat
32970     slideOut : function(){
32971         if(this.isSlid || this.el.hasActiveFx()){
32972             return;
32973         }
32974         this.isSlid = true;
32975         if(this.collapseBtn){
32976             this.collapseBtn.hide();
32977         }
32978         this.closeBtnState = this.closeBtn.getStyle('display');
32979         this.closeBtn.hide();
32980         if(this.stickBtn){
32981             this.stickBtn.show();
32982         }
32983         this.el.show();
32984         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32985         this.beforeSlide();
32986         this.el.setStyle("z-index", 10001);
32987         this.el.slideIn(this.getSlideAnchor(), {
32988             callback: function(){
32989                 this.afterSlide();
32990                 this.initAutoHide();
32991                 Roo.get(document).on("click", this.slideInIf, this);
32992                 this.fireEvent("slideshow", this);
32993             },
32994             scope: this,
32995             block: true
32996         });
32997     },
32998
32999     afterSlideIn : function(){
33000         this.clearAutoHide();
33001         this.isSlid = false;
33002         this.clearMonitor();
33003         this.el.setStyle("z-index", "");
33004         if(this.collapseBtn){
33005             this.collapseBtn.show();
33006         }
33007         this.closeBtn.setStyle('display', this.closeBtnState);
33008         if(this.stickBtn){
33009             this.stickBtn.hide();
33010         }
33011         this.fireEvent("slidehide", this);
33012     },
33013
33014     slideIn : function(cb){
33015         if(!this.isSlid || this.el.hasActiveFx()){
33016             Roo.callback(cb);
33017             return;
33018         }
33019         this.isSlid = false;
33020         this.beforeSlide();
33021         this.el.slideOut(this.getSlideAnchor(), {
33022             callback: function(){
33023                 this.el.setLeftTop(-10000, -10000);
33024                 this.afterSlide();
33025                 this.afterSlideIn();
33026                 Roo.callback(cb);
33027             },
33028             scope: this,
33029             block: true
33030         });
33031     },
33032     
33033     slideInIf : function(e){
33034         if(!e.within(this.el)){
33035             this.slideIn();
33036         }
33037     },
33038
33039     animateCollapse : function(){
33040         this.beforeSlide();
33041         this.el.setStyle("z-index", 20000);
33042         var anchor = this.getSlideAnchor();
33043         this.el.slideOut(anchor, {
33044             callback : function(){
33045                 this.el.setStyle("z-index", "");
33046                 this.collapsedEl.slideIn(anchor, {duration:.3});
33047                 this.afterSlide();
33048                 this.el.setLocation(-10000,-10000);
33049                 this.el.hide();
33050                 this.fireEvent("collapsed", this);
33051             },
33052             scope: this,
33053             block: true
33054         });
33055     },
33056
33057     animateExpand : function(){
33058         this.beforeSlide();
33059         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33060         this.el.setStyle("z-index", 20000);
33061         this.collapsedEl.hide({
33062             duration:.1
33063         });
33064         this.el.slideIn(this.getSlideAnchor(), {
33065             callback : function(){
33066                 this.el.setStyle("z-index", "");
33067                 this.afterSlide();
33068                 if(this.split){
33069                     this.split.el.show();
33070                 }
33071                 this.fireEvent("invalidated", this);
33072                 this.fireEvent("expanded", this);
33073             },
33074             scope: this,
33075             block: true
33076         });
33077     },
33078
33079     anchors : {
33080         "west" : "left",
33081         "east" : "right",
33082         "north" : "top",
33083         "south" : "bottom"
33084     },
33085
33086     sanchors : {
33087         "west" : "l",
33088         "east" : "r",
33089         "north" : "t",
33090         "south" : "b"
33091     },
33092
33093     canchors : {
33094         "west" : "tl-tr",
33095         "east" : "tr-tl",
33096         "north" : "tl-bl",
33097         "south" : "bl-tl"
33098     },
33099
33100     getAnchor : function(){
33101         return this.anchors[this.position];
33102     },
33103
33104     getCollapseAnchor : function(){
33105         return this.canchors[this.position];
33106     },
33107
33108     getSlideAnchor : function(){
33109         return this.sanchors[this.position];
33110     },
33111
33112     getAlignAdj : function(){
33113         var cm = this.cmargins;
33114         switch(this.position){
33115             case "west":
33116                 return [0, 0];
33117             break;
33118             case "east":
33119                 return [0, 0];
33120             break;
33121             case "north":
33122                 return [0, 0];
33123             break;
33124             case "south":
33125                 return [0, 0];
33126             break;
33127         }
33128     },
33129
33130     getExpandAdj : function(){
33131         var c = this.collapsedEl, cm = this.cmargins;
33132         switch(this.position){
33133             case "west":
33134                 return [-(cm.right+c.getWidth()+cm.left), 0];
33135             break;
33136             case "east":
33137                 return [cm.right+c.getWidth()+cm.left, 0];
33138             break;
33139             case "north":
33140                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33141             break;
33142             case "south":
33143                 return [0, cm.top+cm.bottom+c.getHeight()];
33144             break;
33145         }
33146     }
33147 });/*
33148  * Based on:
33149  * Ext JS Library 1.1.1
33150  * Copyright(c) 2006-2007, Ext JS, LLC.
33151  *
33152  * Originally Released Under LGPL - original licence link has changed is not relivant.
33153  *
33154  * Fork - LGPL
33155  * <script type="text/javascript">
33156  */
33157 /*
33158  * These classes are private internal classes
33159  */
33160 Roo.bootstrap.layout.Center = function(config){
33161     config.region = "center";
33162     Roo.bootstrap.layout.Region.call(this, config);
33163     this.visible = true;
33164     this.minWidth = config.minWidth || 20;
33165     this.minHeight = config.minHeight || 20;
33166 };
33167
33168 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33169     hide : function(){
33170         // center panel can't be hidden
33171     },
33172     
33173     show : function(){
33174         // center panel can't be hidden
33175     },
33176     
33177     getMinWidth: function(){
33178         return this.minWidth;
33179     },
33180     
33181     getMinHeight: function(){
33182         return this.minHeight;
33183     }
33184 });
33185
33186
33187
33188
33189  
33190
33191
33192
33193
33194
33195 Roo.bootstrap.layout.North = function(config)
33196 {
33197     config.region = 'north';
33198     config.cursor = 'n-resize';
33199     
33200     Roo.bootstrap.layout.Split.call(this, config);
33201     
33202     
33203     if(this.split){
33204         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33205         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33206         this.split.el.addClass("roo-layout-split-v");
33207     }
33208     var size = config.initialSize || config.height;
33209     if(typeof size != "undefined"){
33210         this.el.setHeight(size);
33211     }
33212 };
33213 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33214 {
33215     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33216     
33217     
33218     
33219     getBox : function(){
33220         if(this.collapsed){
33221             return this.collapsedEl.getBox();
33222         }
33223         var box = this.el.getBox();
33224         if(this.split){
33225             box.height += this.split.el.getHeight();
33226         }
33227         return box;
33228     },
33229     
33230     updateBox : function(box){
33231         if(this.split && !this.collapsed){
33232             box.height -= this.split.el.getHeight();
33233             this.split.el.setLeft(box.x);
33234             this.split.el.setTop(box.y+box.height);
33235             this.split.el.setWidth(box.width);
33236         }
33237         if(this.collapsed){
33238             this.updateBody(box.width, null);
33239         }
33240         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33241     }
33242 });
33243
33244
33245
33246
33247
33248 Roo.bootstrap.layout.South = function(config){
33249     config.region = 'south';
33250     config.cursor = 's-resize';
33251     Roo.bootstrap.layout.Split.call(this, config);
33252     if(this.split){
33253         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33254         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33255         this.split.el.addClass("roo-layout-split-v");
33256     }
33257     var size = config.initialSize || config.height;
33258     if(typeof size != "undefined"){
33259         this.el.setHeight(size);
33260     }
33261 };
33262
33263 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33264     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33265     getBox : function(){
33266         if(this.collapsed){
33267             return this.collapsedEl.getBox();
33268         }
33269         var box = this.el.getBox();
33270         if(this.split){
33271             var sh = this.split.el.getHeight();
33272             box.height += sh;
33273             box.y -= sh;
33274         }
33275         return box;
33276     },
33277     
33278     updateBox : function(box){
33279         if(this.split && !this.collapsed){
33280             var sh = this.split.el.getHeight();
33281             box.height -= sh;
33282             box.y += sh;
33283             this.split.el.setLeft(box.x);
33284             this.split.el.setTop(box.y-sh);
33285             this.split.el.setWidth(box.width);
33286         }
33287         if(this.collapsed){
33288             this.updateBody(box.width, null);
33289         }
33290         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33291     }
33292 });
33293
33294 Roo.bootstrap.layout.East = function(config){
33295     config.region = "east";
33296     config.cursor = "e-resize";
33297     Roo.bootstrap.layout.Split.call(this, config);
33298     if(this.split){
33299         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33300         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33301         this.split.el.addClass("roo-layout-split-h");
33302     }
33303     var size = config.initialSize || config.width;
33304     if(typeof size != "undefined"){
33305         this.el.setWidth(size);
33306     }
33307 };
33308 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33309     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33310     getBox : function(){
33311         if(this.collapsed){
33312             return this.collapsedEl.getBox();
33313         }
33314         var box = this.el.getBox();
33315         if(this.split){
33316             var sw = this.split.el.getWidth();
33317             box.width += sw;
33318             box.x -= sw;
33319         }
33320         return box;
33321     },
33322
33323     updateBox : function(box){
33324         if(this.split && !this.collapsed){
33325             var sw = this.split.el.getWidth();
33326             box.width -= sw;
33327             this.split.el.setLeft(box.x);
33328             this.split.el.setTop(box.y);
33329             this.split.el.setHeight(box.height);
33330             box.x += sw;
33331         }
33332         if(this.collapsed){
33333             this.updateBody(null, box.height);
33334         }
33335         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33336     }
33337 });
33338
33339 Roo.bootstrap.layout.West = function(config){
33340     config.region = "west";
33341     config.cursor = "w-resize";
33342     
33343     Roo.bootstrap.layout.Split.call(this, config);
33344     if(this.split){
33345         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33346         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33347         this.split.el.addClass("roo-layout-split-h");
33348     }
33349     
33350 };
33351 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33352     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33353     
33354     onRender: function(ctr, pos)
33355     {
33356         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33357         var size = this.config.initialSize || this.config.width;
33358         if(typeof size != "undefined"){
33359             this.el.setWidth(size);
33360         }
33361     },
33362     
33363     getBox : function(){
33364         if(this.collapsed){
33365             return this.collapsedEl.getBox();
33366         }
33367         var box = this.el.getBox();
33368         if(this.split){
33369             box.width += this.split.el.getWidth();
33370         }
33371         return box;
33372     },
33373     
33374     updateBox : function(box){
33375         if(this.split && !this.collapsed){
33376             var sw = this.split.el.getWidth();
33377             box.width -= sw;
33378             this.split.el.setLeft(box.x+box.width);
33379             this.split.el.setTop(box.y);
33380             this.split.el.setHeight(box.height);
33381         }
33382         if(this.collapsed){
33383             this.updateBody(null, box.height);
33384         }
33385         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33386     }
33387 });
33388 Roo.namespace("Roo.bootstrap.panel");/*
33389  * Based on:
33390  * Ext JS Library 1.1.1
33391  * Copyright(c) 2006-2007, Ext JS, LLC.
33392  *
33393  * Originally Released Under LGPL - original licence link has changed is not relivant.
33394  *
33395  * Fork - LGPL
33396  * <script type="text/javascript">
33397  */
33398 /**
33399  * @class Roo.ContentPanel
33400  * @extends Roo.util.Observable
33401  * A basic ContentPanel element.
33402  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33403  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33404  * @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
33405  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33406  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33407  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33408  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33409  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33410  * @cfg {String} title          The title for this panel
33411  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33412  * @cfg {String} url            Calls {@link #setUrl} with this value
33413  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33414  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33415  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33416  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33417
33418  * @constructor
33419  * Create a new ContentPanel.
33420  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33421  * @param {String/Object} config A string to set only the title or a config object
33422  * @param {String} content (optional) Set the HTML content for this panel
33423  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33424  */
33425 Roo.bootstrap.panel.Content = function( config){
33426     
33427     var el = config.el;
33428     var content = config.content;
33429
33430     if(config.autoCreate){ // xtype is available if this is called from factory
33431         el = Roo.id();
33432     }
33433     this.el = Roo.get(el);
33434     if(!this.el && config && config.autoCreate){
33435         if(typeof config.autoCreate == "object"){
33436             if(!config.autoCreate.id){
33437                 config.autoCreate.id = config.id||el;
33438             }
33439             this.el = Roo.DomHelper.append(document.body,
33440                         config.autoCreate, true);
33441         }else{
33442             var elcfg =  {   tag: "div",
33443                             cls: "roo-layout-inactive-content",
33444                             id: config.id||el
33445                             };
33446             if (config.html) {
33447                 elcfg.html = config.html;
33448                 
33449             }
33450                         
33451             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33452         }
33453     } 
33454     this.closable = false;
33455     this.loaded = false;
33456     this.active = false;
33457    
33458       
33459     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33460         
33461         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33462         
33463         this.wrapEl = this.el.wrap();
33464         var ti = [];
33465         if (config.toolbar.items) {
33466             ti = config.toolbar.items ;
33467             delete config.toolbar.items ;
33468         }
33469         
33470         var nitems = [];
33471         this.toolbar.render(this.wrapEl, 'before');
33472         for(var i =0;i < ti.length;i++) {
33473           //  Roo.log(['add child', items[i]]);
33474             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33475         }
33476         this.toolbar.items = nitems;
33477         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33478         delete config.toolbar;
33479         
33480     }
33481     /*
33482     // xtype created footer. - not sure if will work as we normally have to render first..
33483     if (this.footer && !this.footer.el && this.footer.xtype) {
33484         if (!this.wrapEl) {
33485             this.wrapEl = this.el.wrap();
33486         }
33487     
33488         this.footer.container = this.wrapEl.createChild();
33489          
33490         this.footer = Roo.factory(this.footer, Roo);
33491         
33492     }
33493     */
33494     
33495      if(typeof config == "string"){
33496         this.title = config;
33497     }else{
33498         Roo.apply(this, config);
33499     }
33500     
33501     if(this.resizeEl){
33502         this.resizeEl = Roo.get(this.resizeEl, true);
33503     }else{
33504         this.resizeEl = this.el;
33505     }
33506     // handle view.xtype
33507     
33508  
33509     
33510     
33511     this.addEvents({
33512         /**
33513          * @event activate
33514          * Fires when this panel is activated. 
33515          * @param {Roo.ContentPanel} this
33516          */
33517         "activate" : true,
33518         /**
33519          * @event deactivate
33520          * Fires when this panel is activated. 
33521          * @param {Roo.ContentPanel} this
33522          */
33523         "deactivate" : true,
33524
33525         /**
33526          * @event resize
33527          * Fires when this panel is resized if fitToFrame is true.
33528          * @param {Roo.ContentPanel} this
33529          * @param {Number} width The width after any component adjustments
33530          * @param {Number} height The height after any component adjustments
33531          */
33532         "resize" : true,
33533         
33534          /**
33535          * @event render
33536          * Fires when this tab is created
33537          * @param {Roo.ContentPanel} this
33538          */
33539         "render" : true
33540         
33541         
33542         
33543     });
33544     
33545
33546     
33547     
33548     if(this.autoScroll){
33549         this.resizeEl.setStyle("overflow", "auto");
33550     } else {
33551         // fix randome scrolling
33552         //this.el.on('scroll', function() {
33553         //    Roo.log('fix random scolling');
33554         //    this.scrollTo('top',0); 
33555         //});
33556     }
33557     content = content || this.content;
33558     if(content){
33559         this.setContent(content);
33560     }
33561     if(config && config.url){
33562         this.setUrl(this.url, this.params, this.loadOnce);
33563     }
33564     
33565     
33566     
33567     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33568     
33569     if (this.view && typeof(this.view.xtype) != 'undefined') {
33570         this.view.el = this.el.appendChild(document.createElement("div"));
33571         this.view = Roo.factory(this.view); 
33572         this.view.render  &&  this.view.render(false, '');  
33573     }
33574     
33575     
33576     this.fireEvent('render', this);
33577 };
33578
33579 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33580     tabTip:'',
33581     setRegion : function(region){
33582         this.region = region;
33583         if(region){
33584            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33585         }else{
33586            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33587         } 
33588     },
33589     
33590     /**
33591      * Returns the toolbar for this Panel if one was configured. 
33592      * @return {Roo.Toolbar} 
33593      */
33594     getToolbar : function(){
33595         return this.toolbar;
33596     },
33597     
33598     setActiveState : function(active){
33599         this.active = active;
33600         if(!active){
33601             this.fireEvent("deactivate", this);
33602         }else{
33603             this.fireEvent("activate", this);
33604         }
33605     },
33606     /**
33607      * Updates this panel's element
33608      * @param {String} content The new content
33609      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33610     */
33611     setContent : function(content, loadScripts){
33612         this.el.update(content, loadScripts);
33613     },
33614
33615     ignoreResize : function(w, h){
33616         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33617             return true;
33618         }else{
33619             this.lastSize = {width: w, height: h};
33620             return false;
33621         }
33622     },
33623     /**
33624      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33625      * @return {Roo.UpdateManager} The UpdateManager
33626      */
33627     getUpdateManager : function(){
33628         return this.el.getUpdateManager();
33629     },
33630      /**
33631      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33632      * @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:
33633 <pre><code>
33634 panel.load({
33635     url: "your-url.php",
33636     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33637     callback: yourFunction,
33638     scope: yourObject, //(optional scope)
33639     discardUrl: false,
33640     nocache: false,
33641     text: "Loading...",
33642     timeout: 30,
33643     scripts: false
33644 });
33645 </code></pre>
33646      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33647      * 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.
33648      * @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}
33649      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33650      * @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.
33651      * @return {Roo.ContentPanel} this
33652      */
33653     load : function(){
33654         var um = this.el.getUpdateManager();
33655         um.update.apply(um, arguments);
33656         return this;
33657     },
33658
33659
33660     /**
33661      * 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.
33662      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33663      * @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)
33664      * @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)
33665      * @return {Roo.UpdateManager} The UpdateManager
33666      */
33667     setUrl : function(url, params, loadOnce){
33668         if(this.refreshDelegate){
33669             this.removeListener("activate", this.refreshDelegate);
33670         }
33671         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33672         this.on("activate", this.refreshDelegate);
33673         return this.el.getUpdateManager();
33674     },
33675     
33676     _handleRefresh : function(url, params, loadOnce){
33677         if(!loadOnce || !this.loaded){
33678             var updater = this.el.getUpdateManager();
33679             updater.update(url, params, this._setLoaded.createDelegate(this));
33680         }
33681     },
33682     
33683     _setLoaded : function(){
33684         this.loaded = true;
33685     }, 
33686     
33687     /**
33688      * Returns this panel's id
33689      * @return {String} 
33690      */
33691     getId : function(){
33692         return this.el.id;
33693     },
33694     
33695     /** 
33696      * Returns this panel's element - used by regiosn to add.
33697      * @return {Roo.Element} 
33698      */
33699     getEl : function(){
33700         return this.wrapEl || this.el;
33701     },
33702     
33703    
33704     
33705     adjustForComponents : function(width, height)
33706     {
33707         //Roo.log('adjustForComponents ');
33708         if(this.resizeEl != this.el){
33709             width -= this.el.getFrameWidth('lr');
33710             height -= this.el.getFrameWidth('tb');
33711         }
33712         if(this.toolbar){
33713             var te = this.toolbar.getEl();
33714             height -= te.getHeight();
33715             te.setWidth(width);
33716         }
33717         if(this.footer){
33718             var te = this.footer.getEl();
33719             Roo.log("footer:" + te.getHeight());
33720             
33721             height -= te.getHeight();
33722             te.setWidth(width);
33723         }
33724         
33725         
33726         if(this.adjustments){
33727             width += this.adjustments[0];
33728             height += this.adjustments[1];
33729         }
33730         return {"width": width, "height": height};
33731     },
33732     
33733     setSize : function(width, height){
33734         if(this.fitToFrame && !this.ignoreResize(width, height)){
33735             if(this.fitContainer && this.resizeEl != this.el){
33736                 this.el.setSize(width, height);
33737             }
33738             var size = this.adjustForComponents(width, height);
33739             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33740             this.fireEvent('resize', this, size.width, size.height);
33741         }
33742     },
33743     
33744     /**
33745      * Returns this panel's title
33746      * @return {String} 
33747      */
33748     getTitle : function(){
33749         return this.title;
33750     },
33751     
33752     /**
33753      * Set this panel's title
33754      * @param {String} title
33755      */
33756     setTitle : function(title){
33757         this.title = title;
33758         if(this.region){
33759             this.region.updatePanelTitle(this, title);
33760         }
33761     },
33762     
33763     /**
33764      * Returns true is this panel was configured to be closable
33765      * @return {Boolean} 
33766      */
33767     isClosable : function(){
33768         return this.closable;
33769     },
33770     
33771     beforeSlide : function(){
33772         this.el.clip();
33773         this.resizeEl.clip();
33774     },
33775     
33776     afterSlide : function(){
33777         this.el.unclip();
33778         this.resizeEl.unclip();
33779     },
33780     
33781     /**
33782      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33783      *   Will fail silently if the {@link #setUrl} method has not been called.
33784      *   This does not activate the panel, just updates its content.
33785      */
33786     refresh : function(){
33787         if(this.refreshDelegate){
33788            this.loaded = false;
33789            this.refreshDelegate();
33790         }
33791     },
33792     
33793     /**
33794      * Destroys this panel
33795      */
33796     destroy : function(){
33797         this.el.removeAllListeners();
33798         var tempEl = document.createElement("span");
33799         tempEl.appendChild(this.el.dom);
33800         tempEl.innerHTML = "";
33801         this.el.remove();
33802         this.el = null;
33803     },
33804     
33805     /**
33806      * form - if the content panel contains a form - this is a reference to it.
33807      * @type {Roo.form.Form}
33808      */
33809     form : false,
33810     /**
33811      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33812      *    This contains a reference to it.
33813      * @type {Roo.View}
33814      */
33815     view : false,
33816     
33817       /**
33818      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33819      * <pre><code>
33820
33821 layout.addxtype({
33822        xtype : 'Form',
33823        items: [ .... ]
33824    }
33825 );
33826
33827 </code></pre>
33828      * @param {Object} cfg Xtype definition of item to add.
33829      */
33830     
33831     
33832     getChildContainer: function () {
33833         return this.getEl();
33834     }
33835     
33836     
33837     /*
33838         var  ret = new Roo.factory(cfg);
33839         return ret;
33840         
33841         
33842         // add form..
33843         if (cfg.xtype.match(/^Form$/)) {
33844             
33845             var el;
33846             //if (this.footer) {
33847             //    el = this.footer.container.insertSibling(false, 'before');
33848             //} else {
33849                 el = this.el.createChild();
33850             //}
33851
33852             this.form = new  Roo.form.Form(cfg);
33853             
33854             
33855             if ( this.form.allItems.length) {
33856                 this.form.render(el.dom);
33857             }
33858             return this.form;
33859         }
33860         // should only have one of theses..
33861         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33862             // views.. should not be just added - used named prop 'view''
33863             
33864             cfg.el = this.el.appendChild(document.createElement("div"));
33865             // factory?
33866             
33867             var ret = new Roo.factory(cfg);
33868              
33869              ret.render && ret.render(false, ''); // render blank..
33870             this.view = ret;
33871             return ret;
33872         }
33873         return false;
33874     }
33875     \*/
33876 });
33877  
33878 /**
33879  * @class Roo.bootstrap.panel.Grid
33880  * @extends Roo.bootstrap.panel.Content
33881  * @constructor
33882  * Create a new GridPanel.
33883  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33884  * @param {Object} config A the config object
33885   
33886  */
33887
33888
33889
33890 Roo.bootstrap.panel.Grid = function(config)
33891 {
33892     
33893       
33894     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33895         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
33896
33897     config.el = this.wrapper;
33898     //this.el = this.wrapper;
33899     
33900       if (config.container) {
33901         // ctor'ed from a Border/panel.grid
33902         
33903         
33904         this.wrapper.setStyle("overflow", "hidden");
33905         this.wrapper.addClass('roo-grid-container');
33906
33907     }
33908     
33909     
33910     if(config.toolbar){
33911         var tool_el = this.wrapper.createChild();    
33912         this.toolbar = Roo.factory(config.toolbar);
33913         var ti = [];
33914         if (config.toolbar.items) {
33915             ti = config.toolbar.items ;
33916             delete config.toolbar.items ;
33917         }
33918         
33919         var nitems = [];
33920         this.toolbar.render(tool_el);
33921         for(var i =0;i < ti.length;i++) {
33922           //  Roo.log(['add child', items[i]]);
33923             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33924         }
33925         this.toolbar.items = nitems;
33926         
33927         delete config.toolbar;
33928     }
33929     
33930     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
33931     config.grid.scrollBody = true;;
33932     config.grid.monitorWindowResize = false; // turn off autosizing
33933     config.grid.autoHeight = false;
33934     config.grid.autoWidth = false;
33935     
33936     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
33937     
33938     if (config.background) {
33939         // render grid on panel activation (if panel background)
33940         this.on('activate', function(gp) {
33941             if (!gp.grid.rendered) {
33942                 gp.grid.render(el);
33943                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
33944
33945             }
33946         });
33947             
33948     } else {
33949         this.grid.render(this.wrapper);
33950         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
33951
33952     }
33953     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
33954     // ??? needed ??? config.el = this.wrapper;
33955     
33956     
33957     
33958   
33959     // xtype created footer. - not sure if will work as we normally have to render first..
33960     if (this.footer && !this.footer.el && this.footer.xtype) {
33961         
33962         var ctr = this.grid.getView().getFooterPanel(true);
33963         this.footer.dataSource = this.grid.dataSource;
33964         this.footer = Roo.factory(this.footer, Roo);
33965         this.footer.render(ctr);
33966         
33967     }
33968     
33969     
33970     
33971     
33972      
33973 };
33974
33975 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
33976     getId : function(){
33977         return this.grid.id;
33978     },
33979     
33980     /**
33981      * Returns the grid for this panel
33982      * @return {Roo.bootstrap.Table} 
33983      */
33984     getGrid : function(){
33985         return this.grid;    
33986     },
33987     
33988     setSize : function(width, height){
33989         if(!this.ignoreResize(width, height)){
33990             var grid = this.grid;
33991             var size = this.adjustForComponents(width, height);
33992             var gridel = grid.getGridEl();
33993             gridel.setSize(size.width, size.height);
33994             /*
33995             var thd = grid.getGridEl().select('thead',true).first();
33996             var tbd = grid.getGridEl().select('tbody', true).first();
33997             if (tbd) {
33998                 tbd.setSize(width, height - thd.getHeight());
33999             }
34000             */
34001             grid.autoSize();
34002         }
34003     },
34004      
34005     
34006     
34007     beforeSlide : function(){
34008         this.grid.getView().scroller.clip();
34009     },
34010     
34011     afterSlide : function(){
34012         this.grid.getView().scroller.unclip();
34013     },
34014     
34015     destroy : function(){
34016         this.grid.destroy();
34017         delete this.grid;
34018         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34019     }
34020 });
34021
34022 /**
34023  * @class Roo.bootstrap.panel.Nest
34024  * @extends Roo.bootstrap.panel.Content
34025  * @constructor
34026  * Create a new Panel, that can contain a layout.Border.
34027  * 
34028  * 
34029  * @param {Roo.BorderLayout} layout The layout for this panel
34030  * @param {String/Object} config A string to set only the title or a config object
34031  */
34032 Roo.bootstrap.panel.Nest = function(config)
34033 {
34034     // construct with only one argument..
34035     /* FIXME - implement nicer consturctors
34036     if (layout.layout) {
34037         config = layout;
34038         layout = config.layout;
34039         delete config.layout;
34040     }
34041     if (layout.xtype && !layout.getEl) {
34042         // then layout needs constructing..
34043         layout = Roo.factory(layout, Roo);
34044     }
34045     */
34046     
34047     config.el =  config.layout.getEl();
34048     
34049     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34050     
34051     config.layout.monitorWindowResize = false; // turn off autosizing
34052     this.layout = config.layout;
34053     this.layout.getEl().addClass("roo-layout-nested-layout");
34054     
34055     
34056     
34057     
34058 };
34059
34060 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34061
34062     setSize : function(width, height){
34063         if(!this.ignoreResize(width, height)){
34064             var size = this.adjustForComponents(width, height);
34065             var el = this.layout.getEl();
34066             el.setSize(size.width, size.height);
34067             var touch = el.dom.offsetWidth;
34068             this.layout.layout();
34069             // ie requires a double layout on the first pass
34070             if(Roo.isIE && !this.initialized){
34071                 this.initialized = true;
34072                 this.layout.layout();
34073             }
34074         }
34075     },
34076     
34077     // activate all subpanels if not currently active..
34078     
34079     setActiveState : function(active){
34080         this.active = active;
34081         if(!active){
34082             this.fireEvent("deactivate", this);
34083             return;
34084         }
34085         
34086         this.fireEvent("activate", this);
34087         // not sure if this should happen before or after..
34088         if (!this.layout) {
34089             return; // should not happen..
34090         }
34091         var reg = false;
34092         for (var r in this.layout.regions) {
34093             reg = this.layout.getRegion(r);
34094             if (reg.getActivePanel()) {
34095                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34096                 reg.setActivePanel(reg.getActivePanel());
34097                 continue;
34098             }
34099             if (!reg.panels.length) {
34100                 continue;
34101             }
34102             reg.showPanel(reg.getPanel(0));
34103         }
34104         
34105         
34106         
34107         
34108     },
34109     
34110     /**
34111      * Returns the nested BorderLayout for this panel
34112      * @return {Roo.BorderLayout} 
34113      */
34114     getLayout : function(){
34115         return this.layout;
34116     },
34117     
34118      /**
34119      * Adds a xtype elements to the layout of the nested panel
34120      * <pre><code>
34121
34122 panel.addxtype({
34123        xtype : 'ContentPanel',
34124        region: 'west',
34125        items: [ .... ]
34126    }
34127 );
34128
34129 panel.addxtype({
34130         xtype : 'NestedLayoutPanel',
34131         region: 'west',
34132         layout: {
34133            center: { },
34134            west: { }   
34135         },
34136         items : [ ... list of content panels or nested layout panels.. ]
34137    }
34138 );
34139 </code></pre>
34140      * @param {Object} cfg Xtype definition of item to add.
34141      */
34142     addxtype : function(cfg) {
34143         return this.layout.addxtype(cfg);
34144     
34145     }
34146 });        /*
34147  * Based on:
34148  * Ext JS Library 1.1.1
34149  * Copyright(c) 2006-2007, Ext JS, LLC.
34150  *
34151  * Originally Released Under LGPL - original licence link has changed is not relivant.
34152  *
34153  * Fork - LGPL
34154  * <script type="text/javascript">
34155  */
34156 /**
34157  * @class Roo.TabPanel
34158  * @extends Roo.util.Observable
34159  * A lightweight tab container.
34160  * <br><br>
34161  * Usage:
34162  * <pre><code>
34163 // basic tabs 1, built from existing content
34164 var tabs = new Roo.TabPanel("tabs1");
34165 tabs.addTab("script", "View Script");
34166 tabs.addTab("markup", "View Markup");
34167 tabs.activate("script");
34168
34169 // more advanced tabs, built from javascript
34170 var jtabs = new Roo.TabPanel("jtabs");
34171 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34172
34173 // set up the UpdateManager
34174 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34175 var updater = tab2.getUpdateManager();
34176 updater.setDefaultUrl("ajax1.htm");
34177 tab2.on('activate', updater.refresh, updater, true);
34178
34179 // Use setUrl for Ajax loading
34180 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34181 tab3.setUrl("ajax2.htm", null, true);
34182
34183 // Disabled tab
34184 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34185 tab4.disable();
34186
34187 jtabs.activate("jtabs-1");
34188  * </code></pre>
34189  * @constructor
34190  * Create a new TabPanel.
34191  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34192  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34193  */
34194 Roo.bootstrap.panel.Tabs = function(config){
34195     /**
34196     * The container element for this TabPanel.
34197     * @type Roo.Element
34198     */
34199     this.el = Roo.get(config.el);
34200     delete config.el;
34201     if(config){
34202         if(typeof config == "boolean"){
34203             this.tabPosition = config ? "bottom" : "top";
34204         }else{
34205             Roo.apply(this, config);
34206         }
34207     }
34208     
34209     if(this.tabPosition == "bottom"){
34210         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34211         this.el.addClass("roo-tabs-bottom");
34212     }
34213     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34214     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34215     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34216     if(Roo.isIE){
34217         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34218     }
34219     if(this.tabPosition != "bottom"){
34220         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34221          * @type Roo.Element
34222          */
34223         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34224         this.el.addClass("roo-tabs-top");
34225     }
34226     this.items = [];
34227
34228     this.bodyEl.setStyle("position", "relative");
34229
34230     this.active = null;
34231     this.activateDelegate = this.activate.createDelegate(this);
34232
34233     this.addEvents({
34234         /**
34235          * @event tabchange
34236          * Fires when the active tab changes
34237          * @param {Roo.TabPanel} this
34238          * @param {Roo.TabPanelItem} activePanel The new active tab
34239          */
34240         "tabchange": true,
34241         /**
34242          * @event beforetabchange
34243          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34244          * @param {Roo.TabPanel} this
34245          * @param {Object} e Set cancel to true on this object to cancel the tab change
34246          * @param {Roo.TabPanelItem} tab The tab being changed to
34247          */
34248         "beforetabchange" : true
34249     });
34250
34251     Roo.EventManager.onWindowResize(this.onResize, this);
34252     this.cpad = this.el.getPadding("lr");
34253     this.hiddenCount = 0;
34254
34255
34256     // toolbar on the tabbar support...
34257     if (this.toolbar) {
34258         alert("no toolbar support yet");
34259         this.toolbar  = false;
34260         /*
34261         var tcfg = this.toolbar;
34262         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34263         this.toolbar = new Roo.Toolbar(tcfg);
34264         if (Roo.isSafari) {
34265             var tbl = tcfg.container.child('table', true);
34266             tbl.setAttribute('width', '100%');
34267         }
34268         */
34269         
34270     }
34271    
34272
34273
34274     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34275 };
34276
34277 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34278     /*
34279      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34280      */
34281     tabPosition : "top",
34282     /*
34283      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34284      */
34285     currentTabWidth : 0,
34286     /*
34287      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34288      */
34289     minTabWidth : 40,
34290     /*
34291      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34292      */
34293     maxTabWidth : 250,
34294     /*
34295      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34296      */
34297     preferredTabWidth : 175,
34298     /*
34299      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34300      */
34301     resizeTabs : false,
34302     /*
34303      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34304      */
34305     monitorResize : true,
34306     /*
34307      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34308      */
34309     toolbar : false,
34310
34311     /**
34312      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34313      * @param {String} id The id of the div to use <b>or create</b>
34314      * @param {String} text The text for the tab
34315      * @param {String} content (optional) Content to put in the TabPanelItem body
34316      * @param {Boolean} closable (optional) True to create a close icon on the tab
34317      * @return {Roo.TabPanelItem} The created TabPanelItem
34318      */
34319     addTab : function(id, text, content, closable)
34320     {
34321         var item = new Roo.bootstrap.panel.TabItem({
34322             panel: this,
34323             id : id,
34324             text : text,
34325             closable : closable
34326         });
34327         this.addTabItem(item);
34328         if(content){
34329             item.setContent(content);
34330         }
34331         return item;
34332     },
34333
34334     /**
34335      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34336      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34337      * @return {Roo.TabPanelItem}
34338      */
34339     getTab : function(id){
34340         return this.items[id];
34341     },
34342
34343     /**
34344      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34345      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34346      */
34347     hideTab : function(id){
34348         var t = this.items[id];
34349         if(!t.isHidden()){
34350            t.setHidden(true);
34351            this.hiddenCount++;
34352            this.autoSizeTabs();
34353         }
34354     },
34355
34356     /**
34357      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34358      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34359      */
34360     unhideTab : function(id){
34361         var t = this.items[id];
34362         if(t.isHidden()){
34363            t.setHidden(false);
34364            this.hiddenCount--;
34365            this.autoSizeTabs();
34366         }
34367     },
34368
34369     /**
34370      * Adds an existing {@link Roo.TabPanelItem}.
34371      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34372      */
34373     addTabItem : function(item){
34374         this.items[item.id] = item;
34375         this.items.push(item);
34376       //  if(this.resizeTabs){
34377     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34378   //         this.autoSizeTabs();
34379 //        }else{
34380 //            item.autoSize();
34381        // }
34382     },
34383
34384     /**
34385      * Removes a {@link Roo.TabPanelItem}.
34386      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34387      */
34388     removeTab : function(id){
34389         var items = this.items;
34390         var tab = items[id];
34391         if(!tab) { return; }
34392         var index = items.indexOf(tab);
34393         if(this.active == tab && items.length > 1){
34394             var newTab = this.getNextAvailable(index);
34395             if(newTab) {
34396                 newTab.activate();
34397             }
34398         }
34399         this.stripEl.dom.removeChild(tab.pnode.dom);
34400         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34401             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34402         }
34403         items.splice(index, 1);
34404         delete this.items[tab.id];
34405         tab.fireEvent("close", tab);
34406         tab.purgeListeners();
34407         this.autoSizeTabs();
34408     },
34409
34410     getNextAvailable : function(start){
34411         var items = this.items;
34412         var index = start;
34413         // look for a next tab that will slide over to
34414         // replace the one being removed
34415         while(index < items.length){
34416             var item = items[++index];
34417             if(item && !item.isHidden()){
34418                 return item;
34419             }
34420         }
34421         // if one isn't found select the previous tab (on the left)
34422         index = start;
34423         while(index >= 0){
34424             var item = items[--index];
34425             if(item && !item.isHidden()){
34426                 return item;
34427             }
34428         }
34429         return null;
34430     },
34431
34432     /**
34433      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34434      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34435      */
34436     disableTab : function(id){
34437         var tab = this.items[id];
34438         if(tab && this.active != tab){
34439             tab.disable();
34440         }
34441     },
34442
34443     /**
34444      * Enables a {@link Roo.TabPanelItem} that is disabled.
34445      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34446      */
34447     enableTab : function(id){
34448         var tab = this.items[id];
34449         tab.enable();
34450     },
34451
34452     /**
34453      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34454      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34455      * @return {Roo.TabPanelItem} The TabPanelItem.
34456      */
34457     activate : function(id){
34458         var tab = this.items[id];
34459         if(!tab){
34460             return null;
34461         }
34462         if(tab == this.active || tab.disabled){
34463             return tab;
34464         }
34465         var e = {};
34466         this.fireEvent("beforetabchange", this, e, tab);
34467         if(e.cancel !== true && !tab.disabled){
34468             if(this.active){
34469                 this.active.hide();
34470             }
34471             this.active = this.items[id];
34472             this.active.show();
34473             this.fireEvent("tabchange", this, this.active);
34474         }
34475         return tab;
34476     },
34477
34478     /**
34479      * Gets the active {@link Roo.TabPanelItem}.
34480      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34481      */
34482     getActiveTab : function(){
34483         return this.active;
34484     },
34485
34486     /**
34487      * Updates the tab body element to fit the height of the container element
34488      * for overflow scrolling
34489      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34490      */
34491     syncHeight : function(targetHeight){
34492         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34493         var bm = this.bodyEl.getMargins();
34494         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34495         this.bodyEl.setHeight(newHeight);
34496         return newHeight;
34497     },
34498
34499     onResize : function(){
34500         if(this.monitorResize){
34501             this.autoSizeTabs();
34502         }
34503     },
34504
34505     /**
34506      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34507      */
34508     beginUpdate : function(){
34509         this.updating = true;
34510     },
34511
34512     /**
34513      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34514      */
34515     endUpdate : function(){
34516         this.updating = false;
34517         this.autoSizeTabs();
34518     },
34519
34520     /**
34521      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34522      */
34523     autoSizeTabs : function(){
34524         var count = this.items.length;
34525         var vcount = count - this.hiddenCount;
34526         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34527             return;
34528         }
34529         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34530         var availWidth = Math.floor(w / vcount);
34531         var b = this.stripBody;
34532         if(b.getWidth() > w){
34533             var tabs = this.items;
34534             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34535             if(availWidth < this.minTabWidth){
34536                 /*if(!this.sleft){    // incomplete scrolling code
34537                     this.createScrollButtons();
34538                 }
34539                 this.showScroll();
34540                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34541             }
34542         }else{
34543             if(this.currentTabWidth < this.preferredTabWidth){
34544                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34545             }
34546         }
34547     },
34548
34549     /**
34550      * Returns the number of tabs in this TabPanel.
34551      * @return {Number}
34552      */
34553      getCount : function(){
34554          return this.items.length;
34555      },
34556
34557     /**
34558      * Resizes all the tabs to the passed width
34559      * @param {Number} The new width
34560      */
34561     setTabWidth : function(width){
34562         this.currentTabWidth = width;
34563         for(var i = 0, len = this.items.length; i < len; i++) {
34564                 if(!this.items[i].isHidden()) {
34565                 this.items[i].setWidth(width);
34566             }
34567         }
34568     },
34569
34570     /**
34571      * Destroys this TabPanel
34572      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34573      */
34574     destroy : function(removeEl){
34575         Roo.EventManager.removeResizeListener(this.onResize, this);
34576         for(var i = 0, len = this.items.length; i < len; i++){
34577             this.items[i].purgeListeners();
34578         }
34579         if(removeEl === true){
34580             this.el.update("");
34581             this.el.remove();
34582         }
34583     },
34584     
34585     createStrip : function(container)
34586     {
34587         var strip = document.createElement("nav");
34588         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34589         container.appendChild(strip);
34590         return strip;
34591     },
34592     
34593     createStripList : function(strip)
34594     {
34595         // div wrapper for retard IE
34596         // returns the "tr" element.
34597         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34598         //'<div class="x-tabs-strip-wrap">'+
34599           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34600           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34601         return strip.firstChild; //.firstChild.firstChild.firstChild;
34602     },
34603     createBody : function(container)
34604     {
34605         var body = document.createElement("div");
34606         Roo.id(body, "tab-body");
34607         //Roo.fly(body).addClass("x-tabs-body");
34608         Roo.fly(body).addClass("tab-content");
34609         container.appendChild(body);
34610         return body;
34611     },
34612     createItemBody :function(bodyEl, id){
34613         var body = Roo.getDom(id);
34614         if(!body){
34615             body = document.createElement("div");
34616             body.id = id;
34617         }
34618         //Roo.fly(body).addClass("x-tabs-item-body");
34619         Roo.fly(body).addClass("tab-pane");
34620          bodyEl.insertBefore(body, bodyEl.firstChild);
34621         return body;
34622     },
34623     /** @private */
34624     createStripElements :  function(stripEl, text, closable)
34625     {
34626         var td = document.createElement("li"); // was td..
34627         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34628         //stripEl.appendChild(td);
34629         /*if(closable){
34630             td.className = "x-tabs-closable";
34631             if(!this.closeTpl){
34632                 this.closeTpl = new Roo.Template(
34633                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34634                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34635                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34636                 );
34637             }
34638             var el = this.closeTpl.overwrite(td, {"text": text});
34639             var close = el.getElementsByTagName("div")[0];
34640             var inner = el.getElementsByTagName("em")[0];
34641             return {"el": el, "close": close, "inner": inner};
34642         } else {
34643         */
34644         // not sure what this is..
34645             if(!this.tabTpl){
34646                 //this.tabTpl = new Roo.Template(
34647                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34648                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34649                 //);
34650                 this.tabTpl = new Roo.Template(
34651                    '<a href="#">' +
34652                    '<span unselectable="on"' +
34653                             (this.disableTooltips ? '' : ' title="{text}"') +
34654                             ' >{text}</span></span></a>'
34655                 );
34656                 
34657             }
34658             var el = this.tabTpl.overwrite(td, {"text": text});
34659             var inner = el.getElementsByTagName("span")[0];
34660             return {"el": el, "inner": inner};
34661         //}
34662     }
34663         
34664     
34665 });
34666
34667 /**
34668  * @class Roo.TabPanelItem
34669  * @extends Roo.util.Observable
34670  * Represents an individual item (tab plus body) in a TabPanel.
34671  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34672  * @param {String} id The id of this TabPanelItem
34673  * @param {String} text The text for the tab of this TabPanelItem
34674  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34675  */
34676 Roo.bootstrap.panel.TabItem = function(config){
34677     /**
34678      * The {@link Roo.TabPanel} this TabPanelItem belongs to
34679      * @type Roo.TabPanel
34680      */
34681     this.tabPanel = config.panel;
34682     /**
34683      * The id for this TabPanelItem
34684      * @type String
34685      */
34686     this.id = config.id;
34687     /** @private */
34688     this.disabled = false;
34689     /** @private */
34690     this.text = config.text;
34691     /** @private */
34692     this.loaded = false;
34693     this.closable = config.closable;
34694
34695     /**
34696      * The body element for this TabPanelItem.
34697      * @type Roo.Element
34698      */
34699     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34700     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34701     this.bodyEl.setStyle("display", "block");
34702     this.bodyEl.setStyle("zoom", "1");
34703     //this.hideAction();
34704
34705     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34706     /** @private */
34707     this.el = Roo.get(els.el);
34708     this.inner = Roo.get(els.inner, true);
34709     this.textEl = Roo.get(this.el.dom.firstChild, true);
34710     this.pnode = Roo.get(els.el.parentNode, true);
34711     this.el.on("mousedown", this.onTabMouseDown, this);
34712     this.el.on("click", this.onTabClick, this);
34713     /** @private */
34714     if(config.closable){
34715         var c = Roo.get(els.close, true);
34716         c.dom.title = this.closeText;
34717         c.addClassOnOver("close-over");
34718         c.on("click", this.closeClick, this);
34719      }
34720
34721     this.addEvents({
34722          /**
34723          * @event activate
34724          * Fires when this tab becomes the active tab.
34725          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34726          * @param {Roo.TabPanelItem} this
34727          */
34728         "activate": true,
34729         /**
34730          * @event beforeclose
34731          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34732          * @param {Roo.TabPanelItem} this
34733          * @param {Object} e Set cancel to true on this object to cancel the close.
34734          */
34735         "beforeclose": true,
34736         /**
34737          * @event close
34738          * Fires when this tab is closed.
34739          * @param {Roo.TabPanelItem} this
34740          */
34741          "close": true,
34742         /**
34743          * @event deactivate
34744          * Fires when this tab is no longer the active tab.
34745          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34746          * @param {Roo.TabPanelItem} this
34747          */
34748          "deactivate" : true
34749     });
34750     this.hidden = false;
34751
34752     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34753 };
34754
34755 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34756            {
34757     purgeListeners : function(){
34758        Roo.util.Observable.prototype.purgeListeners.call(this);
34759        this.el.removeAllListeners();
34760     },
34761     /**
34762      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34763      */
34764     show : function(){
34765         this.pnode.addClass("active");
34766         this.showAction();
34767         if(Roo.isOpera){
34768             this.tabPanel.stripWrap.repaint();
34769         }
34770         this.fireEvent("activate", this.tabPanel, this);
34771     },
34772
34773     /**
34774      * Returns true if this tab is the active tab.
34775      * @return {Boolean}
34776      */
34777     isActive : function(){
34778         return this.tabPanel.getActiveTab() == this;
34779     },
34780
34781     /**
34782      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34783      */
34784     hide : function(){
34785         this.pnode.removeClass("active");
34786         this.hideAction();
34787         this.fireEvent("deactivate", this.tabPanel, this);
34788     },
34789
34790     hideAction : function(){
34791         this.bodyEl.hide();
34792         this.bodyEl.setStyle("position", "absolute");
34793         this.bodyEl.setLeft("-20000px");
34794         this.bodyEl.setTop("-20000px");
34795     },
34796
34797     showAction : function(){
34798         this.bodyEl.setStyle("position", "relative");
34799         this.bodyEl.setTop("");
34800         this.bodyEl.setLeft("");
34801         this.bodyEl.show();
34802     },
34803
34804     /**
34805      * Set the tooltip for the tab.
34806      * @param {String} tooltip The tab's tooltip
34807      */
34808     setTooltip : function(text){
34809         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34810             this.textEl.dom.qtip = text;
34811             this.textEl.dom.removeAttribute('title');
34812         }else{
34813             this.textEl.dom.title = text;
34814         }
34815     },
34816
34817     onTabClick : function(e){
34818         e.preventDefault();
34819         this.tabPanel.activate(this.id);
34820     },
34821
34822     onTabMouseDown : function(e){
34823         e.preventDefault();
34824         this.tabPanel.activate(this.id);
34825     },
34826 /*
34827     getWidth : function(){
34828         return this.inner.getWidth();
34829     },
34830
34831     setWidth : function(width){
34832         var iwidth = width - this.pnode.getPadding("lr");
34833         this.inner.setWidth(iwidth);
34834         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34835         this.pnode.setWidth(width);
34836     },
34837 */
34838     /**
34839      * Show or hide the tab
34840      * @param {Boolean} hidden True to hide or false to show.
34841      */
34842     setHidden : function(hidden){
34843         this.hidden = hidden;
34844         this.pnode.setStyle("display", hidden ? "none" : "");
34845     },
34846
34847     /**
34848      * Returns true if this tab is "hidden"
34849      * @return {Boolean}
34850      */
34851     isHidden : function(){
34852         return this.hidden;
34853     },
34854
34855     /**
34856      * Returns the text for this tab
34857      * @return {String}
34858      */
34859     getText : function(){
34860         return this.text;
34861     },
34862     /*
34863     autoSize : function(){
34864         //this.el.beginMeasure();
34865         this.textEl.setWidth(1);
34866         /*
34867          *  #2804 [new] Tabs in Roojs
34868          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34869          */
34870         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34871         //this.el.endMeasure();
34872     //},
34873
34874     /**
34875      * Sets the text for the tab (Note: this also sets the tooltip text)
34876      * @param {String} text The tab's text and tooltip
34877      */
34878     setText : function(text){
34879         this.text = text;
34880         this.textEl.update(text);
34881         this.setTooltip(text);
34882         //if(!this.tabPanel.resizeTabs){
34883         //    this.autoSize();
34884         //}
34885     },
34886     /**
34887      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34888      */
34889     activate : function(){
34890         this.tabPanel.activate(this.id);
34891     },
34892
34893     /**
34894      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34895      */
34896     disable : function(){
34897         if(this.tabPanel.active != this){
34898             this.disabled = true;
34899             this.pnode.addClass("disabled");
34900         }
34901     },
34902
34903     /**
34904      * Enables this TabPanelItem if it was previously disabled.
34905      */
34906     enable : function(){
34907         this.disabled = false;
34908         this.pnode.removeClass("disabled");
34909     },
34910
34911     /**
34912      * Sets the content for this TabPanelItem.
34913      * @param {String} content The content
34914      * @param {Boolean} loadScripts true to look for and load scripts
34915      */
34916     setContent : function(content, loadScripts){
34917         this.bodyEl.update(content, loadScripts);
34918     },
34919
34920     /**
34921      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
34922      * @return {Roo.UpdateManager} The UpdateManager
34923      */
34924     getUpdateManager : function(){
34925         return this.bodyEl.getUpdateManager();
34926     },
34927
34928     /**
34929      * Set a URL to be used to load the content for this TabPanelItem.
34930      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
34931      * @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)
34932      * @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)
34933      * @return {Roo.UpdateManager} The UpdateManager
34934      */
34935     setUrl : function(url, params, loadOnce){
34936         if(this.refreshDelegate){
34937             this.un('activate', this.refreshDelegate);
34938         }
34939         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34940         this.on("activate", this.refreshDelegate);
34941         return this.bodyEl.getUpdateManager();
34942     },
34943
34944     /** @private */
34945     _handleRefresh : function(url, params, loadOnce){
34946         if(!loadOnce || !this.loaded){
34947             var updater = this.bodyEl.getUpdateManager();
34948             updater.update(url, params, this._setLoaded.createDelegate(this));
34949         }
34950     },
34951
34952     /**
34953      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
34954      *   Will fail silently if the setUrl method has not been called.
34955      *   This does not activate the panel, just updates its content.
34956      */
34957     refresh : function(){
34958         if(this.refreshDelegate){
34959            this.loaded = false;
34960            this.refreshDelegate();
34961         }
34962     },
34963
34964     /** @private */
34965     _setLoaded : function(){
34966         this.loaded = true;
34967     },
34968
34969     /** @private */
34970     closeClick : function(e){
34971         var o = {};
34972         e.stopEvent();
34973         this.fireEvent("beforeclose", this, o);
34974         if(o.cancel !== true){
34975             this.tabPanel.removeTab(this.id);
34976         }
34977     },
34978     /**
34979      * The text displayed in the tooltip for the close icon.
34980      * @type String
34981      */
34982     closeText : "Close this tab"
34983 });